99 std::span<const PREVIEW_NET_ASSIGNMENT> aPreviewAssignments )
101 wxLogTrace(
traceSchDragNetCollision,
"Update: Called with %zu junctions, %d selected items, %zu preview assignments",
102 aJunctions.size(), aSelection.
GetSize(), aPreviewAssignments.size() );
104 std::unordered_map<const SCH_ITEM*, std::optional<int>> previewNetCodes;
106 previewNetCodes.reserve( aPreviewAssignments.size() );
110 if( !assignment.item )
115 assignment.netCode.has_value() ? std::to_string( *assignment.netCode ).c_str() :
"none" );
117 previewNetCodes[ assignment.item ] = assignment.netCode;
120 std::vector<COLLISION_MARKER> markers;
122 if( aJunctions.empty() )
132 if(
auto marker =
analyzeJunction( junction, aSelection, previewNetCodes ) )
135 marker->position.x, marker->position.y );
136 markers.push_back( *marker );
143 if( markers.empty() && disconnections.empty() )
152 markers.size(), disconnections.size() );
157 COLOR4D baseColor( 1.0, 0.0, 0.0, 0.8 );
164 baseColor = themeColor;
167 double baseAlpha = baseColor.
a;
169 if( baseAlpha <= 0.0 )
172 double fillAlpha = std::clamp( baseAlpha * 0.35, 0.05, 1.0 );
173 double strokeAlpha = std::clamp( baseAlpha, 0.05, 1.0 );
180 int lineWidthPixels = 4;
183 lineWidthPixels = std::max( cfg->m_Selection.drag_net_collision_width, 1 );
185 double lineWidth =
m_view->ToWorld( lineWidthPixels );
187 if( lineWidth <= 0.0 )
193 m_overlay->Circle( marker.position, marker.radius );
197 m_overlay->Circle( marker.pointA, marker.radius );
198 m_overlay->Circle( marker.pointB, marker.radius );
253 const std::unordered_map<
const SCH_ITEM*, std::optional<int>>& aPreviewNetCodes )
const
262 position.
x, position.
y );
264 std::unordered_set<int> allNetCodes;
265 std::unordered_set<int> movedNetCodes;
266 std::unordered_set<int> originalNetCodes;
267 std::unordered_set<int> movedOriginalNetCodes;
268 std::unordered_set<int> stationaryOriginalNetCodes;
270 auto accumulateNet = [&](
SCH_ITEM* item )
278 if( !item->IsConnectable() )
281 item, item->GetClass().c_str() );
285 if( !item->IsConnected( position ) && !( item->IsType( { SCH_LINE_T } ) && item->HitTest( position ) ) )
288 item, item->GetClass().c_str(), position.
x, position.
y );
292 auto previewIt = aPreviewNetCodes.find( item );
294 std::optional<int> netCodeOpt;
295 std::optional<int> originalNetOpt;
298 originalNetOpt = originalIt->second;
300 if( previewIt != aPreviewNetCodes.end() )
302 netCodeOpt = previewIt->second;
304 item, item->GetClass().c_str(),
305 netCodeOpt.has_value() ? std::to_string( *netCodeOpt ).c_str() :
"none" );
309 netCodeOpt = originalIt->second;
311 item, item->GetClass().c_str(),
312 netCodeOpt.has_value() ? std::to_string( *netCodeOpt ).c_str() :
"none" );
317 item, item->GetClass().c_str() );
320 bool isSelectionItem = item->IsSelected() || aSelection.
Contains( item );
321 bool isMoved = ( previewIt != aPreviewNetCodes.end() ) || isSelectionItem;
327 originalNetCodes.insert( *originalNetOpt );
329 if( isSelectionItem )
330 movedOriginalNetCodes.insert( *originalNetOpt );
332 stationaryOriginalNetCodes.insert( *originalNetOpt );
336 item, item->GetClass().c_str() );
340 int netCode = *netCodeOpt;
341 allNetCodes.insert( netCode );
344 item, item->GetClass().c_str(), netCode, isMoved ?
"yes" :
"no" );
347 movedNetCodes.insert( netCode );
351 originalNetCodes.insert( *originalNetOpt );
353 if( isSelectionItem )
354 movedOriginalNetCodes.insert( *originalNetOpt );
356 stationaryOriginalNetCodes.insert( *originalNetOpt );
362 int candidateCount = 0;
367 accumulateNet( candidate );
373 for(
EDA_ITEM* selected : aSelection )
374 accumulateNet(
static_cast<SCH_ITEM*
>( selected ) );
377 allNetCodes.size(), movedNetCodes.size() );
379 if( !movedNetCodes.empty() )
383 for(
int netCode : movedNetCodes )
388 "analyzeJunction: Original nets=%zu, moved originals=%zu, stationary originals=%zu",
389 originalNetCodes.size(), movedOriginalNetCodes.size(), stationaryOriginalNetCodes.size() );
391 if( !movedOriginalNetCodes.empty() )
395 for(
int netCode : movedOriginalNetCodes )
399 if( !stationaryOriginalNetCodes.empty() )
403 for(
int netCode : stationaryOriginalNetCodes )
407 if( allNetCodes.size() >= 2 )
411 for(
int netCode : allNetCodes )
415 bool previewCollision = !movedNetCodes.empty() && allNetCodes.size() >= 2;
417 bool originalCollision =
false;
419 if( !movedOriginalNetCodes.empty() && !stationaryOriginalNetCodes.empty() )
421 for(
int movedNet : movedOriginalNetCodes )
423 for(
int stationaryNet : stationaryOriginalNetCodes )
425 if( movedNet != stationaryNet )
427 originalCollision =
true;
432 if( originalCollision )
437 if( !previewCollision && !originalCollision )
440 movedNetCodes.size(), allNetCodes.size() );
444 if( originalCollision && !previewCollision )
447 "analyzeJunction: Original net mismatch detected under moved endpoints" );
453 marker.
radius = std::max( base * 1.5, 800.0 );
456 position.
x, position.
y, marker.
radius );
517 bool hasNewOrPastedItems =
false;
519 for(
EDA_ITEM* edaItem : aSelection )
521 if( edaItem->IsNew() || ( edaItem->GetFlags() &
IS_PASTED ) )
523 hasNewOrPastedItems =
true;
528 if( hasNewOrPastedItems )
531 "recordOriginalConnections: Skipping - selection contains new or pasted items" );
537 for(
EDA_ITEM* edaItem : aSelection )
546 for(
size_t index = 0; index < points.size(); ++index )
548 const VECTOR2I& point = points[index];
555 if( !candidate->CanConnect( item ) )
558 if( !candidate->IsConnected( point )
559 && !( candidate->IsType( { SCH_LINE_T } ) && candidate->HitTest( point ) ) )
564 std::vector<VECTOR2I> candidatePoints = candidate->GetConnectionPoints();
565 size_t candidateIndex = std::numeric_limits<size_t>::max();
567 for(
size_t candidatePos = 0; candidatePos < candidatePoints.size(); ++candidatePos )
569 if( candidatePoints[candidatePos] == point )
571 candidateIndex = candidatePos;
576 if( candidateIndex == std::numeric_limits<size_t>::max() )
580 size_t firstIndex = index;
582 size_t secondIndex = candidateIndex;
584 if( secondItem < firstItem || ( secondItem == firstItem && secondIndex < firstIndex ) )
586 std::swap( firstItem, secondItem );
587 std::swap( firstIndex, secondIndex );
590 if( firstItem == secondItem )
596 if( !firstSelected && !secondSelected )
602 return connection.itemA == firstItem && connection.indexA == firstIndex
603 && connection.itemB == secondItem && connection.indexB == secondIndex;