void SELECTION_TOOL::selectSingle( const VECTOR2I& aWhere ) { BOARD* pcb = getModel<BOARD>( PCB_T ); BOARD_ITEM* item; GENERAL_COLLECTORS_GUIDE guide = getEditFrame<PCB_EDIT_FRAME>()->GetCollectorsGuide(); GENERAL_COLLECTOR collector; collector.Collect( pcb, GENERAL_COLLECTOR::AllBoardItems, wxPoint( aWhere.x, aWhere.y ), guide ); switch( collector.GetCount() ) { case 0: if( !m_additive ) clearSelection(); break; case 1: toggleSelection( collector[0] ); break; default: // Remove unselectable items for( int i = collector.GetCount() - 1; i >= 0 ; --i ) { if( !selectable( collector[i] ) ) collector.Remove( i ); } // Let's see if there is still disambiguation in selection.. if( collector.GetCount() == 1 ) { toggleSelection( collector[0] ); } else if( collector.GetCount() > 1 ) { item = disambiguationMenu( &collector ); if( item ) toggleSelection( item ); } break; } }
bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aOnDrag ) { BOARD_ITEM* item; GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide(); GENERAL_COLLECTOR collector; if( m_editModules ) collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::ModuleItems, wxPoint( aWhere.x, aWhere.y ), guide ); else collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::AllBoardItems, wxPoint( aWhere.x, aWhere.y ), guide ); bool anyCollected = collector.GetCount() != 0; // Remove unselectable items for( int i = collector.GetCount() - 1; i >= 0; --i ) { if( !selectable( collector[i] ) || ( aOnDrag && collector[i]->IsLocked() ) ) collector.Remove( i ); } switch( collector.GetCount() ) { case 0: if( !m_additive && anyCollected ) clearSelection(); return false; case 1: toggleSelection( collector[0] ); return true; default: // Apply some ugly heuristics to avoid disambiguation menus whenever possible guessSelectionCandidates( collector ); // Let's see if there is still disambiguation in selection.. if( collector.GetCount() == 1 ) { toggleSelection( collector[0] ); return true; } else if( collector.GetCount() > 1 ) { if( aOnDrag ) Wait( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) ); item = disambiguationMenu( &collector ); if( item ) { toggleSelection( item ); return true; } } break; } return false; }
// todo: explain the selection heuristics void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) const { std::set<BOARD_ITEM*> rejected; const double footprintAreaRatio = 0.2; const double modulePadMinCoverRatio = 0.45; const double padViaAreaRatio = 0.5; const double trackViaLengthRatio = 2.0; const double trackTrackLengthRatio = 0.3; const double textToFeatureMinRatio = 0.2; const double textToFootprintMinRatio = 0.4; LAYER_ID actLayer = m_frame->GetActiveLayer(); LSET silkLayers( 2, B_SilkS, F_SilkS ); if( silkLayers[actLayer] ) { std::set<BOARD_ITEM*> preferred; for( int i = 0; i < aCollector.GetCount(); ++i ) { BOARD_ITEM* item = aCollector[i]; if ( item->Type() == PCB_MODULE_TEXT_T || item->Type() == PCB_TEXT_T || item->Type() == PCB_LINE_T ) if ( silkLayers[item->GetLayer()] ) preferred.insert ( item ); } if( preferred.size() != 0 ) { aCollector.Empty(); BOOST_FOREACH( BOARD_ITEM* item, preferred ) aCollector.Append( item ); return; } } if( aCollector.CountType( PCB_MODULE_TEXT_T ) > 0 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) if( TEXTE_MODULE* txt = dyn_cast<TEXTE_MODULE*>( aCollector[i] ) ) { double textArea = calcArea( txt ); for( int j = 0; j < aCollector.GetCount(); ++j ) { BOARD_ITEM* item = aCollector[j]; double areaRatio = calcRatio( textArea, calcArea( item ) ); if( item->Type() == PCB_MODULE_T && areaRatio < textToFootprintMinRatio ) { //printf("rejectModuleN\n"); rejected.insert( item ); } switch( item->Type() ) { case PCB_TRACE_T: case PCB_PAD_T: case PCB_LINE_T: case PCB_VIA_T: case PCB_MODULE_T: if( areaRatio > textToFeatureMinRatio ) { //printf("t after moduleRejected\n"); rejected.insert( txt ); } break; default: break; } } } } if( aCollector.CountType( PCB_MODULE_T ) > 0 ) { double minArea = calcMinArea( aCollector, PCB_MODULE_T ); double maxArea = calcMaxArea( aCollector, PCB_MODULE_T ); if( calcRatio( minArea, maxArea ) <= footprintAreaRatio ) { for( int i = 0; i < aCollector.GetCount(); ++i ) if( MODULE* mod = dyn_cast<MODULE*>( aCollector[i] ) ) { double normalizedArea = calcRatio( calcArea(mod), maxArea ); if( normalizedArea > footprintAreaRatio ) { //printf("rejectModule1\n"); rejected.insert( mod ); } } } } if( aCollector.CountType ( PCB_PAD_T ) > 0 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if ( D_PAD* pad = dyn_cast<D_PAD*>( aCollector[i] ) ) { double ratio = pad->GetParent()->PadCoverageRatio(); if( ratio < modulePadMinCoverRatio ) rejected.insert( pad->GetParent() ); } } } if( aCollector.CountType( PCB_VIA_T ) > 0 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( VIA* via = dyn_cast<VIA*>( aCollector[i] ) ) { double viaArea = calcArea( via ); for( int j = 0; j < aCollector.GetCount(); ++j ) { BOARD_ITEM* item = aCollector[j]; double areaRatio = calcRatio ( viaArea, calcArea( item ) ); if( item->Type() == PCB_MODULE_T && areaRatio < modulePadMinCoverRatio ) rejected.insert( item ); if( item->Type() == PCB_PAD_T && areaRatio < padViaAreaRatio ) rejected.insert( item ); if( TRACK* track = dyn_cast<TRACK*>( item ) ) { if( track->GetNetCode() != via->GetNetCode() ) continue; double lenRatio = (double) ( track->GetLength() + track->GetWidth() ) / (double) via->GetWidth(); if( lenRatio > trackViaLengthRatio ) rejected.insert( track ); } } } } } int nTracks = aCollector.CountType ( PCB_TRACE_T ); if( nTracks > 0 ) { double maxLength = 0.0; double minLength = std::numeric_limits<double>::max(); double maxArea = 0.0; for( int i = 0; i < aCollector.GetCount(); ++i ) if ( TRACK *track = dyn_cast<TRACK*> ( aCollector[i] ) ) { maxLength = std::max( track->GetLength(), maxLength ); maxLength = std::max( (double)track->GetWidth(), maxLength ); minLength = std::min( std::max ( track->GetLength(), (double)track->GetWidth() ), minLength ); double area = ( track->GetLength() + track->GetWidth() * track->GetWidth() ); maxArea = std::max(area, maxArea); } if( maxLength > 0.0 && minLength/maxLength < trackTrackLengthRatio && nTracks > 1 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( TRACK* track = dyn_cast<TRACK*>( aCollector[i] ) ) { double ratio = std::max( (double) track->GetWidth(), track->GetLength()) / maxLength; if( ratio > trackTrackLengthRatio ) rejected.insert( track) ; } } } for( int j = 0; j < aCollector.GetCount(); ++j ) { if( MODULE* mod = dyn_cast<MODULE*>( aCollector[j] ) ) { double ratio = maxArea / mod->GetFootprintRect().GetArea(); if( ratio < modulePadMinCoverRatio ) { //printf("rejectModule\n"); rejected.insert( mod ); } } } } BOOST_FOREACH( BOARD_ITEM* item, rejected ) { aCollector.Remove( item ); }
bool SELECTION_TOOL::selectSingle( const VECTOR2I& aWhere, bool aAllowDisambiguation ) { BOARD_ITEM* item; GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide(); GENERAL_COLLECTOR collector; // Preferred types (they have the priority when if they are covered by a bigger item) const KICAD_T types[] = { PCB_TRACE_T, PCB_VIA_T, PCB_LINE_T, PCB_MODULE_TEXT_T, EOT }; if( m_editModules ) collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::ModuleItems, wxPoint( aWhere.x, aWhere.y ), guide ); else collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::AllBoardItems, wxPoint( aWhere.x, aWhere.y ), guide ); switch( collector.GetCount() ) { case 0: if( !m_additive ) clearSelection(); return false; case 1: toggleSelection( collector[0] ); return true; default: // Remove unselectable items for( int i = collector.GetCount() - 1; i >= 0; --i ) { if( !selectable( collector[i] ) ) collector.Remove( i ); } // Check if among the selection candidates there is only one instance of preferred type item = prefer( collector, types ); if( item ) { toggleSelection( item ); return true; } // Let's see if there is still disambiguation in selection.. if( collector.GetCount() == 1 ) { toggleSelection( collector[0] ); return true; } else if( aAllowDisambiguation && collector.GetCount() > 1 ) { item = disambiguationMenu( &collector ); if( item ) { toggleSelection( item ); return true; } } break; } return false; }
// todo: explain the selection heuristics void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) const { std::set<BOARD_ITEM*> rejected; const double footprintAreaRatio = 0.2; const double modulePadMinCoverRatio = 0.45; const double padViaAreaRatio = 0.5; const double trackViaLengthRatio = 2.0; const double trackTrackLengthRatio = 0.3; const double textToFeatureMinRatio = 0.2; const double textToFootprintMinRatio = 0.4; // If the common area of two compared items is above the following threshold, they cannot // be rejected (it means they overlap and it might be hard to pick one by selecting // its unique area). const double commonAreaRatio = 0.6; LAYER_ID actLayer = m_frame->GetActiveLayer(); LSET silkLayers( 2, B_SilkS, F_SilkS ); if( silkLayers[actLayer] ) { std::set<BOARD_ITEM*> preferred; for( int i = 0; i < aCollector.GetCount(); ++i ) { BOARD_ITEM* item = aCollector[i]; KICAD_T type = item->Type(); if( ( type == PCB_MODULE_TEXT_T || type == PCB_TEXT_T || type == PCB_LINE_T ) && silkLayers[item->GetLayer()] ) { preferred.insert( item ); } } if( preferred.size() != 0 ) { aCollector.Empty(); for( BOARD_ITEM* item : preferred ) aCollector.Append( item ); return; } } if( aCollector.CountType( PCB_MODULE_TEXT_T ) > 0 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( TEXTE_MODULE* txt = dyn_cast<TEXTE_MODULE*>( aCollector[i] ) ) { double textArea = calcArea( txt ); for( int j = 0; j < aCollector.GetCount(); ++j ) { if( i == j ) continue; BOARD_ITEM* item = aCollector[j]; double itemArea = calcArea( item ); double areaRatio = calcRatio( textArea, itemArea ); double commonArea = calcCommonArea( txt, item ); double itemCommonRatio = calcRatio( commonArea, itemArea ); double txtCommonRatio = calcRatio( commonArea, textArea ); if( item->Type() == PCB_MODULE_T && areaRatio < textToFootprintMinRatio && itemCommonRatio < commonAreaRatio ) rejected.insert( item ); switch( item->Type() ) { case PCB_TRACE_T: case PCB_PAD_T: case PCB_LINE_T: case PCB_VIA_T: case PCB_MODULE_T: if( areaRatio > textToFeatureMinRatio && txtCommonRatio < commonAreaRatio ) rejected.insert( txt ); break; default: break; } } } } } if( aCollector.CountType( PCB_MODULE_T ) > 0 ) { double minArea = calcMinArea( aCollector, PCB_MODULE_T ); double maxArea = calcMaxArea( aCollector, PCB_MODULE_T ); if( calcRatio( minArea, maxArea ) <= footprintAreaRatio ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( MODULE* mod = dyn_cast<MODULE*>( aCollector[i] ) ) { double normalizedArea = calcRatio( calcArea( mod ), maxArea ); if( normalizedArea > footprintAreaRatio ) rejected.insert( mod ); } } } } if( aCollector.CountType( PCB_PAD_T ) > 0 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( D_PAD* pad = dyn_cast<D_PAD*>( aCollector[i] ) ) { double ratio = pad->GetParent()->PadCoverageRatio(); if( ratio < modulePadMinCoverRatio ) rejected.insert( pad->GetParent() ); } } } if( aCollector.CountType( PCB_VIA_T ) > 0 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( VIA* via = dyn_cast<VIA*>( aCollector[i] ) ) { double viaArea = calcArea( via ); for( int j = 0; j < aCollector.GetCount(); ++j ) { if( i == j ) continue; BOARD_ITEM* item = aCollector[j]; double areaRatio = calcRatio( viaArea, calcArea( item ) ); if( item->Type() == PCB_MODULE_T && areaRatio < modulePadMinCoverRatio ) rejected.insert( item ); if( item->Type() == PCB_PAD_T && areaRatio < padViaAreaRatio ) rejected.insert( item ); if( TRACK* track = dyn_cast<TRACK*>( item ) ) { if( track->GetNetCode() != via->GetNetCode() ) continue; double lenRatio = (double) ( track->GetLength() + track->GetWidth() ) / (double) via->GetWidth(); if( lenRatio > trackViaLengthRatio ) rejected.insert( track ); } } } } } int nTracks = aCollector.CountType( PCB_TRACE_T ); if( nTracks > 0 ) { double maxLength = 0.0; double minLength = std::numeric_limits<double>::max(); double maxArea = 0.0; const TRACK* maxTrack = nullptr; for( int i = 0; i < aCollector.GetCount(); ++i ) { if( TRACK* track = dyn_cast<TRACK*> ( aCollector[i] ) ) { maxLength = std::max( track->GetLength(), maxLength ); maxLength = std::max( (double) track->GetWidth(), maxLength ); minLength = std::min( std::max( track->GetLength(), (double) track->GetWidth() ), minLength ); double area = track->GetLength() * track->GetWidth(); if( area > maxArea ) { maxArea = area; maxTrack = track; } } } if( maxLength > 0.0 && minLength / maxLength < trackTrackLengthRatio && nTracks > 1 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( TRACK* track = dyn_cast<TRACK*>( aCollector[i] ) ) { double ratio = std::max( (double) track->GetWidth(), track->GetLength() ) / maxLength; if( ratio > trackTrackLengthRatio ) rejected.insert( track ); } } } for( int j = 0; j < aCollector.GetCount(); ++j ) { if( MODULE* mod = dyn_cast<MODULE*>( aCollector[j] ) ) { double ratio = calcRatio( maxArea, mod->GetFootprintRect().GetArea() ); if( ratio < modulePadMinCoverRatio && calcCommonArea( maxTrack, mod ) < commonAreaRatio ) rejected.insert( mod ); } } } if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything { for( BOARD_ITEM* item : rejected ) { aCollector.Remove( item ); } } }