diff --git a/inventory/routes/testing.py b/inventory/routes/testing.py index 2510f71..2823c82 100644 --- a/inventory/routes/testing.py +++ b/inventory/routes/testing.py @@ -7,6 +7,6 @@ bp_testing = Blueprint("testing", __name__) def init_testing_routes(app): @bp_testing.get('/testing') def test_page(): - return render_template('testing.html') + return render_template('testing.html', grid_size=25) app.register_blueprint(bp_testing) \ No newline at end of file diff --git a/inventory/templates/testing.html b/inventory/templates/testing.html index 3b7ae8b..269eb16 100644 --- a/inventory/templates/testing.html +++ b/inventory/templates/testing.html @@ -1,21 +1,241 @@ {% extends 'base.html' %} +{% set dot_size = [grid_size * 1.25, 32]|max|int %} {% block style %} -#outer { - border-style: dashed !important; - display: grid; - height: 85vh; +:root { + --grid: {{ grid_size }}px; } -.cell { - border-style: dashed !important; +#grid { + background-image: + linear-gradient(to right, #ccc 1px, transparent 1px), + linear-gradient(to bottom, #ccc 1px, transparent 1px); + background-size: var(--grid) var(--grid); + cursor: none; + height: calc(round(nearest, 80vh, var(--grid)) + 1px); + width: calc(round(nearest, 100%, var(--grid)) + 1px); +} + +#toolBar { + top: 10px; + transform: translateX(-50%); + z-index: 10000; +} + +#coords { + bottom: 10px; + pointer-events: none; + left: 10px; +} + +#overlay { + z-index: 9999; + pointer-events: none; + inset: 0; } {% endblock %} {% block main %} -
+
+
+
+ +
+
+ + + + + +
+
+ + + + + +
+ +
{% endblock %} {% block script %} -{% endblock %} + const canvasEl = document.getElementById('overlay'); + const coordsEl = document.getElementById('coords'); + const dotEl = document.getElementById('dot'); + const dotSVGEl = document.getElementById('dotSVG'); + const gridEl = document.getElementById('grid'); + + let ctx; + let selectedColor = '#000000'; + + let currentRect = null; + let rects = []; + + resizeAndSetupCanvas(); + window.addEventListener('resize', resizeAndSetupCanvas); + + function snapToGrid(x, y) { + const rect = canvasEl.getBoundingClientRect(); + const clampedX = Math.min(Math.max(x, rect.left), rect.right); + const clampedY = Math.min(Math.max(y, rect.top), rect.bottom); + + const localX = clampedX - rect.left; + const localY = clampedY - rect.top; + + const ix = Math.round(localX / {{ grid_size }}); + const iy = Math.round(localY / {{ grid_size }}); + return { + ix, + iy, + x: ix * {{ grid_size }}, + y: iy * {{ grid_size }} + }; + } + + function normalizeRect(rect) { + return { + x: Math.min(rect.x1, rect.x2), + y: Math.min(rect.y1, rect.y2), + w: Math.abs(rect.x2 - rect.x1), + h: Math.abs(rect.y2 - rect.y1), + color: rect.color + }; + } + + function resizeAndSetupCanvas() { + const dpr = window.devicePixelRatio || 1; + const rect = canvasEl.getBoundingClientRect(); + + canvasEl.width = rect.width * dpr; + canvasEl.height = rect.height * dpr; + + ctx = canvasEl.getContext('2d'); + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + + redrawAll(); + } + + function redrawAll() { + if (!ctx) return; + + clearCanvas(); + rects.forEach(drawRect); + } + + function drawRect(rect) { + ctx.strokeStyle = rect.color; + ctx.strokeRect(rect.x, rect.y, rect.w, rect.h); + if (rect.fill) { + ctx.globalAlpha = 0.15; + ctx.fillStyle = rect.color; + ctx.fillRect(rect.x, rect.y, rect.w, rect.h); + ctx.globalAlpha = 1; + } + } + + function clearCanvas() { + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.clearRect(0, 0, canvasEl.width, canvasEl.height); + const dpr = window.devicePixelRatio || 1; + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + } + + window.setColor = function setColor() { + selectedColor = document.getElementById('color').value; + } + + document.addEventListener('keydown', (e) => { + if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'z') { + rects.pop(); + redrawAll(); + } + }); + + gridEl.addEventListener('mousemove', (e) => { + const { ix, iy, x: snapX, y: snapY } = snapToGrid(e.clientX, e.clientY); + + coordsEl.innerText = `(${ix}, ${iy})`; + + const renderX = snapX - {{ dot_size }} / 2; + const renderY = snapY - {{ dot_size }} / 2; + + coordsEl.classList.remove('d-none'); + + dotEl.classList.remove('d-none'); + dotEl.style.top = `${renderY}px`; + dotEl.style.left = `${renderX}px`; + + if (currentRect) { + const previewRect = normalizeRect({ + ...currentRect, + x2: snapX, + y2: snapY + }); + + clearCanvas(); + rects.forEach(drawRect); + ctx.setLineDash([5, 3]); + drawRect(previewRect); + ctx.setLineDash([]); + } + }); + + gridEl.addEventListener('mouseleave', (e) => { + coordsEl.classList.add('d-none'); + dotEl.classList.add('d-none'); + }); + + gridEl.addEventListener('mousedown', (e) => { + if (e.button !== 0) return; + + e.preventDefault(); + + if (e.target.closest('#toolBar')) return; + + const {ix, iy, x: snapX, y: snapY} = snapToGrid(e.clientX, e.clientY); + + currentRect = { + x1: snapX, + y1: snapY, + x2: snapX, + y2: snapY, + color: selectedColor + }; + }); + + gridEl.addEventListener('mouseup', (e) => { + if (!currentRect) return; + + const {ix, iy, x: snapX, y: snapY } = snapToGrid(e.clientX, e.clientY); + + currentRect.x2 = snapX; + currentRect.y2 = snapY; + + const finalRect = normalizeRect(currentRect); + + if (finalRect.w > 0 && finalRect.h > 0) { + rects.push(finalRect); + } + clearCanvas(); + rects.forEach(drawRect); + + currentRect = null; + }); +{% endblock %} \ No newline at end of file