92 std::span<const PREVIEW_NET_ASSIGNMENT> aPreviewAssignments )
94 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"Update: Called with %zu junctions, %d selected items, %zu preview assignments",
95 aJunctions.size(), aSelection.
GetSize(), aPreviewAssignments.size() );
97 if( aJunctions.empty() )
99 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"Update: No junctions to check, clearing overlay" );
108 std::unordered_map<const SCH_ITEM*, std::optional<int>> previewNetCodes;
110 previewNetCodes.reserve( aPreviewAssignments.size() );
114 if( !assignment.item )
117 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"Update: Preview assignment - item %p, netCode %s",
119 assignment.netCode.has_value() ? std::to_string( *assignment.netCode ).c_str() :
"none" );
121 previewNetCodes[ assignment.item ] = assignment.netCode;
124 std::vector<COLLISION_MARKER> markers;
126 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"Update: Analyzing %zu junctions", aJunctions.size() );
130 if(
auto marker =
analyzeJunction( junction, aSelection, previewNetCodes ) )
132 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"Update: Junction at (%d, %d) has collision",
133 marker->position.x, marker->position.y );
134 markers.push_back( *marker );
138 if( markers.empty() )
140 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"Update: No collision markers found" );
146 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"Update: Drawing %zu collision markers", markers.size() );
148 COLOR4D baseColor( 1.0, 0.0, 0.0, 0.8 );
155 baseColor = themeColor;
158 double baseAlpha = baseColor.
a;
160 if( baseAlpha <= 0.0 )
163 double fillAlpha = std::clamp( baseAlpha * 0.35, 0.05, 1.0 );
164 double strokeAlpha = std::clamp( baseAlpha, 0.05, 1.0 );
171 int lineWidthPixels = 4;
174 lineWidthPixels = std::max( cfg->m_Selection.drag_net_collision_width, 1 );
176 double lineWidth =
m_view->ToWorld( lineWidthPixels );
178 if( lineWidth <= 0.0 )
184 m_overlay->Circle( marker.position, marker.radius );
236 const std::unordered_map<
const SCH_ITEM*, std::optional<int>>& aPreviewNetCodes )
const
244 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"analyzeJunction: Checking junction at (%d, %d)",
245 position.
x, position.
y );
247 std::unordered_set<int> allNetCodes;
248 std::unordered_set<int> movedNetCodes;
249 std::unordered_set<int> originalNetCodes;
250 std::unordered_set<int> movedOriginalNetCodes;
251 std::unordered_set<int> stationaryOriginalNetCodes;
253 auto accumulateNet = [&](
SCH_ITEM* item )
257 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
" accumulateNet: null item" );
261 if( !item->IsConnectable() )
263 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
" accumulateNet: item %p (%s) not connectable",
264 item, item->GetClass().c_str() );
268 if( !item->IsConnected( position ) && !( item->IsType( { SCH_LINE_T } ) && item->HitTest( position ) ) )
270 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
" accumulateNet: item %p (%s) not connected at (%d, %d)",
271 item, item->GetClass().c_str(), position.
x, position.
y );
275 auto previewIt = aPreviewNetCodes.find( item );
277 std::optional<int> netCodeOpt;
278 std::optional<int> originalNetOpt;
281 originalNetOpt = originalIt->second;
283 if( previewIt != aPreviewNetCodes.end() )
285 netCodeOpt = previewIt->second;
286 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
" accumulateNet: item %p (%s) using preview net %s",
287 item, item->GetClass().c_str(),
288 netCodeOpt.has_value() ? std::to_string( *netCodeOpt ).c_str() :
"none" );
292 netCodeOpt = originalIt->second;
293 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
" accumulateNet: item %p (%s) using cached net %s",
294 item, item->GetClass().c_str(),
295 netCodeOpt.has_value() ? std::to_string( *netCodeOpt ).c_str() :
"none" );
299 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
" accumulateNet: item %p (%s) has no net code",
300 item, item->GetClass().c_str() );
303 bool isSelectionItem = item->IsSelected() || aSelection.
Contains( item );
304 bool isMoved = ( previewIt != aPreviewNetCodes.end() ) || isSelectionItem;
310 originalNetCodes.insert( *originalNetOpt );
312 if( isSelectionItem )
313 movedOriginalNetCodes.insert( *originalNetOpt );
315 stationaryOriginalNetCodes.insert( *originalNetOpt );
318 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
" accumulateNet: item %p (%s) netCode is nullopt",
319 item, item->GetClass().c_str() );
323 int netCode = *netCodeOpt;
324 allNetCodes.insert( netCode );
326 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
" accumulateNet: item %p (%s) net %d, moved=%s",
327 item, item->GetClass().c_str(), netCode, isMoved ?
"yes" :
"no" );
330 movedNetCodes.insert( netCode );
334 originalNetCodes.insert( *originalNetOpt );
336 if( isSelectionItem )
337 movedOriginalNetCodes.insert( *originalNetOpt );
339 stationaryOriginalNetCodes.insert( *originalNetOpt );
343 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"analyzeJunction: Checking items overlapping position" );
345 int candidateCount = 0;
350 accumulateNet( candidate );
353 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"analyzeJunction: Checked %d overlapping items", candidateCount );
354 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"analyzeJunction: Checking %d selected items", aSelection.
GetSize() );
356 for(
EDA_ITEM* selected : aSelection )
357 accumulateNet(
static_cast<SCH_ITEM*
>( selected ) );
359 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"analyzeJunction: Found %zu unique nets, %zu moved nets",
360 allNetCodes.size(), movedNetCodes.size() );
362 if( !movedNetCodes.empty() )
364 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"analyzeJunction: Moved nets:" );
366 for(
int netCode : movedNetCodes )
367 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
" - Net %d", netCode );
370 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
371 "analyzeJunction: Original nets=%zu, moved originals=%zu, stationary originals=%zu",
372 originalNetCodes.size(), movedOriginalNetCodes.size(), stationaryOriginalNetCodes.size() );
374 if( !movedOriginalNetCodes.empty() )
376 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"analyzeJunction: Moved original nets:" );
378 for(
int netCode : movedOriginalNetCodes )
379 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
" - Net %d", netCode );
382 if( !stationaryOriginalNetCodes.empty() )
384 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"analyzeJunction: Stationary original nets:" );
386 for(
int netCode : stationaryOriginalNetCodes )
387 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
" - Net %d", netCode );
390 if( allNetCodes.size() >= 2 )
392 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"analyzeJunction: All nets at junction:" );
394 for(
int netCode : allNetCodes )
395 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
" - Net %d", netCode );
398 bool previewCollision = !movedNetCodes.empty() && allNetCodes.size() >= 2;
400 bool originalCollision =
false;
402 if( !movedOriginalNetCodes.empty() && !stationaryOriginalNetCodes.empty() )
404 for(
int movedNet : movedOriginalNetCodes )
406 for(
int stationaryNet : stationaryOriginalNetCodes )
408 if( movedNet != stationaryNet )
410 originalCollision =
true;
415 if( originalCollision )
420 if( !previewCollision && !originalCollision )
422 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"analyzeJunction: No collision (movedNets=%zu, allNets=%zu)",
423 movedNetCodes.size(), allNetCodes.size() );
427 if( originalCollision && !previewCollision )
429 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
430 "analyzeJunction: Original net mismatch detected under moved endpoints" );
436 marker.
radius = std::max( base * 1.5, 800.0 );
438 wxLogTrace(
"KICAD_SCH_DRAG_NET_COLLISION",
"analyzeJunction: COLLISION DETECTED at (%d, %d) with radius %.1f",
439 position.
x, position.
y, marker.
radius );