fix: deconfliction applies to ALL markers (not just labels), size-aware bounding boxes

This commit is contained in:
you
2026-03-21 00:35:37 +00:00
parent 975abade32
commit 5f3e5a6ad1
2 changed files with 40 additions and 53 deletions
+1 -1
View File
@@ -83,7 +83,7 @@
<script src="app.js?v=1774052279"></script>
<script src="home.js?v=1774042199"></script>
<script src="packets.js?v=1774052279"></script>
<script src="map.js?v=1774053246" onerror="console.error('Failed to load:', this.src)"></script>
<script src="map.js?v=1774053337" onerror="console.error('Failed to load:', this.src)"></script>
<script src="channels.js?v=1774050030" onerror="console.error('Failed to load:', this.src)"></script>
<script src="nodes.js?v=1774050030" onerror="console.error('Failed to load:', this.src)"></script>
<script src="traces.js?v=1774048777" onerror="console.error('Failed to load:', this.src)"></script>
+39 -52
View File
@@ -377,39 +377,41 @@
var _renderingMarkers = false;
var _lastDeconflictZoom = null;
function deconflictLabels(labelMarkers, mapRef) {
function deconflictLabels(markers, mapRef) {
const placed = [];
const LABEL_W = 38;
const LABEL_H = 24;
const PAD = 6;
const PAD = 4;
const overlaps = function(b) {
return placed.some(function(p) {
return b.x < p.x + p.w + PAD && b.x + b.w + PAD > p.x &&
b.y < p.y + p.h + PAD && b.y + b.h + PAD > p.y;
});
var overlaps = function(b) {
for (var k = 0; k < placed.length; k++) {
var p = placed[k];
if (b.x < p.x + p.w + PAD && b.x + b.w + PAD > p.x &&
b.y < p.y + p.h + PAD && b.y + b.h + PAD > p.y) return true;
}
return false;
};
// Generate spiral offsets — 5 rings, 8 directions each, up to 120px out
// Spiral offsets — 6 rings, 8 directions, up to ~132px
var offsets = [];
for (var ring = 1; ring <= 6; ring++) {
var dist = ring * 28;
var dist = ring * 22;
for (var angle = 0; angle < 360; angle += 45) {
var rad = angle * Math.PI / 180;
offsets.push([Math.round(Math.cos(rad) * dist), Math.round(Math.sin(rad) * dist)]);
}
}
for (var i = 0; i < labelMarkers.length; i++) {
var m = labelMarkers[i];
for (var i = 0; i < markers.length; i++) {
var m = markers[i];
var w = m.isLabel ? 38 : 20;
var h = m.isLabel ? 24 : 20;
var pt = mapRef.latLngToLayerPoint(m.latLng);
var bestPt = pt;
var box = { x: pt.x - LABEL_W / 2, y: pt.y - LABEL_H / 2, w: LABEL_W, h: LABEL_H };
var box = { x: pt.x - w / 2, y: pt.y - h / 2, w: w, h: h };
if (overlaps(box)) {
for (var j = 0; j < offsets.length; j++) {
var tryPt = L.point(pt.x + offsets[j][0], pt.y + offsets[j][1]);
var tryBox = { x: tryPt.x - LABEL_W / 2, y: tryPt.y - LABEL_H / 2, w: LABEL_W, h: LABEL_H };
var tryBox = { x: tryPt.x - w / 2, y: tryPt.y - h / 2, w: w, h: h };
if (!overlaps(tryBox)) {
bestPt = tryPt;
box = tryBox;
@@ -439,43 +441,13 @@
return true;
});
const labelMarkers = [];
const allMarkers = [];
for (const node of filtered) {
const useLabel = node.role === 'repeater' && filters.hashLabels;
const icon = useLabel ? makeRepeaterLabelIcon(node) : makeMarkerIcon(node.role || 'companion');
const latLng = L.latLng(node.lat, node.lon);
if (useLabel) {
labelMarkers.push({ latLng, node, icon });
} else {
const marker = L.marker(latLng, {
icon,
alt: `${node.name || 'Unknown'} (${node.role || 'node'})`,
});
marker.bindPopup(buildPopup(node), { maxWidth: 280 });
markerLayer.addLayer(marker);
}
}
if (labelMarkers.length > 0) {
deconflictLabels(labelMarkers, map);
for (const m of labelMarkers) {
const pos = m.adjustedLatLng || m.latLng;
const marker = L.marker(pos, {
icon: m.icon,
alt: `${m.node.name || 'Unknown'} (${m.node.role || 'node'})`,
});
marker.bindPopup(buildPopup(m.node), { maxWidth: 280 });
markerLayer.addLayer(marker);
if (m.offset > 15) {
const line = L.polyline([m.latLng, pos], {
color: '#999', weight: 1, dashArray: '3,3', opacity: 0.6
});
markerLayer.addLayer(line);
}
}
allMarkers.push({ latLng, node, icon, isLabel: useLabel, popupFn: function() { return buildPopup(node); }, alt: (node.name || 'Unknown') + ' (' + (node.role || 'node') + ')' });
}
// Add observer markers
@@ -483,12 +455,27 @@
for (const obs of observers) {
if (!obs.lat || !obs.lon) continue;
const icon = makeMarkerIcon('observer');
const marker = L.marker([obs.lat, obs.lon], {
icon,
alt: `${obs.name || obs.id} (observer)`,
const latLng = L.latLng(obs.lat, obs.lon);
allMarkers.push({ latLng, node: obs, icon, isLabel: false, popupFn: function() { return buildObserverPopup(obs); }, alt: (obs.name || obs.id || 'Unknown') + ' (observer)' });
}
}
// Deconflict ALL markers
if (allMarkers.length > 0) {
deconflictLabels(allMarkers, map);
}
for (const m of allMarkers) {
const pos = m.adjustedLatLng || m.latLng;
const marker = L.marker(pos, { icon: m.icon, alt: m.alt });
marker.bindPopup(m.popupFn(), { maxWidth: 280 });
markerLayer.addLayer(marker);
if (m.offset > 15 && m.isLabel) {
const line = L.polyline([m.latLng, pos], {
color: '#999', weight: 1, dashArray: '3,3', opacity: 0.6
});
marker.bindPopup(buildObserverPopup(obs), { maxWidth: 280 });
markerLayer.addLayer(marker);
markerLayer.addLayer(line);
}
}
}