- Fix GetShoppingList to use proper GDOP-based accuracy estimation instead of simplified implementation - Fix simulation flow to run synchronously and display results immediately instead of polling - Update GDOP legend HTML structure with proper styling hooks - Add shopping list container with "add node at worst spot" button Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
210 lines
9.2 KiB
HTML
210 lines
9.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
<title>Spaxel — Simulator</title>
|
|
<link rel="stylesheet" href="css/tokens.css">
|
|
<link rel="stylesheet" href="css/layout.css">
|
|
<link rel="stylesheet" href="css/panels.css">
|
|
<link rel="stylesheet" href="css/simulator.css">
|
|
<style>
|
|
.simulator-header__link {
|
|
color: var(--text-secondary);
|
|
text-decoration: none;
|
|
font-size: var(--text-sm);
|
|
}
|
|
.simulator-header__link:hover {
|
|
color: var(--text-primary);
|
|
}
|
|
#sim-status {
|
|
color: var(--text-secondary);
|
|
flex: 1;
|
|
text-align: center;
|
|
font-size: var(--text-sm);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="has-mobile-nav">
|
|
<div class="app-shell app-shell--live">
|
|
|
|
<nav class="app-header setup-header">
|
|
<a href="/" class="simulator-header__link">← Home</a>
|
|
<span style="color:var(--text-primary);font-weight:600;">Pre-Deployment Simulator</span>
|
|
<span id="sim-status"></span>
|
|
<a href="/setup" class="simulator-header__link">Setup</a>
|
|
</nav>
|
|
|
|
<main class="app-main">
|
|
<div id="simulator-container">
|
|
<!-- 3D Scene Container -->
|
|
<div id="scene-container" class="simulator-scene"></div>
|
|
|
|
<!-- GDOP Overlay Container -->
|
|
<canvas id="gdop-canvas" class="gdop-overlay"></canvas>
|
|
|
|
<!-- Simulation Controls Panel -->
|
|
<div id="sim-controls" class="sim-panel sim-panel--controls">
|
|
<h3>Simulation Controls</h3>
|
|
<div class="sim-controls__row">
|
|
<button id="btn-simulate" class="sim-btn sim-btn--primary">Run Simulation</button>
|
|
<button id="btn-stop" class="sim-btn sim-btn--danger">Stop</button>
|
|
<button id="btn-reset" class="sim-btn">Reset</button>
|
|
</div>
|
|
<div class="sim-controls__row">
|
|
<label for="sim-duration">Duration (s):</label>
|
|
<input type="number" id="sim-duration" value="60" min="10" max="300">
|
|
</div>
|
|
<div class="sim-controls__row">
|
|
<label for="sim-walkers">Walkers:</label>
|
|
<input type="number" id="sim-walkers" value="3" min="1" max="10">
|
|
</div>
|
|
<div class="sim-controls__row">
|
|
<label>
|
|
<input type="checkbox" id="sim-show-paths">
|
|
Show walker paths
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Space Configuration Panel -->
|
|
<div id="space-panel" class="sim-panel sim-panel--space">
|
|
<h3>Space Configuration</h3>
|
|
<div class="sim-space__form">
|
|
<div class="sim-space__row">
|
|
<label for="space-width">Width (m):</label>
|
|
<input type="number" id="space-width" value="10" min="3" max="50" step="0.5">
|
|
</div>
|
|
<div class="sim-space__row">
|
|
<label for="space-depth">Depth (m):</label>
|
|
<input type="number" id="space-depth" value="8" min="3" max="50" step="0.5">
|
|
</div>
|
|
<div class="sim-space__row">
|
|
<label for="space-height">Height (m):</label>
|
|
<input type="number" id="space-height" value="2.5" min="2" max="10" step="0.1">
|
|
</div>
|
|
<button id="btn-apply-space" class="sim-btn">Apply Space</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Node Placement Panel -->
|
|
<div id="node-panel" class="sim-panel sim-panel--nodes">
|
|
<h3>Node Placement</h3>
|
|
<div class="sim-nodes__list" id="node-list"></div>
|
|
<div class="sim-nodes__add">
|
|
<button id="btn-add-node" class="sim-btn sim-btn--primary">+ Add Virtual Node</button>
|
|
</div>
|
|
<div class="sim-nodes__suggestions" id="node-suggestions"></div>
|
|
</div>
|
|
|
|
<!-- Results Panel -->
|
|
<div id="results-panel" class="sim-panel sim-panel--results">
|
|
<h3>Simulation Results</h3>
|
|
<div class="sim-results__metrics">
|
|
<div class="sim-metric">
|
|
<span class="sim-metric__label">Coverage Score:</span>
|
|
<span class="sim-metric__value" id="metric-coverage">--</span>
|
|
</div>
|
|
<div class="sim-metric">
|
|
<span class="sim-metric__label">Avg GDOP:</span>
|
|
<span class="sim-metric__value" id="metric-gdop">--</span>
|
|
</div>
|
|
<div class="sim-metric">
|
|
<span class="sim-metric__label">Blobs Detected:</span>
|
|
<span class="sim-metric__value" id="metric-blobs">--</span>
|
|
</div>
|
|
</div>
|
|
<div class="sim-results__gdop-legend">
|
|
<h4>GDOP Quality</h4>
|
|
<div class="gdop-legend">
|
|
<div class="gdop-legend-item" data-quality="excellent">
|
|
<span class="gdop-legend-color"></span>
|
|
<span class="gdop-legend-label">Excellent (<2)</span>
|
|
</div>
|
|
<div class="gdop-legend-item" data-quality="good">
|
|
<span class="gdop-legend-color"></span>
|
|
<span class="gdop-legend-label">Good (2-4)</span>
|
|
</div>
|
|
<div class="gdop-legend-item" data-quality="fair">
|
|
<span class="gdop-legend-color"></span>
|
|
<span class="gdop-legend-label">Fair (4-8)</span>
|
|
</div>
|
|
<div class="gdop-legend-item" data-quality="poor">
|
|
<span class="gdop-legend-color"></span>
|
|
<span class="gdop-legend-label">Poor (>8)</span>
|
|
</div>
|
|
<div class="gdop-legend-item" data-quality="none">
|
|
<span class="gdop-legend-color"></span>
|
|
<span class="gdop-legend-label">None</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sim-results__recommendations" id="sim-recommendations"></div>
|
|
<div class="sim-results__shopping" id="sim-shopping" style="display:none;">
|
|
<h4>Shopping List</h4>
|
|
<div id="shopping-list-content"></div>
|
|
<button id="btn-add-node-here" class="sim-btn sim-btn--primary" style="margin-top:8px;">
|
|
+ Add Node at Worst Coverage Spot
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Walker Visualization -->
|
|
<div id="walkers-container" class="sim-walkers"></div>
|
|
</div>
|
|
</main>
|
|
|
|
</div><!-- /.app-shell -->
|
|
|
|
<!-- Mobile bottom nav -->
|
|
<nav class="app-mobile-nav" aria-label="Main navigation">
|
|
<ul class="app-mobile-nav__list">
|
|
<li class="app-mobile-nav__item">
|
|
<a href="/" class="app-mobile-nav__link">
|
|
<span>🏠</span> Home
|
|
</a>
|
|
</li>
|
|
<li class="app-mobile-nav__item">
|
|
<a href="/live" class="app-mobile-nav__link">
|
|
<span>⛶</span> Live
|
|
</a>
|
|
</li>
|
|
<li class="app-mobile-nav__item">
|
|
<a href="/fleet" class="app-mobile-nav__link">
|
|
<span>📡</span> Fleet
|
|
</a>
|
|
</li>
|
|
<li class="app-mobile-nav__item">
|
|
<a href="/simulator" class="app-mobile-nav__link app-mobile-nav__link--active">
|
|
<span>📊</span> Sim
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
|
|
<!-- Three.js from CDN -->
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/TransformControls.js"></script>
|
|
|
|
<!-- 3D Visualization Layer -->
|
|
<script src="js/viz3d.js"></script>
|
|
|
|
<!-- Placement module (reused for TransformControls integration) -->
|
|
<script src="js/placement.js"></script>
|
|
|
|
<!-- Core simulator script -->
|
|
<script src="js/simulate.js"></script>
|
|
|
|
<script>
|
|
// Initialize simulator on page load
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
if (typeof SpaxelSimulator !== 'undefined' && SpaxelSimulator.init) {
|
|
SpaxelSimulator.init();
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|