diff --git a/inventory/templates/testing.html b/inventory/templates/testing.html index 05f8593..5581c2f 100644 --- a/inventory/templates/testing.html +++ b/inventory/templates/testing.html @@ -71,6 +71,24 @@ + + + + + + + + + + + + + + @@ -198,6 +216,13 @@ window.addEventListener('resize', resizeAndSetupCanvas); setGrid(); +function axisMode(type, axis) { + if (type === 'fullGrid') return 'grid'; + if (type === 'verticalGrid') return axis === 'x' ? 'grid' : 'px'; + if (type === 'horizontalGrid') return axis === 'y' ? 'grid' : 'px'; + return 'px'; // noGrid +} + function getActiveTool() { const checked = document.querySelector('input[name="tool"]:checked'); return checked ? checked.id : 'outline'; @@ -260,40 +285,67 @@ function snapToGrid(x, y) { }; } -function normalizeRect(shape) { - const ix1 = Math.round(shape.x1 / {{ grid_size }}); - const iy1 = Math.round(shape.y1 / {{ grid_size }}); - const ix2 = Math.round(shape.x2 / {{ grid_size }}); - const iy2 = Math.round(shape.y2 / {{ grid_size }}); +function normRange(a1, a2, mode, grid) { + if (mode === 'grid') { + const i1 = Math.round(a1 / grid); + const i2 = Math.round(a2 / grid); + const start = Math.min(i1, i2); + const size = Math.abs(i2 - i1); + return { start, size }; + } else { + const start = Math.min(a1, a2); + const size = Math.abs(a2 - a1); + return { start, size }; + } +} - const ix = Math.min(ix1, ix2); - const iy = Math.min(iy1, iy2); - const iw = Math.abs(ix2 - ix1); - const ih = Math.abs(iy2 - iy1); +function normalizeRect(shape) { + const grid = {{ grid_size }}; + const type = getActiveType(); + + const modeX = axisMode(type, 'x'); + const modeY = axisMode(type, 'y'); + + const xr = normRange(shape.x1, shape.x2, modeX, grid); + const yr = normRange(shape.y1, shape.y2, modeY, grid); + + const x = (modeX === 'grid') ? xr.start : xr.start; + const w = (modeX === 'grid') ? xr.size : xr.size; + const y = (modeX === 'grid') ? yr.start : yr.start; + const h = (modeX === 'grid') ? yr.size : yr.size; return { type: 'rect', - ix, - iy, - iw, - ih, + modeX, modeY, + x, y, w, h, color: shape.color, fill: shape.fill }; } +function normalizeEllipse(shape) { + const r = normalizeRect(shape); + return { ...r, type: 'ellipse' }; +} + function normalizeLine(shape) { - const ix1 = shape.x1 / {{ grid_size }}; - const iy1 = shape.y1 / {{ grid_size }}; - const ix2 = shape.x2 / {{ grid_size }}; - const iy2 = shape.y2 / {{ grid_size }}; + const grid = {{ grid_size }}; + const type = getActiveType(); + + const modeX = axisMode(type, 'x'); + const modeY = axisMode(type, 'y'); + + function normPoint(v, mode) { + return mode === 'grid' ? (v / grid) : v; + } return { type: 'line', - ix1, - iy1, - ix2, - iy2, + modeX, modeY, + x1: normPoint(shape.x1, modeX), + y1: normPoint(shape.y1, modeY), + x2: normPoint(shape.x2, modeX), + y2: normPoint(shape.y2, modeY), color: shape.color }; } @@ -327,28 +379,52 @@ function redrawAll() { function drawShape(shape) { if (!ctx) return; + const grid = {{ grid_size }}; + const modeX = shape.modeX || 'grid'; + const modeY = shape.modeY || 'grid'; + + const toPxX = (v) => modeX === 'grid' ? v * grid : v; + const toPxY = (v) => modeY === 'grid' ? v * grid : v; + ctx.save(); ctx.strokeStyle = shape.color || '#000000'; - if (shape.type === 'rect') { - const x = shape.ix * {{ grid_size }}; - const y = shape.iy * {{ grid_size }}; - const w = shape.iw * {{ grid_size }}; - const h = shape.ih * {{ grid_size }}; + if (shape.type === 'rect' || shape.type === 'ellipse') { + const x = toPxX(shape.x); + const y = toPxY(shape.y); + const w = toPxX(shape.w); + const h = toPxY(shape.h); - ctx.strokeRect(x, y, w, h); + if (shape.type === 'rect') { + ctx.strokeRect(x, y, w, h); + } else { + const cx = x + w / 2; + const cy = y + h / 2; + ctx.beginPath(); + ctx.ellipse(cx, cy, Math.abs(w / 2), Math.abs(h / 2), 0, 0, Math.PI * 2); + ctx.stroke(); + } if (shape.fill) { ctx.globalAlpha = 0.15; ctx.fillStyle = shape.color; - ctx.fillRect(x, y, w, h); + if (shape.type === 'rect') { + ctx.fillRect(x, y, w, h); + } else { + const cx = x + w / 2; + const cy = y + h / 2; + ctx.beginPath(); + ctx.ellipse(cx, cy, Math.abs(w / 2), Math.abs(h / 2), 0, 0, Math.PI * 2); + ctx.fill() + } ctx.globalAlpha = 1; } + } else if (shape.type === 'line') { - const x1 = shape.ix1 * {{ grid_size }}; - const y1 = shape.iy1 * {{ grid_size }}; - const x2 = shape.ix2 * {{ grid_size }}; - const y2 = shape.iy2 * {{ grid_size }}; + const x1 = toPxX(shape.x1); + const y1 = toPxY(shape.y1); + const x2 = toPxX(shape.x2); + const y2 = toPxY(shape.y2); ctx.beginPath(); ctx.moveTo(x1, y1); @@ -546,13 +622,20 @@ gridEl.addEventListener('pointermove', (e) => { color: currentShape.color }); drawShape(previewLine); - } else { + } else if (tool === 'filled' || tool === 'outline') { const previewRect = normalizeRect({ ...currentShape, x2: snapX, y2: snapY }); drawShape(previewRect); + } else if (tool === 'filledEllipse' || tool === 'outlineEllipse') { + const previewEllipse = normalizeEllipse({ + ...currentShape, + x2: snapX, + y2: snapY + }); + drawShape(previewEllipse); } ctx.setLineDash([]); @@ -586,7 +669,7 @@ gridEl.addEventListener('pointerdown', (e) => { y2: snapY, color: selectedColor }; - } else { + } else if (tool === 'outline' || tool === 'filled') { currentShape = { tool, x1: snapX, @@ -596,6 +679,16 @@ gridEl.addEventListener('pointerdown', (e) => { color: selectedColor, fill: document.getElementById('filled').checked }; + } else if (tool === 'outlineEllipse' || tool === 'filledEllipse') { + currentShape = { + tool, + x1: snapX, + y1: snapY, + x2: snapX, + y2: snapY, + color: selectedColor, + fill: (tool === 'filledEllipse') + }; } }); @@ -616,15 +709,18 @@ window.addEventListener('pointerup', (e) => { if (currentShape.tool === 'line') { const line = normalizeLine(currentShape); - if (line.ix1 !== line.ix2 || line.iy1 !== line.iy2) { + if (line.x1 !== line.x2 || line.y1 !== line.y2) { finalShape = line; } - } else { + } else if (currentShape.tool === 'filled' || currentShape.tool === 'outline') { const rect = normalizeRect(currentShape); - if (rect.iw > 0 && rect.ih > 0) { + if (rect.w > 0 && rect.h > 0) { finalShape = rect; } + } else if (currentShape.tool === 'filledEllipse' || currentShape.tool === 'outlineEllipse') { + const ellipse = normalizeEllipse(currentShape); + if (ellipse.w > 0 && ellipse.h > 0) finalShape = ellipse; } if (finalShape) {