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);
|
||||
updateCssCursor();
|
||||
|
||||
pencilTool.stop();
|
||||
|
||||
// History entries for pencil and eraser
|
||||
if (tool === "pencil" || tool === "eraser") {
|
||||
// startBlocksRef will mutate if we pass it directly
|
||||
|
|
@ -210,7 +212,7 @@ function Canvas() {
|
|||
() => setSelectionCoords([...prevSelection])
|
||||
);
|
||||
}
|
||||
}, [updateCssCursor, blocks, tool, addHistory, setBlocks, selectionCoords, setSelectionCoords]);
|
||||
}, [updateCssCursor, pencilTool, blocks, tool, addHistory, setBlocks, selectionCoords, setSelectionCoords]);
|
||||
|
||||
const onWheel = useCallback(
|
||||
(e: React.WheelEvent) => {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { useContext } from "react";
|
||||
import { useContext, useRef } from "react";
|
||||
|
||||
import { CanvasContext } from "@/context/Canvas";
|
||||
import { SelectionContext } from "@/context/Selection";
|
||||
import { ToolContext } from "@/context/Tool";
|
||||
|
||||
import { useRadiusPosition } from "../useRadiusPosition";
|
||||
import { useRadiusPosition } from "@/hooks/useRadiusPosition";
|
||||
import { interpolate } from "@/utils/interpolate";
|
||||
|
||||
export function usePencilTool(mouseCoords: Position) {
|
||||
const { blocks, setBlocks } = useContext(CanvasContext);
|
||||
|
|
@ -12,33 +13,52 @@ export function usePencilTool(mouseCoords: Position) {
|
|||
const { selectedBlock, radius } = useContext(ToolContext);
|
||||
|
||||
const radiusPosition = useRadiusPosition(mouseCoords);
|
||||
const lastPosition = useRef<Position | null>(null);
|
||||
|
||||
const stop = () => {
|
||||
lastPosition.current = null;
|
||||
};
|
||||
|
||||
const use = () => {
|
||||
// Don't allow the user to add air blocks
|
||||
if (selectedBlock == "air") return;
|
||||
const radiusBlocks: Block[] = [];
|
||||
const newBlocks: Block[] = [];
|
||||
|
||||
for (let x = 0; x < radius; x++) {
|
||||
for (let y = 0; y < radius; y++) {
|
||||
const tileX = radiusPosition.x + x;
|
||||
const tileY = radiusPosition.y + y;
|
||||
const addBlock = (x: number, y: number) => {
|
||||
if (isInSelection(x, y)) {
|
||||
newBlocks.push({
|
||||
name: selectedBlock,
|
||||
x,
|
||||
y,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Only add blocks within the selection
|
||||
if (isInSelection(tileX, tileY)) {
|
||||
radiusBlocks.push({
|
||||
name: selectedBlock,
|
||||
x: tileX,
|
||||
y: tileY,
|
||||
});
|
||||
// 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 y = 0; y < radius; y++) {
|
||||
const tileX = radiusPosition.x + x;
|
||||
const tileY = radiusPosition.y + y;
|
||||
|
||||
addBlock(tileX, tileY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicates
|
||||
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