brickblock
This commit is contained in:
136
app.js
136
app.js
@@ -19,6 +19,11 @@ const floorTileSelect = document.getElementById("floor-tile");
|
||||
const fillRowInput = document.getElementById("fill-row");
|
||||
const fillRowTileSelect = document.getElementById("fill-row-tile");
|
||||
const fillRowButton = document.getElementById("fill-row-button");
|
||||
const brickStartInput = document.getElementById("brick-start");
|
||||
const brickEndInput = document.getElementById("brick-end");
|
||||
const brickTileSelect = document.getElementById("brick-tile");
|
||||
const brickStaggerInput = document.getElementById("brick-stagger");
|
||||
const brickApplyButton = document.getElementById("brick-apply");
|
||||
const applyGridButton = document.getElementById("apply-grid");
|
||||
const clearGridButton = document.getElementById("clear-grid");
|
||||
|
||||
@@ -148,11 +153,13 @@ const setActiveTile = (tileId) => {
|
||||
const renderFloorOptions = () => {
|
||||
floorTileSelect.innerHTML = "";
|
||||
fillRowTileSelect.innerHTML = "";
|
||||
brickTileSelect.innerHTML = "";
|
||||
const placeholder = document.createElement("option");
|
||||
placeholder.value = "";
|
||||
placeholder.textContent = "Select tile";
|
||||
floorTileSelect.append(placeholder);
|
||||
fillRowTileSelect.append(placeholder.cloneNode(true));
|
||||
brickTileSelect.append(placeholder.cloneNode(true));
|
||||
|
||||
tiles.forEach((tile) => {
|
||||
const option = document.createElement("option");
|
||||
@@ -160,6 +167,7 @@ const renderFloorOptions = () => {
|
||||
option.textContent = tile.name;
|
||||
floorTileSelect.append(option);
|
||||
fillRowTileSelect.append(option.cloneNode(true));
|
||||
brickTileSelect.append(option.cloneNode(true));
|
||||
});
|
||||
|
||||
if (floorTileId && !tiles.some((tile) => tile.id === floorTileId)) {
|
||||
@@ -167,6 +175,7 @@ const renderFloorOptions = () => {
|
||||
}
|
||||
floorTileSelect.value = floorTileId ?? "";
|
||||
fillRowTileSelect.value = activeTileId ?? "";
|
||||
brickTileSelect.value = activeTileId ?? "";
|
||||
};
|
||||
|
||||
const renderTiles = () => {
|
||||
@@ -274,18 +283,75 @@ const drawGridLines = () => {
|
||||
patternContext.strokeStyle = gridLineColor;
|
||||
patternContext.lineWidth = 1;
|
||||
|
||||
const lastRowIndex = config.rows - 1;
|
||||
const floorTile = floorTileId ? getTileById(floorTileId) : null;
|
||||
|
||||
for (let row = 0; row <= config.rows; row += 1) {
|
||||
patternContext.beginPath();
|
||||
patternContext.moveTo(0, row * config.tileSize + 0.5);
|
||||
patternContext.lineTo(patternCanvas.width, row * config.tileSize + 0.5);
|
||||
patternContext.stroke();
|
||||
if (row === 0 || row === config.rows) {
|
||||
patternContext.beginPath();
|
||||
patternContext.moveTo(0, row * config.tileSize + 0.5);
|
||||
patternContext.lineTo(patternCanvas.width, row * config.tileSize + 0.5);
|
||||
patternContext.stroke();
|
||||
continue;
|
||||
}
|
||||
|
||||
let segmentStart = 0;
|
||||
for (let col = 0; col < config.cols; col += 1) {
|
||||
const topId = grid[row - 1][col];
|
||||
const bottomId = grid[row][col];
|
||||
const shouldSkip = topId && bottomId && topId === bottomId;
|
||||
if (shouldSkip) {
|
||||
if (segmentStart < col) {
|
||||
patternContext.beginPath();
|
||||
patternContext.moveTo(segmentStart * config.tileSize, row * config.tileSize + 0.5);
|
||||
patternContext.lineTo(col * config.tileSize, row * config.tileSize + 0.5);
|
||||
patternContext.stroke();
|
||||
}
|
||||
segmentStart = col + 1;
|
||||
}
|
||||
}
|
||||
if (segmentStart < config.cols) {
|
||||
patternContext.beginPath();
|
||||
patternContext.moveTo(segmentStart * config.tileSize, row * config.tileSize + 0.5);
|
||||
patternContext.lineTo(patternCanvas.width, row * config.tileSize + 0.5);
|
||||
patternContext.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
for (let col = 0; col <= config.cols; col += 1) {
|
||||
patternContext.beginPath();
|
||||
patternContext.moveTo(col * config.tileSize + 0.5, 0);
|
||||
patternContext.lineTo(col * config.tileSize + 0.5, patternCanvas.height);
|
||||
patternContext.stroke();
|
||||
if (col === 0 || col === config.cols) {
|
||||
patternContext.beginPath();
|
||||
patternContext.moveTo(col * config.tileSize + 0.5, 0);
|
||||
patternContext.lineTo(col * config.tileSize + 0.5, patternCanvas.height);
|
||||
patternContext.stroke();
|
||||
continue;
|
||||
}
|
||||
|
||||
let segmentStart = 0;
|
||||
for (let row = 0; row < config.rows; row += 1) {
|
||||
const leftId = grid[row][col - 1];
|
||||
const rightId = grid[row][col];
|
||||
const isFloorRow = floorEnabled && row === lastRowIndex;
|
||||
const floorSkip =
|
||||
isFloorRow && floorTile && floorTile.width > 1 && col % floorTile.width !== 0;
|
||||
const shouldSkip = (leftId && rightId && leftId === rightId) || floorSkip;
|
||||
|
||||
if (shouldSkip) {
|
||||
if (segmentStart < row) {
|
||||
patternContext.beginPath();
|
||||
patternContext.moveTo(col * config.tileSize + 0.5, segmentStart * config.tileSize);
|
||||
patternContext.lineTo(col * config.tileSize + 0.5, row * config.tileSize);
|
||||
patternContext.stroke();
|
||||
}
|
||||
segmentStart = row + 1;
|
||||
}
|
||||
}
|
||||
if (segmentStart < config.rows) {
|
||||
patternContext.beginPath();
|
||||
patternContext.moveTo(col * config.tileSize + 0.5, segmentStart * config.tileSize);
|
||||
patternContext.lineTo(col * config.tileSize + 0.5, patternCanvas.height);
|
||||
patternContext.stroke();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -430,6 +496,41 @@ const fillRowWithTile = (rowIndex, tileId) => {
|
||||
syncPatternPreview();
|
||||
};
|
||||
|
||||
const fillBrickRows = (startRow, endRow, tileId, stagger) => {
|
||||
const tile = getTileById(tileId);
|
||||
if (!tile) {
|
||||
return;
|
||||
}
|
||||
const maxRow = floorEnabled ? config.rows - 2 : config.rows - 1;
|
||||
const start = clampNumber(startRow, 0, maxRow);
|
||||
const end = clampNumber(endRow, start, maxRow);
|
||||
const tileWidth = clampNumber(tile.width, 1, config.cols);
|
||||
|
||||
for (let row = start; row <= end; row += 1) {
|
||||
const offset = stagger && (row - start) % 2 === 1 ? Math.floor(tileWidth / 2) : 0;
|
||||
const toRemove = new Set();
|
||||
for (let col = 0; col < config.cols; col += 1) {
|
||||
const placementId = grid[row][col];
|
||||
if (placementId) {
|
||||
toRemove.add(placementId);
|
||||
}
|
||||
}
|
||||
toRemove.forEach((placementId) => clearPlacement(placementId));
|
||||
|
||||
let col = 0;
|
||||
if (offset > 0) {
|
||||
addPlacement(row, 0, tile.id, clampNumber(offset, 1, config.cols), 1);
|
||||
col = offset;
|
||||
}
|
||||
for (; col < config.cols; col += tileWidth) {
|
||||
const remaining = config.cols - col;
|
||||
addPlacement(row, col, tile.id, clampNumber(tileWidth, 1, remaining), 1);
|
||||
}
|
||||
}
|
||||
renderCanvas();
|
||||
syncPatternPreview();
|
||||
};
|
||||
|
||||
const paintArea = (row, col, tileId, width = 1, height = 1) => {
|
||||
const maxRow = floorEnabled ? config.rows - 2 : config.rows - 1;
|
||||
if (row > maxRow || col >= config.cols) {
|
||||
@@ -516,6 +617,9 @@ applyGridButton.addEventListener("click", () => {
|
||||
gridRowsInput.value = config.rows;
|
||||
gridColsInput.value = config.cols;
|
||||
gridSizeInput.value = config.tileSize;
|
||||
fillRowInput.max = String(config.rows);
|
||||
brickStartInput.max = String(config.rows);
|
||||
brickEndInput.max = String(config.rows);
|
||||
initGrid();
|
||||
renderCanvas();
|
||||
syncPatternPreview();
|
||||
@@ -550,6 +654,16 @@ fillRowButton.addEventListener("click", () => {
|
||||
fillRowWithTile(rowIndex, tileId);
|
||||
});
|
||||
|
||||
brickApplyButton.addEventListener("click", () => {
|
||||
const startRow = Number(brickStartInput.value) - 1;
|
||||
const endRow = Number(brickEndInput.value) - 1;
|
||||
const tileId = brickTileSelect.value || activeTileId;
|
||||
if (!tileId) {
|
||||
return;
|
||||
}
|
||||
fillBrickRows(startRow, endRow, tileId, brickStaggerInput.checked);
|
||||
});
|
||||
|
||||
const readFileAsDataUrl = (file) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
@@ -744,6 +858,9 @@ importJsonButton.addEventListener("click", () => {
|
||||
gridSizeInput.value = config.tileSize;
|
||||
floorEnabledInput.checked = floorEnabled;
|
||||
fillRowInput.value = "1";
|
||||
fillRowInput.max = String(config.rows);
|
||||
brickStartInput.max = String(config.rows);
|
||||
brickEndInput.max = String(config.rows);
|
||||
|
||||
tiles = normalizeTiles(payload.tiles);
|
||||
|
||||
@@ -824,3 +941,6 @@ loadLibraryFromStorage();
|
||||
renderTiles();
|
||||
renderCanvas();
|
||||
syncPatternPreview();
|
||||
fillRowInput.max = String(config.rows);
|
||||
brickStartInput.max = String(config.rows);
|
||||
brickEndInput.max = String(config.rows);
|
||||
|
||||
Reference in New Issue
Block a user