Add brush size slider and circle/square brush shapes

This commit is contained in:
Jared Miller 2026-02-07 21:02:43 -05:00
parent a746c6c0ee
commit ce49530ff4
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C

View file

@ -205,6 +205,20 @@
</div> </div>
</fieldset> </fieldset>
<fieldset>
<legend>Brush</legend>
<div class="form-group">
<label for="brushSize">Size:</label>
<input type="range" id="brushSize" min="1" max="8" value="1" style="width: 100px; vertical-align: middle;">
<span id="brushSizeValue">1</span>
</div>
<div class="form-group">
<label>Shape:</label>
<button id="brushCircle" style="width: 30px; height: 30px; margin-right: 0.25rem; border: 2px solid;"></button>
<button id="brushSquare" style="width: 30px; height: 30px; border: 2px solid;"></button>
</div>
</fieldset>
<fieldset> <fieldset>
<legend>View</legend> <legend>View</legend>
<div class="form-group"> <div class="form-group">
@ -290,6 +304,8 @@
let isDrawing = false; let isDrawing = false;
let drawButton = 0; let drawButton = 0;
let lastPaintedCell = null; let lastPaintedCell = null;
let brushSize = 1;
let brushShape = 'circle';
const canvas = document.getElementById('canvas'); const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true }); const ctx = canvas.getContext('2d', { willReadFrequently: true });
@ -425,6 +441,39 @@
renderCanvas(); renderCanvas();
} }
function paintBrush(x, y, symbol) {
if (brushSize === 1) {
paintCell(x, y, symbol);
return;
}
// Temporarily disable single-cell tracking for brush painting
const oldLastPainted = lastPaintedCell;
lastPaintedCell = null;
for (let dy = -(brushSize - 1); dy < brushSize; dy++) {
for (let dx = -(brushSize - 1); dx < brushSize; dx++) {
const nx = x + dx;
const ny = y + dy;
if (nx < 0 || nx >= gridWidth || ny < 0 || ny >= gridHeight) continue;
if (brushShape === 'circle') {
if (dx * dx + dy * dy < brushSize * brushSize) {
grid[ny][nx] = symbol;
}
} else { // square
if (Math.abs(dx) < brushSize && Math.abs(dy) < brushSize) {
grid[ny][nx] = symbol;
}
}
}
}
lastPaintedCell = `${x},${y}`;
renderCanvas();
}
function getGridCoords(event) { function getGridCoords(event) {
const rect = canvas.getBoundingClientRect(); const rect = canvas.getBoundingClientRect();
const canvasX = (event.clientX - rect.left) * (canvas.width / rect.width); const canvasX = (event.clientX - rect.left) * (canvas.width / rect.width);
@ -446,7 +495,7 @@
symbol = button === 0 ? symbols.m1 : symbols.m2; symbol = button === 0 ? symbols.m1 : symbols.m2;
} }
paintCell(x, y, symbol); paintBrush(x, y, symbol);
} }
function saveState() { function saveState() {
@ -457,6 +506,8 @@
symbols: symbols, symbols: symbols,
activeSlot: activeSlot, activeSlot: activeSlot,
palette: palette, palette: palette,
brushSize: brushSize,
brushShape: brushShape,
}; };
localStorage.setItem('map_editor_state', JSON.stringify(state)); localStorage.setItem('map_editor_state', JSON.stringify(state));
} }
@ -480,6 +531,8 @@
if (state.symbols) symbols = state.symbols; if (state.symbols) symbols = state.symbols;
if (state.activeSlot) activeSlot = state.activeSlot; if (state.activeSlot) activeSlot = state.activeSlot;
if (state.palette) palette = state.palette; if (state.palette) palette = state.palette;
if (state.brushSize !== undefined) brushSize = state.brushSize;
if (state.brushShape) brushShape = state.brushShape;
widthInput.value = gridWidth; widthInput.value = gridWidth;
heightInput.value = gridHeight; heightInput.value = gridHeight;
@ -487,6 +540,7 @@
m2Slot.textContent = symbols.m2; m2Slot.textContent = symbols.m2;
updateActiveSlot(); updateActiveSlot();
updateSymbolSlotColors(); updateSymbolSlotColors();
updateBrushUI();
sizeCanvas(); sizeCanvas();
renderCanvas(); renderCanvas();
renderPalette(); renderPalette();
@ -600,6 +654,24 @@ ${paletteStr}"""
m2Slot.style.color = `rgb(${c2[0]},${c2[1]},${c2[2]})`; m2Slot.style.color = `rgb(${c2[0]},${c2[1]},${c2[2]})`;
} }
function updateBrushUI() {
const slider = document.getElementById('brushSize');
const valueDisplay = document.getElementById('brushSizeValue');
const circleBtn = document.getElementById('brushCircle');
const squareBtn = document.getElementById('brushSquare');
slider.value = brushSize;
valueDisplay.textContent = brushSize;
if (brushShape === 'circle') {
circleBtn.style.borderWidth = '4px';
squareBtn.style.borderWidth = '2px';
} else {
circleBtn.style.borderWidth = '2px';
squareBtn.style.borderWidth = '4px';
}
}
// Palette manager functions // Palette manager functions
function loadPalettesFromStorage() { function loadPalettesFromStorage() {
const saved = localStorage.getItem('map_editor_palettes'); const saved = localStorage.getItem('map_editor_palettes');
@ -911,6 +983,25 @@ ${paletteStr}"""
}); });
}); });
// Brush controls
document.getElementById('brushSize').addEventListener('input', (e) => {
brushSize = parseInt(e.target.value);
document.getElementById('brushSizeValue').textContent = brushSize;
saveState();
});
document.getElementById('brushCircle').addEventListener('click', () => {
brushShape = 'circle';
updateBrushUI();
saveState();
});
document.getElementById('brushSquare').addEventListener('click', () => {
brushShape = 'square';
updateBrushUI();
saveState();
});
// Symbol slot selection // Symbol slot selection
m1Slot.addEventListener('click', () => { m1Slot.addEventListener('click', () => {
activeSlot = 'm1'; activeSlot = 'm1';
@ -1001,6 +1092,7 @@ ${paletteStr}"""
if (!loadState()) { if (!loadState()) {
renderPalette(); renderPalette();
updateSymbolSlotColors(); updateSymbolSlotColors();
updateBrushUI();
initGrid(100, 100, false); initGrid(100, 100, false);
} }
</script> </script>