x-axis justierung: visualize
This commit is contained in:
@@ -51,6 +51,23 @@
|
||||
font-size: 11px;
|
||||
}
|
||||
.btn:hover { border-color: var(--accent); color: var(--accent); }
|
||||
#run-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 4px 12px;
|
||||
background: var(--panel);
|
||||
border-bottom: 1px solid var(--border);
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.run-lbl {
|
||||
font-size: 10px;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .04em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#canvas-wrap { flex: 1; min-height: 360px; position: relative; overflow: hidden; }
|
||||
canvas { display: block; width: 100%; height: 100%; }
|
||||
#hint {
|
||||
@@ -127,6 +144,7 @@
|
||||
<span><span class="dot circle" style="background:#dde3ec"></span>Erkannt (nur 2D)</span>
|
||||
<span><span class="dot circle" style="background:#fbbf24"></span>Gemessen (3b)</span>
|
||||
<span><span class="dot circle" style="background:#3b82f6"></span>Fremd (3b)</span>
|
||||
<span><span class="dot circle" style="background:#f97316"></span>Vergleich</span>
|
||||
<span><span class="dot" style="background:#9b7bff"></span>Kamera</span>
|
||||
</div>
|
||||
<span id="stats"></span>
|
||||
@@ -134,6 +152,19 @@
|
||||
<button class="btn" id="btnReload">↺</button>
|
||||
</div>
|
||||
|
||||
<div id="run-bar">
|
||||
<span class="run-lbl">Basis</span>
|
||||
<select id="sel-run-primary" class="btn" style="min-width:158px">
|
||||
<option value="">⟳ aktuellster</option>
|
||||
</select>
|
||||
<span class="run-lbl" style="margin-left:10px">Vergleich
|
||||
<span style="text-transform:none;opacity:.7;font-size:9px">(nur fremd)</span>
|
||||
</span>
|
||||
<select id="sel-run-compare" class="btn" style="min-width:158px">
|
||||
<option value="">– keiner –</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="canvas-wrap">
|
||||
<canvas id="cv"></canvas>
|
||||
<div id="hint">Orbit · Scroll · Rechte Taste = Pan</div>
|
||||
@@ -182,7 +213,8 @@ const gPaper = new THREE.Group(); // weißes A0-Papier
|
||||
const gMarkers = new THREE.Group(); // Modell-Rechtecke
|
||||
const gMeasured = new THREE.Group(); // gemessene Positionen (3b)
|
||||
const gCameras = new THREE.Group(); // Kamera-Frusta
|
||||
scene.add(gPaper, gMarkers, gMeasured, gCameras);
|
||||
const gCompare = new THREE.Group(); // Vergleichs-Punkte (anderer Timestamp, nur fremd)
|
||||
scene.add(gPaper, gMarkers, gMeasured, gCameras, gCompare);
|
||||
|
||||
function clearGroup(g) {
|
||||
while (g.children.length) {
|
||||
@@ -542,11 +574,17 @@ function buildTable(data) {
|
||||
}
|
||||
|
||||
// ── Daten laden ───────────────────────────────────────────────────────────────
|
||||
|
||||
/** Haupt-Run laden (Basis-Dropdown). Ohne Selektion → neuester Run. */
|
||||
async function loadData() {
|
||||
const statusEl = document.getElementById('status');
|
||||
const statusEl = document.getElementById('status');
|
||||
statusEl.textContent = 'Laden …';
|
||||
const selRun = document.getElementById('sel-run-primary')?.value ?? '';
|
||||
const url = selRun
|
||||
? `/api/board/latest?run=${encodeURIComponent(selRun)}`
|
||||
: '/api/board/latest';
|
||||
try {
|
||||
const r = await fetch('/api/board/latest');
|
||||
const r = await fetch(url);
|
||||
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
||||
const data = await r.json();
|
||||
if (!data.runDir) {
|
||||
@@ -564,10 +602,63 @@ async function loadData() {
|
||||
}
|
||||
}
|
||||
|
||||
loadData();
|
||||
document.getElementById('btnReload').addEventListener('click', loadData);
|
||||
/** Vergleichs-Run laden (Compare-Dropdown) – zeigt nur fremd-triangulierte Marker als orange Kugeln. */
|
||||
async function loadCompareData() {
|
||||
clearGroup(gCompare);
|
||||
const selRun = document.getElementById('sel-run-compare')?.value ?? '';
|
||||
if (!selRun) return;
|
||||
try {
|
||||
const r = await fetch(`/api/board/latest?run=${encodeURIComponent(selRun)}`);
|
||||
if (!r.ok) return;
|
||||
const data = await r.json();
|
||||
const markers = data.measuredMarkers?.markers ?? [];
|
||||
if (!markers.length) return;
|
||||
// Board-Marker-IDs aus Robot.json (für diesen Run)
|
||||
const boardIds = new Set((data.robot?.links?.Board?.markers ?? []).map(m => m.id));
|
||||
for (const m of markers) {
|
||||
if (!boardIds.has(m.marker_id)) {
|
||||
// Nicht zugeordnet → orange Kugel (Vergleich)
|
||||
gCompare.add(makeSphere(r2vArr(m.position_mm), 0.006, 0xf97316));
|
||||
}
|
||||
}
|
||||
} catch { /* kein 3b-Output für diesen Run */ }
|
||||
}
|
||||
|
||||
/** Run-Listen laden und beide Dropdowns befüllen. */
|
||||
async function initRunSelectors() {
|
||||
try {
|
||||
const [r5, r10] = await Promise.all([
|
||||
fetch('/api/board/runs?limit=5'),
|
||||
fetch('/api/board/runs?limit=10'),
|
||||
]);
|
||||
const { runs: runs5 } = r5.ok ? await r5.json() : { runs: [] };
|
||||
const { runs: runs10 } = r10.ok ? await r10.json() : { runs: [] };
|
||||
|
||||
const selP = document.getElementById('sel-run-primary');
|
||||
const selC = document.getElementById('sel-run-compare');
|
||||
const cur = selP?.value ?? ''; // aktuell gewählten Run behalten
|
||||
|
||||
if (selP) {
|
||||
selP.innerHTML = '<option value="">⟳ aktuellster</option>' +
|
||||
runs5.map(r => `<option value="${r}"${r === cur ? ' selected' : ''}>${r}</option>`).join('');
|
||||
}
|
||||
if (selC) {
|
||||
selC.innerHTML = '<option value="">– keiner –</option>' +
|
||||
runs10.map(r => `<option value="${r}">${r}</option>`).join('');
|
||||
}
|
||||
} catch { /* offline oder noch keine Runs */ }
|
||||
}
|
||||
|
||||
initRunSelectors().then(() => loadData());
|
||||
document.getElementById('btnReload').addEventListener('click', () => {
|
||||
initRunSelectors().then(() => { loadData(); loadCompareData(); });
|
||||
});
|
||||
document.getElementById('sel-run-primary')?.addEventListener('change', loadData);
|
||||
document.getElementById('sel-run-compare')?.addEventListener('change', loadCompareData);
|
||||
window.addEventListener('message', (e) => {
|
||||
if (e.data?.type === 'reload') loadData();
|
||||
if (e.data?.type === 'reload') {
|
||||
initRunSelectors().then(() => { loadData(); loadCompareData(); });
|
||||
}
|
||||
});
|
||||
|
||||
// ── Resize & Render-Loop ──────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user