import React, { useEffect, useRef, useState } from "react"; import { Eraser, Hand, Pencil } from "lucide-react"; import * as PIXI from "pixi.js"; import { Container, Stage } from "@pixi/react"; import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, } from "@/components/ui/menubar"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import ThemeChanger from "./components/menubar/theme-changer"; import Blocks from "./components/blocks"; import Cursor from "./components/cursor"; import CursorInformation from "./components/cursor-information"; import spritesheet from "@/lib/data/blocks/programmer-art/spritesheet.json"; // Set scale mode to NEAREST PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST; function App() { const stageContainerRef = useRef(null); const [stageSize, setStageSize] = useState({ width: 0, height: 0 }); const [coords, setCoords] = useState({ x: 0, y: 0 }); const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); const [localMousePosition, setLocalMousePosition] = useState({ x: 0, y: 0 }); const [dragging, setDragging] = useState(false); const [scale, setScale] = useState(1); const [blocks, setBlocks] = useState([]); const [textures, setTextures] = useState>({}); const [cssCursor, setCssCursor] = useState("grab"); const [tool, setTool] = useState("hand"); const [selectedBlock, setSelectedBlock] = useState("stone"); const onToolChange = (value: Tool) => { setTool(value); setCssCursor(value === "hand" ? "grab" : "pointer"); }; const onMouseMove = (e: React.MouseEvent) => { if (dragging) { if (tool === "hand") { setCoords((prevCoords) => ({ x: prevCoords.x + e.movementX, y: prevCoords.y + e.movementY, })); } onMouseDown(); } const stageRect = stageContainerRef.current?.getBoundingClientRect(); if (!stageRect) return; const mouseX = e.clientX - stageRect.left; const mouseY = e.clientY - stageRect.top; setMousePosition({ x: mouseX, y: mouseY, }); setLocalMousePosition({ x: (mouseX - coords.x) / scale, y: (mouseY - coords.y) / scale, }); }; const onMouseDown = () => { setDragging(true); const blockX = Math.floor(localMousePosition.x / 16); const blockY = Math.floor(localMousePosition.y / 16); const updatedBlocks = blocks.filter((b) => !(b.x === blockX && b.y === blockY)); switch (tool) { case "hand": setCssCursor("grabbing"); break; case "pencil": { setBlocks([ ...updatedBlocks, { name: selectedBlock, x: blockX, y: blockY, }, ]); break; } case "eraser": setBlocks(updatedBlocks); break; } }; const onMouseUp = () => { setDragging(false); setCssCursor(tool === "hand" ? "grab" : "pointer"); }; const onWheel = (e: React.WheelEvent) => { e.preventDefault(); const scaleChange = e.deltaY > 0 ? -0.1 : 0.1; const newScale = Math.min(Math.max(scale + scaleChange * scale, 0.25), 32); setScale(newScale); setCoords({ x: mousePosition.x - localMousePosition.x * newScale, y: mousePosition.y - localMousePosition.y * newScale, }); }; useEffect(() => { const resizeCanvas = () => { if (stageContainerRef.current) { setStageSize({ width: stageContainerRef.current.offsetWidth, height: stageContainerRef.current.offsetHeight, }); } }; resizeCanvas(); window.addEventListener("resize", resizeCanvas); const loadSpritesheet = async () => { const sheet = new PIXI.Spritesheet(PIXI.BaseTexture.from("/blocks/programmer-art/spritesheet.png"), spritesheet); await sheet.parse(); setTextures(sheet.textures); }; loadSpritesheet(); }, []); return (
blockmatic File Open Schematic Open Image Export to... .schematic .litematic image More
{textures && }
); } export default App;