Files
ShooterHub/frontend/group-size.html
2026-04-02 11:24:30 +02:00

201 lines
9.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Group Size Calculator ShooterHub</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="/css/app.css">
<style>
.upload-area {
border: 2px dashed #ced4da; border-radius: 10px;
padding: 3.5rem 2rem; text-align: center; color: #6c757d;
cursor: pointer; transition: border-color .2s, color .2s;
}
.upload-area:hover, .upload-area.drag-over { border-color: #0d6efd; color: #0d6efd; }
#annotCanvas { display: block; width: 100%; border-radius: 6px; border: 1px solid #dee2e6; }
.input-section { background: #f8f9fa; border-radius: 8px; padding: .85rem 1rem; margin-bottom: .6rem; }
.input-section h6 { margin-bottom: .6rem; font-size: .85rem; }
.legend-dot { display: inline-block; width: 11px; height: 11px; border-radius: 50%; vertical-align: middle; }
.result-row td:first-child { color: #6c757d; font-size: .85rem; width: 170px; }
.result-row td:last-child { font-weight: 500; font-size: .9rem; }
</style>
</head>
<body>
<div id="navbar"></div>
<div class="container-fluid py-3">
<div class="row g-3">
<!-- ══ Left: controls ═══════════════════════════════════════════════════ -->
<div class="col-md-4 col-xl-3">
<div id="backBtnWrap" class="d-none mb-2">
<a id="backBtn" href="/chrono.html" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>Back to analysis
</a>
</div>
<h5 class="fw-bold mb-1">
<i class="bi bi-crosshair2 me-2 text-primary"></i>Group Size Calculator
</h5>
<p class="text-muted small mb-3">Annotate a target photo to measure group size, mean radius, and correction.</p>
<!-- Unit switch -->
<div class="input-section">
<h6 class="fw-semibold mb-2"><i class="bi bi-rulers me-1"></i>Display units</h6>
<div class="btn-group btn-group-sm w-100" id="gsDistUnitBtns">
<button class="btn btn-outline-secondary" data-dist-unit="mm">mm</button>
<button class="btn btn-outline-secondary" data-dist-unit="moa">MOA</button>
<button class="btn btn-outline-secondary" data-dist-unit="mrad">MRAD</button>
</div>
</div>
<!-- Setup -->
<div class="input-section">
<h6 class="fw-semibold"><i class="bi bi-sliders me-1"></i>Setup</h6>
<div class="mb-2">
<label class="form-label form-label-sm mb-1">Distance (m)</label>
<input type="number" class="form-control form-control-sm" id="distanceM"
placeholder="e.g. 100" min="1" step="1">
<div class="form-text">Required for MOA values</div>
</div>
<div class="mb-2">
<label class="form-label form-label-sm mb-1">
Reference length (mm) <span class="text-danger">*</span>
</label>
<input type="number" class="form-control form-control-sm" id="refLength"
placeholder="e.g. 100" min="1" step="0.5">
<div class="form-text">Real-world length of the line you'll draw</div>
</div>
<div>
<label class="form-label form-label-sm mb-1">Bullet ⌀ (mm)</label>
<input type="number" class="form-control form-control-sm" id="bulletDia"
placeholder="e.g. 7.62" min="0" step="0.1">
<div class="form-text">Draws bullet holes at correct scale</div>
</div>
</div>
<!-- Annotation buttons -->
<div class="input-section">
<h6 class="fw-semibold"><i class="bi bi-cursor me-1"></i>Annotation</h6>
<div class="d-grid gap-2 mb-2">
<button class="btn btn-sm btn-primary w-100" id="btnRef" onclick="btnRef()">
<i class="bi bi-rulers me-1"></i>① Draw reference line
</button>
<button class="btn btn-sm btn-outline-secondary w-100" id="btnPoa" onclick="btnPoa()">
<i class="bi bi-crosshair me-1"></i>② Set POA
</button>
<button class="btn btn-sm btn-outline-secondary w-100" id="btnPoi" onclick="btnPoi()">
<i class="bi bi-plus-circle me-1"></i>③ Add POI (bullet holes)
</button>
</div>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-outline-secondary flex-fill" onclick="btnUndo()">
<i class="bi bi-arrow-counterclockwise"></i> Undo
</button>
<button class="btn btn-sm btn-outline-danger flex-fill" onclick="btnReset()">
<i class="bi bi-trash"></i> Reset
</button>
</div>
</div>
<!-- Legend -->
<div class="input-section">
<h6 class="fw-semibold"><i class="bi bi-palette me-1"></i>Legend</h6>
<div class="small vstack gap-1">
<div><span class="legend-dot me-2" style="background:#3b82f6"></span>Reference line</div>
<div><span class="legend-dot me-2" style="background:#22c55e"></span>POA — Point of Aim</div>
<div><span class="legend-dot me-2" style="background:#ef4444"></span>POI — Bullet holes</div>
<div><span class="legend-dot me-2" style="background:#f97316"></span>Group centroid</div>
</div>
</div>
<button class="btn btn-sm btn-outline-secondary w-100" onclick="loadNewPhoto()">
<i class="bi bi-image me-1"></i>Load different photo
</button>
</div>
<!-- ══ Right: canvas + results ══════════════════════════════════════════ -->
<div class="col-md-8 col-xl-9">
<!-- Upload section -->
<div id="uploadSection">
<div class="upload-area" id="dropArea">
<i class="bi bi-image fs-1 d-block mb-3 opacity-40"></i>
<p class="fw-semibold mb-1">Drop your target photo here</p>
<p class="small mb-0 text-muted">or click to browse — JPEG, PNG, WebP</p>
<input type="file" id="fileInput" accept="image/jpeg,image/png,image/webp" class="d-none">
</div>
<div id="uploadError" class="alert alert-danger mt-2 d-none small"></div>
</div>
<!-- Annotation section -->
<div id="annotSection" class="d-none">
<!-- Status + linked badge -->
<div class="d-flex align-items-center gap-2 mb-2 flex-wrap">
<span id="statusMsg" class="text-primary small fw-semibold"></span>
<span id="linkedBadge" class="badge bg-success d-none"></span>
</div>
<!-- Canvas -->
<div id="canvasWrap" class="mb-3">
<canvas id="annotCanvas"></canvas>
</div>
<!-- Results -->
<div class="card border-0 shadow-sm">
<div class="card-body">
<h6 class="fw-semibold mb-2"><i class="bi bi-bar-chart-line me-1 text-primary"></i>Results</h6>
<p id="resultsHint" class="text-muted small mb-0">Draw a reference line on the image.</p>
<div id="resultsSection" class="d-none">
<table class="table table-sm mb-2">
<tbody>
<tr class="result-row">
<td>Shots (POIs)</td>
<td id="resPOICount"></td>
</tr>
<tr class="result-row">
<td>Group size (ES)</td>
<td id="resGroupSize"></td>
</tr>
<tr class="result-row">
<td>Mean radius</td>
<td id="resMeanRadius"></td>
</tr>
<tr class="result-row">
<td><i class="bi bi-dot text-warning"></i> Group offset from POA</td>
<td id="resOffset"></td>
</tr>
<tr class="result-row">
<td><i class="bi bi-arrow-right text-danger"></i> Correction needed</td>
<td id="resCorrection"></td>
</tr>
</tbody>
</table>
<p id="resMoaNote" class="text-muted small d-none mb-2">
<i class="bi bi-info-circle me-1"></i>Enter the distance above to see MOA values.
</p>
<button id="saveBtn" class="btn btn-sm btn-outline-primary d-none" onclick="saveToApi()">
<i class="bi bi-floppy me-1"></i>Save POIs & compute server-side
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="toastContainer"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="/js/api.js"></script>
<script src="/js/utils.js"></script>
<script src="/js/i18n.js"></script>
<script src="/js/nav.js"></script>
<script src="/js/group-size.js"></script>
</body>
</html>