1ee7458140
2. Whole bunch of pointless casts removed 3. Unused variables removed 4. Fix bug caused by JP on April 25, 2015 where strings were adjusted for translation and the hotkeys section table accidentally swapped the footprint editor title with tag, resulting in "footprint editor" being exported instead of "[footprinteditor]" No functional changes besides #4, technically it'll "break" imports hotkeys files but April 25 broke imports as well.
423 lines
12 KiB
C++
423 lines
12 KiB
C++
/**
|
|
* @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 );
|
|
}
|
|
}
|