fix: holes in pencil tool
This commit is contained in:
parent
7233452877
commit
214fed6e94
3 changed files with 60 additions and 18 deletions
|
|
@ -188,6 +188,8 @@ function Canvas() {
|
||||||
setDragging(false);
|
setDragging(false);
|
||||||
updateCssCursor();
|
updateCssCursor();
|
||||||
|
|
||||||
|
pencilTool.stop();
|
||||||
|
|
||||||
// History entries for pencil and eraser
|
// History entries for pencil and eraser
|
||||||
if (tool === "pencil" || tool === "eraser") {
|
if (tool === "pencil" || tool === "eraser") {
|
||||||
// startBlocksRef will mutate if we pass it directly
|
// startBlocksRef will mutate if we pass it directly
|
||||||
|
|
@ -210,7 +212,7 @@ function Canvas() {
|
||||||
() => setSelectionCoords([...prevSelection])
|
() => setSelectionCoords([...prevSelection])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [updateCssCursor, blocks, tool, addHistory, setBlocks, selectionCoords, setSelectionCoords]);
|
}, [updateCssCursor, pencilTool, blocks, tool, addHistory, setBlocks, selectionCoords, setSelectionCoords]);
|
||||||
|
|
||||||
const onWheel = useCallback(
|
const onWheel = useCallback(
|
||||||
(e: React.WheelEvent) => {
|
(e: React.WheelEvent) => {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { useContext } from "react";
|
import { useContext, useRef } from "react";
|
||||||
|
|
||||||
import { CanvasContext } from "@/context/Canvas";
|
import { CanvasContext } from "@/context/Canvas";
|
||||||
import { SelectionContext } from "@/context/Selection";
|
import { SelectionContext } from "@/context/Selection";
|
||||||
import { ToolContext } from "@/context/Tool";
|
import { ToolContext } from "@/context/Tool";
|
||||||
|
|
||||||
import { useRadiusPosition } from "../useRadiusPosition";
|
import { useRadiusPosition } from "@/hooks/useRadiusPosition";
|
||||||
|
import { interpolate } from "@/utils/interpolate";
|
||||||
|
|
||||||
export function usePencilTool(mouseCoords: Position) {
|
export function usePencilTool(mouseCoords: Position) {
|
||||||
const { blocks, setBlocks } = useContext(CanvasContext);
|
const { blocks, setBlocks } = useContext(CanvasContext);
|
||||||
|
|
@ -12,33 +13,52 @@ export function usePencilTool(mouseCoords: Position) {
|
||||||
const { selectedBlock, radius } = useContext(ToolContext);
|
const { selectedBlock, radius } = useContext(ToolContext);
|
||||||
|
|
||||||
const radiusPosition = useRadiusPosition(mouseCoords);
|
const radiusPosition = useRadiusPosition(mouseCoords);
|
||||||
|
const lastPosition = useRef<Position | null>(null);
|
||||||
|
|
||||||
|
const stop = () => {
|
||||||
|
lastPosition.current = null;
|
||||||
|
};
|
||||||
|
|
||||||
const use = () => {
|
const use = () => {
|
||||||
|
// Don't allow the user to add air blocks
|
||||||
if (selectedBlock == "air") return;
|
if (selectedBlock == "air") return;
|
||||||
const radiusBlocks: Block[] = [];
|
const newBlocks: Block[] = [];
|
||||||
|
|
||||||
|
const addBlock = (x: number, y: number) => {
|
||||||
|
if (isInSelection(x, y)) {
|
||||||
|
newBlocks.push({
|
||||||
|
name: selectedBlock,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interpolate to remove holes
|
||||||
|
if (lastPosition.current) {
|
||||||
|
const interpolatedPositions = interpolate(radius, lastPosition.current, radiusPosition);
|
||||||
|
if (!interpolatedPositions) return;
|
||||||
|
|
||||||
|
interpolatedPositions.forEach(({ x, y }) => addBlock(x, y));
|
||||||
|
} else {
|
||||||
for (let x = 0; x < radius; x++) {
|
for (let x = 0; x < radius; x++) {
|
||||||
for (let y = 0; y < radius; y++) {
|
for (let y = 0; y < radius; y++) {
|
||||||
const tileX = radiusPosition.x + x;
|
const tileX = radiusPosition.x + x;
|
||||||
const tileY = radiusPosition.y + y;
|
const tileY = radiusPosition.y + y;
|
||||||
|
|
||||||
// Only add blocks within the selection
|
addBlock(tileX, tileY);
|
||||||
if (isInSelection(tileX, tileY)) {
|
|
||||||
radiusBlocks.push({
|
|
||||||
name: selectedBlock,
|
|
||||||
x: tileX,
|
|
||||||
y: tileY,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove duplicates
|
||||||
const mergedBlocks = blocks.filter((block) => {
|
const mergedBlocks = blocks.filter((block) => {
|
||||||
return !radiusBlocks.some((newBlock) => block.x === newBlock.x && block.y === newBlock.y);
|
return !newBlocks.some((b) => block.x === b.x && block.y === b.y);
|
||||||
});
|
});
|
||||||
|
|
||||||
setBlocks([...mergedBlocks, ...radiusBlocks]);
|
setBlocks([...mergedBlocks, ...newBlocks]);
|
||||||
|
lastPosition.current = radiusPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
return { use };
|
return { stop, use };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
src/utils/interpolate.ts
Normal file
20
src/utils/interpolate.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
export function interpolate(radius: number, position0: Position, position1: Position): Position[] {
|
||||||
|
const dx = position1.x - position0.x;
|
||||||
|
const dy = position1.y - position0.y;
|
||||||
|
const steps = Math.max(Math.abs(dx), Math.abs(dy));
|
||||||
|
|
||||||
|
const positions: Position[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i <= steps; i++) {
|
||||||
|
const x = Math.round(position0.x + (dx * i) / steps);
|
||||||
|
const y = Math.round(position0.y + (dy * i) / steps);
|
||||||
|
|
||||||
|
for (let rx = 0; rx < radius; rx++) {
|
||||||
|
for (let ry = 0; ry < radius; ry++) {
|
||||||
|
positions.push({ x: x + rx, y: y + ry });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue