Files
vision-alignment/index.html
T
2026-09-04 15:48:02 +02:00

179 lines
7.2 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vision Alignment</title>
</head>
<body>
<div id="app">
<header class="header">
<h1>Vision Alignment</h1>
<p class="subtitle">
Three-point fiducial registration · camera offset · software skew compensation
</p>
</header>
<section class="controls">
<label class="field">
<span>Camera</span>
<select id="camera-select">
<option value="">— select after starting —</option>
</select>
</label>
<div class="btn-row">
<button type="button" id="btn-start" class="btn primary">Start camera</button>
<button type="button" id="btn-stop" class="btn" disabled>Stop</button>
</div>
<p id="status" class="status" role="status">Camera idle</p>
</section>
<section class="viewport-wrap">
<div class="camera-viewport" id="viewport">
<video id="cam" autoplay playsinline muted></video>
<div class="crosshair" aria-hidden="true"><span class="ring"></span></div>
<div class="placeholder" id="placeholder">
Start the camera, jog so the cross-hair is on a fiducial, then mark points.
</div>
</div>
</section>
<section class="controls jog-section">
<h2 class="section-title">Jog</h2>
<div class="jog-layout">
<div class="jog-xy" aria-label="XY jog">
<button type="button" class="btn jog-btn" data-jog="y+" title="Y+">Y+</button>
<button type="button" class="btn jog-btn" data-jog="x-" title="X-">X-</button>
<button type="button" class="btn jog-btn jog-stop" data-jog="stop" title="Stop jog"></button>
<button type="button" class="btn jog-btn" data-jog="x+" title="X+">X+</button>
<button type="button" class="btn jog-btn" data-jog="y-" title="Y-">Y-</button>
</div>
<div class="jog-z" aria-label="Z jog">
<button type="button" class="btn jog-btn" data-jog="z+" title="Z+">Z+</button>
<button type="button" class="btn jog-btn" data-jog="z-" title="Z-">Z-</button>
</div>
<div class="jog-settings">
<div class="jog-dro">
<div class="dro-row"><span>MPos</span> <code id="jog-mpos">X — Y — Z —</code></div>
<div class="dro-row"><span>WPos</span> <code id="jog-wpos">X — Y — Z —</code></div>
</div>
<div class="jog-selects">
<label class="field compact">
<span>Step</span>
<select id="jog-step">
<option value="0.1">0.1</option>
<option value="1" selected>1</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
</select>
</label>
<label class="field compact">
<span>Feed</span>
<select id="jog-feed">
<option value="100">100</option>
<option value="500" selected>500</option>
<option value="1000">1000</option>
<option value="2000">2000</option>
<option value="3000">3000</option>
</select>
</label>
</div>
<label class="field checkbox">
<input type="checkbox" id="jog-continuous" />
<span>Hold = continuous</span>
</label>
</div>
</div>
<p class="hint">Uses Grbl <code>$J=</code> jogging. Hold buttons for continuous when enabled; release or ⏹ to cancel.</p>
</section>
<section class="controls">
<h2 class="section-title">Camera-to-spindle offset</h2>
<p class="hint">
Distance from <strong>camera centre</strong> to <strong>spindle centre</strong>
(positive X = spindle is to the +X of the camera).
</p>
<div class="row-2">
<label class="field">
<span>Offset X</span>
<input type="number" id="offset-x" step="0.001" value="0" />
</label>
<label class="field">
<span>Offset Y</span>
<input type="number" id="offset-y" step="0.001" value="0" />
</label>
</div>
</section>
<section class="controls">
<h2 class="section-title">Fiducial points</h2>
<p class="pos-line">
Live: <code id="live-pos">X — Y —</code>
<span id="conn-badge" class="badge"></span>
</p>
<div class="points-grid">
<div class="point-card">
<strong>Point 1 · Origin</strong>
<code id="pt1-display">not set</code>
<button type="button" id="btn-mark-1" class="btn primary small-btn">Mark Point 1</button>
</div>
<div class="point-card">
<strong>Point 2 · Axis</strong>
<code id="pt2-display">not set</code>
<button type="button" id="btn-mark-2" class="btn primary small-btn">Mark Point 2</button>
</div>
<div class="point-card">
<strong>Point 3 · Verify</strong>
<code id="pt3-display">not set</code>
<button type="button" id="btn-mark-3" class="btn primary small-btn">Mark Point 3</button>
</div>
</div>
<div class="btn-row">
<button type="button" id="btn-clear-points" class="btn">Clear points</button>
</div>
</section>
<section class="controls">
<h2 class="section-title">Alignment result</h2>
<pre id="align-result" class="result-box">Mark points 1 and 2 (3 optional) then Align.</pre>
<div class="btn-row">
<button type="button" id="btn-align" class="btn primary" disabled>
Align &amp; Zero Workspace
</button>
<button type="button" id="btn-rotate-gcode" class="btn" disabled>
Rotate loaded G-code
</button>
</div>
<p class="hint">
<strong>Align &amp; Zero</strong> computes θ from points 1→2, applies camera offset,
and issues <code>G10 L20</code> so spindle WCS origin matches fiducial 1.
<strong>Rotate loaded G-code</strong> rewrites X/Y/I/J by −θ and reloads the job
(FluidNC has no G68).
</p>
</section>
<section class="options">
<label class="field inline">
<span>Cross-hair colour</span>
<input type="color" id="crosshair-color" value="#00ff66" />
</label>
<label class="field inline">
<span>Thickness</span>
<input type="range" id="crosshair-thickness" min="1" max="4" step="1" value="2" />
</label>
</section>
<section class="debug-panel">
<div class="debug-header">
<strong>Debug log</strong>
<button type="button" id="btn-clear-log" class="btn small">Clear</button>
</div>
<pre id="debug-log" class="debug-log" aria-live="polite"></pre>
</section>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>