From 78032752676b9c4faaac3c07b0f81e6df4576788 Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Tue, 24 Dec 2024 20:43:03 +0000 Subject: [PATCH] feat: tool radius --- src/components/canvas/Canvas.tsx | 62 ++++++++++++++----- src/components/canvas/Cursor.tsx | 17 +++-- src/components/menubar/index.tsx | 2 +- src/components/tool-settings/Radius.tsx | 25 ++++++++ .../SelectorBlocks.tsx | 0 .../index.tsx | 10 ++- src/context/Tool.tsx | 15 +++-- src/pages/AppPage.tsx | 4 +- 8 files changed, 103 insertions(+), 32 deletions(-) create mode 100644 src/components/tool-settings/Radius.tsx rename src/components/{block-selector => tool-settings}/SelectorBlocks.tsx (100%) rename src/components/{block-selector => tool-settings}/index.tsx (82%) diff --git a/src/components/canvas/Canvas.tsx b/src/components/canvas/Canvas.tsx index 38cc3ff..35906f2 100644 --- a/src/components/canvas/Canvas.tsx +++ b/src/components/canvas/Canvas.tsx @@ -29,7 +29,7 @@ function Canvas() { const { setLoading } = useContext(LoadingContext); const { settings } = useContext(SettingsContext); const { missingTexture, textures, solidTextures } = useContext(TexturesContext); - const { tool, selectedBlock, cssCursor, setTool, setCssCursor } = useContext(ToolContext); + const { tool, radius, selectedBlock, cssCursor, setTool, setCssCursor } = useContext(ToolContext); const stageContainerRef = useRef(null); @@ -40,10 +40,6 @@ function Canvas() { const [holdingAlt, setHoldingAlt] = useState(false); const [oldTool, setOldTool] = useState("hand"); - const updatedBlocks = useMemo(() => { - return blocks.filter((b) => !(b.x === mouseCoords.x && b.y === mouseCoords.y)); - }, [blocks, mouseCoords]); - const visibleArea = useMemo(() => { const blockSize = 16 * scale; @@ -87,26 +83,58 @@ function Canvas() { }, [dragging, holdingAlt, tool, setCssCursor]); const onToolUse = useCallback(() => { + // Radius calculation - if odd number cursor is in center, if even cursor is in top-left corner + const getBlocksWithinRadius = (centerX: number, centerY: number, radius: number, blockName: string): Block[] => { + const radiusBlocks = []; + const halfSize = Math.floor(radius / 2); + + const startX = centerX - (radius % 2 === 0 ? 0 : halfSize); + const startY = centerY - (radius % 2 === 0 ? 0 : halfSize); + + for (let x = 0; x < radius; x++) { + for (let y = 0; y < radius; y++) { + const tileX = startX + x; + const tileY = startY + y; + + radiusBlocks.push({ + name: blockName, + x: tileX, + y: tileY, + }); + } + } + + return radiusBlocks; + }; + switch (tool) { case "pencil": { - setBlocks([ - ...updatedBlocks, - { - name: selectedBlock, - x: mouseCoords.x, - y: mouseCoords.y, - }, - ]); + const newBlocks = getBlocksWithinRadius(mouseCoords.x, mouseCoords.y, radius, selectedBlock); + const mergedBlocks = blocks.filter((block) => { + return !newBlocks.some((newBlock) => block.x === newBlock.x && block.y === newBlock.y); + }); + + setBlocks([...mergedBlocks, ...newBlocks]); break; } - case "eraser": + case "eraser": { // Fixes Infinity and NaN errors if (blocks.length == 1) break; - setBlocks(updatedBlocks); + const halfSize = Math.floor(radius / 2); + const startX = mouseCoords.x - (radius % 2 === 0 ? 0 : halfSize); + const startY = mouseCoords.y - (radius % 2 === 0 ? 0 : halfSize); + + const updated = blocks.filter((block) => { + const withinSquare = block.x >= startX && block.x < startX + radius && block.y >= startY && block.y < startY + radius; + return !withinSquare; + }); + + setBlocks(updated); break; + } } - }, [tool, mouseCoords, selectedBlock, updatedBlocks, setBlocks, blocks.length]); + }, [tool, mouseCoords, selectedBlock, setBlocks, blocks, radius]); const onMouseMove = useCallback( (e: React.MouseEvent) => { @@ -261,7 +289,7 @@ function Canvas() { {settings.canvasBorder && } - + {settings.grid && ( diff --git a/src/components/canvas/Cursor.tsx b/src/components/canvas/Cursor.tsx index 31d456e..b8c9241 100644 --- a/src/components/canvas/Cursor.tsx +++ b/src/components/canvas/Cursor.tsx @@ -1,18 +1,25 @@ import { Graphics } from "@pixi/react"; interface Props { - mouseCoords: Position + mouseCoords: Position; + radius: number; } -function Cursor({ mouseCoords }: Props) { +function Cursor({ mouseCoords, radius }: Props) { + const isOddRadius = radius % 2 !== 0; + const halfSize = Math.floor(radius / 2); + + const offset = isOddRadius ? -halfSize : 0; + const size = radius * 16; + return ( { g.clear(); g.lineStyle(1, 0xffffff, 1); - g.drawRect(0, 0, 16, 16); + g.drawRect(0, 0, size, size); }} /> ); diff --git a/src/components/menubar/index.tsx b/src/components/menubar/index.tsx index 2f5b2a9..9375b50 100644 --- a/src/components/menubar/index.tsx +++ b/src/components/menubar/index.tsx @@ -13,7 +13,7 @@ import GithubIcon from "@/assets/github.svg?react"; function Menubar() { return ( - + diff --git a/src/components/tool-settings/Radius.tsx b/src/components/tool-settings/Radius.tsx new file mode 100644 index 0000000..1fc4610 --- /dev/null +++ b/src/components/tool-settings/Radius.tsx @@ -0,0 +1,25 @@ +import { useContext } from "react"; + +import { ToolContext } from "@/context/Tool"; +import { Input } from "@/components/ui/input"; +import { Label } from "../ui/label"; + +function Radius() { + const { radius, setRadius } = useContext(ToolContext); + + return ( +
+ + setRadius(Math.min(Math.max(parseInt(e.target.value), 1), 10))} + /> +
+ ); +} + +export default Radius; diff --git a/src/components/block-selector/SelectorBlocks.tsx b/src/components/tool-settings/SelectorBlocks.tsx similarity index 100% rename from src/components/block-selector/SelectorBlocks.tsx rename to src/components/tool-settings/SelectorBlocks.tsx diff --git a/src/components/block-selector/index.tsx b/src/components/tool-settings/index.tsx similarity index 82% rename from src/components/block-selector/index.tsx rename to src/components/tool-settings/index.tsx index 30c40c4..2cd5a08 100644 --- a/src/components/block-selector/index.tsx +++ b/src/components/tool-settings/index.tsx @@ -1,10 +1,12 @@ import { useEffect, useRef, useState } from "react"; import { Input } from "@/components/ui/input"; +import { Separator } from "@/components/ui/separator"; import SelectorBlocks from "./SelectorBlocks"; +import Radius from "./Radius"; -function BlockSelector() { +function ToolSettings() { const divRef = useRef(null); const [stageWidth, setStageWidth] = useState(0); const [searchInput, setSearchInput] = useState(""); @@ -17,8 +19,10 @@ function BlockSelector() { return (
- setSearchInput(e.target.value)} /> + + + setSearchInput(e.target.value)} />
@@ -26,4 +30,4 @@ function BlockSelector() { ); } -export default BlockSelector; +export default ToolSettings; diff --git a/src/context/Tool.tsx b/src/context/Tool.tsx index 269d337..1e95c8f 100644 --- a/src/context/Tool.tsx +++ b/src/context/Tool.tsx @@ -6,15 +6,18 @@ interface Props { export const ToolContext = createContext({ tool: "hand" as Tool, + radius: 1, selectedBlock: "stone", cssCursor: "pointer", - setTool: (tool: Tool) => {}, - setSelectedBlock: (block: string) => {}, - setCssCursor: (cursor: string) => {}, + setTool: ((tool: Tool) => {}) as React.Dispatch>, + setRadius: ((value: number) => {}) as React.Dispatch>, + setSelectedBlock: ((block: string) => {}) as React.Dispatch>, + setCssCursor: ((cursor: string) => {}) as React.Dispatch>, }); export const ToolProvider = ({ children }: Props) => { const [tool, setTool] = useState("hand"); + const [radius, setRadius] = useState(1); const [selectedBlock, setSelectedBlock] = useState("stone"); const [cssCursor, setCssCursor] = useState("pointer"); @@ -33,5 +36,9 @@ export const ToolProvider = ({ children }: Props) => { } }, [tool]); - return {children}; + return ( + + {children} + + ); }; diff --git a/src/pages/AppPage.tsx b/src/pages/AppPage.tsx index 86e521e..e87f8bb 100644 --- a/src/pages/AppPage.tsx +++ b/src/pages/AppPage.tsx @@ -8,7 +8,7 @@ import { ToolProvider } from "@/context/Tool"; import Menubar from "@/components/menubar"; import Toolbar from "@/components/toolbar"; import Canvas from "@/components/canvas/Canvas"; -import BlockSelector from "@/components/block-selector"; +import ToolSettings from "@/components/tool-settings"; function AppPage() { return ( @@ -22,7 +22,7 @@ function AppPage() { - +