/** * @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 #include #include #include #include #include #include #include #include #include #include /* a list of DRAG_SEGM_PICKER items used to move or drag tracks */ std::vector 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->m_Start; m_endInitialValue = m_Track->m_End; 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_Flag = 0; m_RotationOffset = 0.0; m_Flipped = false; } /* Set auxiliary parameters relative to calucaltions needed * to find track ends positions while dragging pads * and when modules are rotated, flipped .. */ void DRAG_SEGM_PICKER::SetAuxParameters() { MODULE * module = NULL; if( m_Pad_Start ) { module = (MODULE *) m_Pad_Start->GetParent(); m_PadStartOffset = m_Track->GetStart() - m_Pad_Start->GetPosition(); } if( m_Pad_End ) { if( module == NULL ) module = (MODULE *) m_Pad_End->GetParent(); m_PadEndOffset = m_Track->GetEnd() - m_Pad_End->GetPosition(); } if( module ) { m_Flipped = module->IsFlipped(); m_RotationOffset = module->GetOrientation(); } } /* * Calculate track ends positions while dragging pads * and when modules are rotated, flipped .. * aOffset = module or pad position offset when moving the module or pad * (the actual position is the module/pad position - offset) */ 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 = (MODULE *) m_Pad_Start->GetParent(); if( module == NULL && m_Pad_End ) module = (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 ) NEGATE( padoffset.y ); m_Track->m_Start = 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 ) NEGATE( padoffset.y ); m_Track->m_End = 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&padList = connections.GetPadsList(); for( D_PAD* pad = aModule->m_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&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; } /** Fills m_DragList with track segments connected to pads in aConnections * For each selected track segment the EDIT flag is set */ 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::vectorpadList = 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, OFF ); } } // 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, ON ); } if( pad->HitTest( track->GetEnd() ) ) { track->end = pad; track->SetState( END_ON_PAD, ON ); } 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 ); } } /** * Function EraseDragList * clear the .m_Flags of all track segments found in g_DragSegmentList * and clear the list. */ void EraseDragList() { for( unsigned ii = 0; ii < g_DragSegmentList.size(); ii++ ) g_DragSegmentList[ii].m_Track->ClearFlags(); g_DragSegmentList.clear(); } /* Add Track to the drag list, and erase it from screen * flag = STARTPOINT (if the point to drag is the start point of Track) or ENDPOINT */ void AddSegmentToDragList( int flag, TRACK* aTrack ) { DRAG_SEGM_PICKER wrapper( aTrack ); if( (flag & STARTPOINT) ) wrapper.m_Flag |= 1; if( (flag & ENDPOINT) ) wrapper.m_Flag |= 2; if( (flag & STARTPOINT) ) aTrack->SetFlags( STARTPOINT ); if( (flag & ENDPOINT) ) aTrack->SetFlags( ENDPOINT ); g_DragSegmentList.push_back( wrapper ); } /* Build the list of tracks connected to the ref point * Net codes must be up to date, because only tracks having the right net code are tested. * aRefPos = reference point of connection */ void Collect_TrackSegmentsToDrag( BOARD* aPcb, wxPoint& aRefPos, int LayerMask, int net_code ) { TRACK* track = aPcb->m_Track->GetStartNetCode( net_code ); for( ; track; track = track->Next() ) { if( track->GetNet() != net_code ) // not the same netcodenet code: all candidates tested break; if( ( LayerMask & track->ReturnMaskLayer() ) == 0 ) continue; // Cannot be connected, not on the same layer if( track->IsDragging() ) continue; // already put in list int flag = 0; if( (track->m_Start == aRefPos) && ((track->GetFlags() & STARTPOINT) == 0) ) flag |= STARTPOINT; if( track->m_End == aRefPos && ((track->GetFlags() & ENDPOINT) == 0) ) 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->ReturnMaskLayer(), net_code ); } } } /* * Undraw the track segments in list, and set the IN_EDIT flag * Usually called after the track list is built, to prepare * the redraw of the list when the mouse is moved */ 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, ON ); if( (g_DragSegmentList[ii].m_Flag & STARTPOINT) ) track->SetFlags( STARTPOINT ); if( (g_DragSegmentList[ii].m_Flag & ENDPOINT) ) track->SetFlags( ENDPOINT ); track->Draw( aCanvas, aDC, GR_XOR ); } }