95 std::span<const PREVIEW_NET_ASSIGNMENT> aPreviewAssignments )
97 wxLogTrace(
traceSchDragNetCollision,
"Update: Called with %zu junctions, %d selected items, %zu preview assignments",
98 aJunctions.size(), aSelection.
GetSize(), aPreviewAssignments.size() );
100 std::unordered_map<const SCH_ITEM*, std::optional<int>> previewNetCodes;
102 previewNetCodes.reserve( aPreviewAssignments.size() );
106 if( !assignment.item )
111 assignment.netCode.has_value() ? std::to_string( *assignment.netCode ).c_str() :
"none" );
113 previewNetCodes[ assignment.item ] = assignment.netCode;
116 std::vector<COLLISION_MARKER> markers;
118 if( aJunctions.empty() )
128 if(
auto marker =
analyzeJunction( junction, aSelection, previewNetCodes ) )
131 marker->position.x, marker->position.y );
132 markers.push_back( *marker );
139 if( markers.empty() && disconnections.empty() )
148 markers.size(), disconnections.size() );
153 COLOR4D baseColor( 1.0, 0.0, 0.0, 0.8 );
160 baseColor = themeColor;
163 double baseAlpha = baseColor.
a;
165 if( baseAlpha <= 0.0 )
168 double fillAlpha = std::clamp( baseAlpha * 0.35, 0.05, 1.0 );
169 double strokeAlpha = std::clamp( baseAlpha, 0.05, 1.0 );
176 int lineWidthPixels = 4;
179 lineWidthPixels = std::max( cfg->m_Selection.drag_net_collision_width, 1 );
181 double lineWidth =
m_view->ToWorld( lineWidthPixels );
183 if( lineWidth <= 0.0 )
189 m_overlay->Circle( marker.position, marker.radius );
193 m_overlay->Circle( marker.pointA, marker.radius );
194 m_overlay->Circle( marker.pointB, marker.radius );
249 const std::unordered_map<
const SCH_ITEM*, std::optional<int>>& aPreviewNetCodes )
const
258 position.
x, position.
y );
260 std::unordered_set<int> allNetCodes;
261 std::unordered_set<int> movedNetCodes;
262 std::unordered_set<int> originalNetCodes;
263 std::unordered_set<int> movedOriginalNetCodes;
264 std::unordered_set<int> stationaryOriginalNetCodes;
266 auto accumulateNet = [&](
SCH_ITEM* item )
274 if( !item->IsConnectable() )
277 item, item->GetClass().c_str() );
281 if( !item->IsConnected( position ) && !( item->IsType( { SCH_LINE_T } ) && item->HitTest( position ) ) )
284 item, item->GetClass().c_str(), position.
x, position.
y );
288 auto previewIt = aPreviewNetCodes.find( item );
290 std::optional<int> netCodeOpt;
291 std::optional<int> originalNetOpt;
294 originalNetOpt = originalIt->second;
296 if( previewIt != aPreviewNetCodes.end() )
298 netCodeOpt = previewIt->second;
300 item, item->GetClass().c_str(),
301 netCodeOpt.has_value() ? std::to_string( *netCodeOpt ).c_str() :
"none" );
305 netCodeOpt = originalIt->second;
307 item, item->GetClass().c_str(),
308 netCodeOpt.has_value() ? std::to_string( *netCodeOpt ).c_str() :
"none" );
313 item, item->GetClass().c_str() );
316 bool isSelectionItem = item->IsSelected() || aSelection.
Contains( item );
317 bool isMoved = ( previewIt != aPreviewNetCodes.end() ) || isSelectionItem;
323 originalNetCodes.insert( *originalNetOpt );
325 if( isSelectionItem )
326 movedOriginalNetCodes.insert( *originalNetOpt );
328 stationaryOriginalNetCodes.insert( *originalNetOpt );
332 item, item->GetClass().c_str() );
336 int netCode = *netCodeOpt;
337 allNetCodes.insert( netCode );
340 item, item->GetClass().c_str(), netCode, isMoved ?
"yes" :
"no" );
343 movedNetCodes.insert( netCode );
347 originalNetCodes.insert( *originalNetOpt );
349 if( isSelectionItem )
350 movedOriginalNetCodes.insert( *originalNetOpt );
352 stationaryOriginalNetCodes.insert( *originalNetOpt );
358 int candidateCount = 0;
363 accumulateNet( candidate );
369 for(
EDA_ITEM* selected : aSelection )
370 accumulateNet(
static_cast<SCH_ITEM*
>( selected ) );
373 allNetCodes.size(), movedNetCodes.size() );
375 if( !movedNetCodes.empty() )
379 for(
int netCode : movedNetCodes )
384 "analyzeJunction: Original nets=%zu, moved originals=%zu, stationary originals=%zu",
385 originalNetCodes.size(), movedOriginalNetCodes.size(), stationaryOriginalNetCodes.size() );
387 if( !movedOriginalNetCodes.empty() )
391 for(
int netCode : movedOriginalNetCodes )
395 if( !stationaryOriginalNetCodes.empty() )
399 for(
int netCode : stationaryOriginalNetCodes )
403 if( allNetCodes.size() >= 2 )
407 for(
int netCode : allNetCodes )
411 bool previewCollision = !movedNetCodes.empty() && allNetCodes.size() >= 2;
413 bool originalCollision =
false;
415 if( !movedOriginalNetCodes.empty() && !stationaryOriginalNetCodes.empty() )
417 for(
int movedNet : movedOriginalNetCodes )
419 for(
int stationaryNet : stationaryOriginalNetCodes )
421 if( movedNet != stationaryNet )
423 originalCollision =
true;
428 if( originalCollision )
433 if( !previewCollision && !originalCollision )
436 movedNetCodes.size(), allNetCodes.size() );
440 if( originalCollision && !previewCollision )
443 "analyzeJunction: Original net mismatch detected under moved endpoints" );
449 marker.
radius = std::max( base * 1.5, 800.0 );
452 position.
x, position.
y, marker.
radius );
513 bool hasNewOrPastedItems =
false;
515 for(
EDA_ITEM* edaItem : aSelection )
517 if( edaItem->IsNew() || ( edaItem->GetFlags() &
IS_PASTED ) )
519 hasNewOrPastedItems =
true;
524 if( hasNewOrPastedItems )
527 "recordOriginalConnections: Skipping - selection contains new or pasted items" );
533 for(
EDA_ITEM* edaItem : aSelection )
551 if( !candidate->CanConnect( item ) )
554 if( !candidate->IsConnected( point )
555 && !( candidate->IsType( { SCH_LINE_T } ) && candidate->HitTest( point ) ) )
560 std::vector<VECTOR2I> candidatePoints = candidate->GetConnectionPoints();
561 size_t candidateIndex = std::numeric_limits<size_t>::max();
563 for(
size_t candidatePos = 0; candidatePos < candidatePoints.size(); ++candidatePos )
565 if( candidatePoints[candidatePos] == point )
567 candidateIndex = candidatePos;
572 if( candidateIndex == std::numeric_limits<size_t>::max() )
576 size_t firstIndex =
index;
578 size_t secondIndex = candidateIndex;
580 if( secondItem < firstItem || ( secondItem == firstItem && secondIndex < firstIndex ) )
582 std::swap( firstItem, secondItem );
583 std::swap( firstIndex, secondIndex );
586 if( firstItem == secondItem )
592 if( !firstSelected && !secondSelected )
598 return connection.itemA == firstItem && connection.indexA == firstIndex
599 && connection.itemB == secondItem && connection.indexB == secondIndex;
618 std::vector<DISCONNECTION_MARKER> markers;
625 if( !itemA || !itemB )
634 if( connection.indexA >= pointsA.size() || connection.indexB >= pointsB.size() )
637 VECTOR2I pointA = pointsA[ connection.indexA ];
638 VECTOR2I pointB = pointsB[ connection.indexB ];
641 bool stillConnected = ( pointA == pointB );
644 if( !stillConnected && itemB->
IsType( { SCH_LINE_T } ) && itemB->
HitTest( pointA, 0 ) )
645 stillConnected =
true;
647 if( !stillConnected && itemA->
IsType( { SCH_LINE_T } ) && itemA->
HitTest( pointB, 0 ) )
648 stillConnected =
true;
659 double radius = std::max( { 800.0,
665 marker.pointB = pointB;
667 markers.push_back( marker );
670 if( !markers.empty() )
673 "collectDisconnectedMarkers: Identified %zu disconnections", markers.size() );