Bug fixes!

This commit is contained in:
Yaro Kasear 2025-12-12 09:40:05 -06:00
parent 8abf9bdcdf
commit 4fe3dfb8b4

View file

@ -71,6 +71,24 @@
</svg> </svg>
</label> </label>
<input type="radio" class="btn-check" id="outlineEllipse" name="tool">
<label for="outlineEllipse"
class="btn btn-sm btn-light border d-inline-flex align-items-center justify-content-center">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-circle" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16" />
</svg>
</label>
<input type="radio" class="btn-check" id="filledEllipse" name="tool">
<label for="filledEllipse"
class="btn btn-sm btn-light border d-inline-flex align-items-center justify-content-center">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-circle-fill" viewBox="0 0 16 16">
<circle cx="8" cy="8" r="8" />
</svg>
</label>
<input type="radio" class="btn-check" id="line" name="tool"> <input type="radio" class="btn-check" id="line" name="tool">
<label for="line" <label for="line"
class="btn btn-sm btn-light border d-inline-flex align-items-center justify-content-center"> class="btn btn-sm btn-light border d-inline-flex align-items-center justify-content-center">
@ -198,6 +216,13 @@ window.addEventListener('resize', resizeAndSetupCanvas);
setGrid(); 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() { function getActiveTool() {
const checked = document.querySelector('input[name="tool"]:checked'); const checked = document.querySelector('input[name="tool"]:checked');
return checked ? checked.id : 'outline'; return checked ? checked.id : 'outline';
@ -260,40 +285,67 @@ function snapToGrid(x, y) {
}; };
} }
function normalizeRect(shape) { function normRange(a1, a2, mode, grid) {
const ix1 = Math.round(shape.x1 / {{ grid_size }}); if (mode === 'grid') {
const iy1 = Math.round(shape.y1 / {{ grid_size }}); const i1 = Math.round(a1 / grid);
const ix2 = Math.round(shape.x2 / {{ grid_size }}); const i2 = Math.round(a2 / grid);
const iy2 = Math.round(shape.y2 / {{ grid_size }}); 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); function normalizeRect(shape) {
const iy = Math.min(iy1, iy2); const grid = {{ grid_size }};
const iw = Math.abs(ix2 - ix1); const type = getActiveType();
const ih = Math.abs(iy2 - iy1);
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 { return {
type: 'rect', type: 'rect',
ix, modeX, modeY,
iy, x, y, w, h,
iw,
ih,
color: shape.color, color: shape.color,
fill: shape.fill fill: shape.fill
}; };
} }
function normalizeEllipse(shape) {
const r = normalizeRect(shape);
return { ...r, type: 'ellipse' };
}
function normalizeLine(shape) { function normalizeLine(shape) {
const ix1 = shape.x1 / {{ grid_size }}; const grid = {{ grid_size }};
const iy1 = shape.y1 / {{ grid_size }}; const type = getActiveType();
const ix2 = shape.x2 / {{ grid_size }};
const iy2 = shape.y2 / {{ grid_size }}; const modeX = axisMode(type, 'x');
const modeY = axisMode(type, 'y');
function normPoint(v, mode) {
return mode === 'grid' ? (v / grid) : v;
}
return { return {
type: 'line', type: 'line',
ix1, modeX, modeY,
iy1, x1: normPoint(shape.x1, modeX),
ix2, y1: normPoint(shape.y1, modeY),
iy2, x2: normPoint(shape.x2, modeX),
y2: normPoint(shape.y2, modeY),
color: shape.color color: shape.color
}; };
} }
@ -327,28 +379,52 @@ function redrawAll() {
function drawShape(shape) { function drawShape(shape) {
if (!ctx) return; 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.save();
ctx.strokeStyle = shape.color || '#000000'; ctx.strokeStyle = shape.color || '#000000';
if (shape.type === 'rect') { if (shape.type === 'rect' || shape.type === 'ellipse') {
const x = shape.ix * {{ grid_size }}; const x = toPxX(shape.x);
const y = shape.iy * {{ grid_size }}; const y = toPxY(shape.y);
const w = shape.iw * {{ grid_size }}; const w = toPxX(shape.w);
const h = shape.ih * {{ grid_size }}; const h = toPxY(shape.h);
if (shape.type === 'rect') {
ctx.strokeRect(x, y, w, h); 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) { if (shape.fill) {
ctx.globalAlpha = 0.15; ctx.globalAlpha = 0.15;
ctx.fillStyle = shape.color; ctx.fillStyle = shape.color;
if (shape.type === 'rect') {
ctx.fillRect(x, y, w, h); 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; ctx.globalAlpha = 1;
} }
} else if (shape.type === 'line') { } else if (shape.type === 'line') {
const x1 = shape.ix1 * {{ grid_size }}; const x1 = toPxX(shape.x1);
const y1 = shape.iy1 * {{ grid_size }}; const y1 = toPxY(shape.y1);
const x2 = shape.ix2 * {{ grid_size }}; const x2 = toPxX(shape.x2);
const y2 = shape.iy2 * {{ grid_size }}; const y2 = toPxY(shape.y2);
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(x1, y1); ctx.moveTo(x1, y1);
@ -546,13 +622,20 @@ gridEl.addEventListener('pointermove', (e) => {
color: currentShape.color color: currentShape.color
}); });
drawShape(previewLine); drawShape(previewLine);
} else { } else if (tool === 'filled' || tool === 'outline') {
const previewRect = normalizeRect({ const previewRect = normalizeRect({
...currentShape, ...currentShape,
x2: snapX, x2: snapX,
y2: snapY y2: snapY
}); });
drawShape(previewRect); drawShape(previewRect);
} else if (tool === 'filledEllipse' || tool === 'outlineEllipse') {
const previewEllipse = normalizeEllipse({
...currentShape,
x2: snapX,
y2: snapY
});
drawShape(previewEllipse);
} }
ctx.setLineDash([]); ctx.setLineDash([]);
@ -586,7 +669,7 @@ gridEl.addEventListener('pointerdown', (e) => {
y2: snapY, y2: snapY,
color: selectedColor color: selectedColor
}; };
} else { } else if (tool === 'outline' || tool === 'filled') {
currentShape = { currentShape = {
tool, tool,
x1: snapX, x1: snapX,
@ -596,6 +679,16 @@ gridEl.addEventListener('pointerdown', (e) => {
color: selectedColor, color: selectedColor,
fill: document.getElementById('filled').checked 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') { if (currentShape.tool === 'line') {
const line = normalizeLine(currentShape); const line = normalizeLine(currentShape);
if (line.ix1 !== line.ix2 || line.iy1 !== line.iy2) { if (line.x1 !== line.x2 || line.y1 !== line.y2) {
finalShape = line; finalShape = line;
} }
} else { } else if (currentShape.tool === 'filled' || currentShape.tool === 'outline') {
const rect = normalizeRect(currentShape); const rect = normalizeRect(currentShape);
if (rect.iw > 0 && rect.ih > 0) { if (rect.w > 0 && rect.h > 0) {
finalShape = rect; 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) { if (finalShape) {