Files
kicad-source-mirror/pcbnew/dragsegm.cpp
T
unknown 1ee7458140 1. Fixed copy paste error in append_board_to_current.cpp ruining a if else condition, should fix a possible bounding box computation bug
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.
2015-11-04 09:48:34 +01:00

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 );
}
}