summaryrefslogtreecommitdiff
path: root/pcbnew/dragsegm.cpp
blob: 7e48f5c2a7cd8f970d87575e18d6fda04909df9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
/**
 * @file dragsegm.cpp
 * @brief Classes to find track segments connected to a pad or a module
 * for drag commands
 */

/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2004-2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
 * Copyright (C) 1992-2012 KiCad Developers, see change_log.txt for contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#include <fctsys.h>
#include <common.h>
#include <trigo.h>
#include <gr_basic.h>
#include <class_drawpanel.h>
#include <wxBasePcbFrame.h>
#include <macros.h>

#include <drag.h>
#include <pcbnew.h>

#include <class_module.h>
#include <class_board.h>
#include <connect.h>


/* a list of DRAG_SEGM_PICKER items used to move or drag tracks */
std::vector<DRAG_SEGM_PICKER> g_DragSegmentList;

/* helper class to handle a list of track segments to drag or move
 */
DRAG_SEGM_PICKER::DRAG_SEGM_PICKER( TRACK* aTrack )
{
    m_Track = aTrack;
    m_startInitialValue = m_Track->GetStart();
    m_endInitialValue   = m_Track->GetEnd();
    m_Pad_Start = m_Track->GetState( START_ON_PAD ) ? (D_PAD*)m_Track->start : NULL;
    m_Pad_End = m_Track->GetState( END_ON_PAD ) ? (D_PAD*)m_Track->end : NULL;
    m_TempFlags = 0;
    m_RotationOffset = 0.0;
    m_Flipped = false;
}


void DRAG_SEGM_PICKER::SetAuxParameters()
{
    MODULE* module = NULL;

    if( m_Pad_Start )
    {
        module = m_Pad_Start->GetParent();
        m_PadStartOffset = m_Track->GetStart() - m_Pad_Start->GetPosition();
    }

    if( m_Pad_End  )
    {
        if( module == NULL )
            module = m_Pad_End->GetParent();

        m_PadEndOffset = m_Track->GetEnd() - m_Pad_End->GetPosition();
    }

    if( module )
    {
        m_Flipped = module->IsFlipped();
        m_RotationOffset = module->GetOrientation();
    }
}


void DRAG_SEGM_PICKER::SetTrackEndsCoordinates( wxPoint aOffset )
{
    // the track start position is the pad position + m_PadStartOffset
    // however m_PadStartOffset is known for the initial rotation/flip
    // this is also true for track end position and m_PadEndOffset
    // Therefore, because rotation/flipping is allowed during a drag
    // and move module, we should recalculate the pad offset,
    // depending on the current orientation/flip state of the module
    // relative to its initial orientation.
    // (although most of time, offset is 0,0)

    double curr_rot_offset = m_RotationOffset;
    MODULE* module = NULL;
    bool flip = false;

    if( m_Pad_Start )
        module = m_Pad_Start->GetParent();

    if( module == NULL && m_Pad_End )
        module = m_Pad_End->GetParent();

    if( module )
    {
        flip = m_Flipped != module->IsFlipped();
        curr_rot_offset = module->GetOrientation() - m_RotationOffset;

        if( flip )  // when flipping, module orientation is negated
            curr_rot_offset = - module->GetOrientation() - m_RotationOffset;
    }

    if( m_Pad_Start )
    {
        wxPoint padoffset = m_PadStartOffset;

        if( curr_rot_offset != 0.0 )
            RotatePoint(&padoffset, curr_rot_offset);

        if( flip )
            padoffset.y = -padoffset.y;

        m_Track->SetStart( m_Pad_Start->GetPosition() - aOffset + padoffset );
    }

    if( m_Pad_End )
    {
        wxPoint padoffset = m_PadEndOffset;

        if( curr_rot_offset != 0.0 )
            RotatePoint( &padoffset, curr_rot_offset );

        if( flip )
            padoffset.y = -padoffset.y;

        m_Track->SetEnd( m_Pad_End->GetPosition() - aOffset + padoffset );
    }
}


// A sort function needed to build ordered pads lists
extern bool sortPadsByXthenYCoord( D_PAD* const & ref, D_PAD* const & comp );


void DRAG_LIST::BuildDragListe( MODULE* aModule )
{
    m_Pad = NULL;
    m_Module = aModule;

    // Build connections info
    CONNECTIONS connections( m_Brd );
    std::vector<D_PAD*>&padList = connections.GetPadsList();

    for( D_PAD* pad = aModule->Pads();  pad;  pad = pad->Next() )
        padList.push_back( pad );

    sort( padList.begin(), padList.end(), sortPadsByXthenYCoord );

    fillList( connections );
}


void DRAG_LIST::BuildDragListe( D_PAD* aPad )
{
    m_Pad = aPad;
    m_Module = NULL;

    // Build connections info
    CONNECTIONS connections( m_Brd );
    std::vector<D_PAD*>&padList = connections.GetPadsList();
    padList.push_back( aPad );

    fillList( connections );
}


// A helper function to sort track list per tracks
bool sort_tracklist( const DRAG_SEGM_PICKER& ref, const DRAG_SEGM_PICKER& tst )
{
    return ref.m_Track < tst.m_Track;
}


void DRAG_LIST::fillList( CONNECTIONS& aConnections )
{
    aConnections.BuildTracksCandidatesList( m_Brd->m_Track, NULL);

    // Build connections info tracks to pads
    // Rebuild pads to track info only)
    aConnections.SearchTracksConnectedToPads( false, true );

    std::vector<D_PAD*>padList = aConnections.GetPadsList();

    // clear flags and variables of selected tracks
    for( unsigned ii = 0;  ii < padList.size(); ii++ )
    {
        D_PAD * pad = padList[ii];

        // store track connected to the pad
        for( unsigned jj = 0; jj < pad->m_TracksConnected.size(); jj++ )
        {
            TRACK * track = pad->m_TracksConnected[jj];
            track->start = NULL;
            track->end = NULL;
            track->SetState( START_ON_PAD|END_ON_PAD|BUSY, false );
        }
    }

    // store tracks connected to pads
    for(  unsigned ii = 0;  ii < padList.size(); ii++ )
    {
        D_PAD * pad = padList[ii];

        // store track connected to the pad
        for( unsigned jj = 0; jj < pad->m_TracksConnected.size(); jj++ )
        {
            TRACK * track = pad->m_TracksConnected[jj];

            if( pad->HitTest( track->GetStart() ) )
            {
                track->start = pad;
                track->SetState( START_ON_PAD, true );
            }

            if( pad->HitTest( track->GetEnd() ) )
            {
                track->end = pad;
                track->SetState( END_ON_PAD, true );
            }

            DRAG_SEGM_PICKER wrapper( track );
            m_DragList.push_back( wrapper );
        }
    }

    // remove duplicate in m_DragList:
    // a track can be stored more than once if connected to 2 overlapping pads, or
    // each track end connected to 2 moving pads
    // to avoid artifact in draw function, items should be not duplicated
    // However, there is not a lot of items to be removed, so there ir no optimization.

    // sort the drag list by track pointers
    sort( m_DragList.begin(), m_DragList.end(), sort_tracklist );

    // Explore the list, merge duplicates
    for( int ii = 0; ii < (int)m_DragList.size()-1; ii++ )
    {
        int jj = ii+1;

        if( m_DragList[ii].m_Track != m_DragList[jj].m_Track )
            continue;

        // duplicate found: merge info and remove duplicate
        if( m_DragList[ii].m_Pad_Start == NULL )
            m_DragList[ii].m_Pad_Start = m_DragList[jj].m_Pad_Start;

        if( m_DragList[ii].m_Pad_End == NULL )
            m_DragList[ii].m_Pad_End = m_DragList[jj].m_Pad_End;

        m_DragList.erase( m_DragList.begin() + jj );
        ii--;
    }

    // Initialize pad offsets and other params
    for( unsigned ii = 0; ii < m_DragList.size(); ii++ )
        m_DragList[ii].SetAuxParameters();

    // Copy the list in global list
    g_DragSegmentList = m_DragList;
}


void DRAG_LIST::ClearList()
{
    for( unsigned ii = 0; ii < m_DragList.size(); ii++ )
        m_DragList[ii].m_Track->ClearFlags();

    m_DragList.clear();

    m_Module = NULL;
    m_Pad = NULL;
}


// Redraw the list of segments stored in g_DragSegmentList, while moving a footprint
void DrawSegmentWhileMovingFootprint( EDA_DRAW_PANEL* panel, wxDC* DC )
{
    for( unsigned ii = 0; ii < g_DragSegmentList.size(); ii++ )
    {
        TRACK* track = g_DragSegmentList[ii].m_Track;

#ifndef USE_WX_OVERLAY
        track->Draw( panel, DC, GR_XOR );   // erase from screen at old position
#endif
        g_DragSegmentList[ii].SetTrackEndsCoordinates( g_Offset_Module );
        track->Draw( panel, DC, GR_XOR );
    }
}


void EraseDragList()
{
    for( unsigned ii = 0; ii < g_DragSegmentList.size(); ii++ )
        g_DragSegmentList[ii].m_Track->ClearFlags();

    g_DragSegmentList.clear();
}


void AddSegmentToDragList( int flag, TRACK* aTrack )
{
    DRAG_SEGM_PICKER wrapper( aTrack );

    if( flag & STARTPOINT )
    {
        wrapper.m_TempFlags |= STARTPOINT;
        aTrack->SetFlags( STARTPOINT );
    }

    if( flag & ENDPOINT )
    {
        wrapper.m_TempFlags |= ENDPOINT;
        aTrack->SetFlags( ENDPOINT );
    }

    g_DragSegmentList.push_back( wrapper );
}


void Collect_TrackSegmentsToDrag( BOARD* aPcb, const wxPoint& aRefPos, LSET aLayerMask,
                                  int aNetCode, int aMaxDist )
{
    TRACK* track = aPcb->m_Track->GetStartNetCode( aNetCode );

    for( ; track; track = track->Next() )
    {
        if( track->GetNetCode() != aNetCode )   // not the same netcode: all candidates tested
            break;

        if( !( aLayerMask & track->GetLayerSet() ).any() )
            continue;                       // Cannot be connected, not on the same layer

        if( track->IsDragging() )
            continue;                       // already put in list

        STATUS_FLAGS flag = 0;
        int maxdist = std::max( aMaxDist, track->GetWidth() / 2 );

        if( (track->GetFlags() & STARTPOINT) == 0 )
        {
            wxPoint delta = track->GetStart() - aRefPos;

            if( std::abs( delta.x ) <= maxdist && std::abs( delta.y ) <= maxdist )
            {
                int dist = KiROUND( EuclideanNorm( delta ) );

                if( dist <= maxdist )
                {
                    flag |= STARTPOINT;

                    if( track->Type() == PCB_VIA_T )
                        flag |= ENDPOINT;
                }
            }
        }

        if( (track->GetFlags() & ENDPOINT) == 0 )
        {
            wxPoint delta = track->GetEnd() - aRefPos;

            if( std::abs( delta.x ) <= maxdist && std::abs( delta.y ) <= maxdist )
            {
                int dist = KiROUND( EuclideanNorm( delta ) );

                if( dist <= maxdist )
                    flag |= ENDPOINT;
            }
        }

        // Note: vias will be flagged with both STARTPOINT and ENDPOINT
        // and must not be entered twice.
        if( flag )
        {
            AddSegmentToDragList( flag, track );

            // If a connected via is found at location aRefPos,
            // collect also tracks connected by this via.
            if( track->Type() == PCB_VIA_T )
                Collect_TrackSegmentsToDrag( aPcb, aRefPos, track->GetLayerSet(),
                                             aNetCode, track->GetWidth() / 2 );
        }
    }
}


void UndrawAndMarkSegmentsToDrag( EDA_DRAW_PANEL* aCanvas, wxDC* aDC )
{
    for( unsigned ii = 0; ii < g_DragSegmentList.size(); ii++ )
    {
        TRACK* track = g_DragSegmentList[ii].m_Track;

        track->Draw( aCanvas, aDC, GR_XOR );
        track->SetState( IN_EDIT, false );
        track->SetFlags( IS_DRAGGED );

        if( g_DragSegmentList[ii].m_TempFlags & STARTPOINT )
            track->SetFlags( STARTPOINT );

        if( g_DragSegmentList[ii].m_TempFlags & ENDPOINT )
            track->SetFlags( ENDPOINT );

        track->Draw( aCanvas, aDC, GR_XOR );
    }
}