import { useEffect, useRef, useState } from "react"; import * as PIXI from "pixi.js"; import { useApp } from "@pixi/react"; import { CompositeTilemap, settings } from "@pixi/tilemap"; import blocksData from "@/data/blocks/programmer-art/average_colors.json"; interface Props { blocks: Block[]; setBlocks: React.Dispatch>; textures: Record; image: HTMLImageElement | undefined; imageDimensions: Dimension; coords: Position; scale: number; } // Lifts 16,000 tiles limit settings.use32bitIndex = true; function Blocks({ blocks, setBlocks, textures, image, imageDimensions, coords, scale }: Props) { const app = useApp(); const [missingTexture, setMissingTexture] = useState(); const tilemapRef = useRef(); const tileBlocks = () => { if (!tilemapRef.current) return; const tilemap = tilemapRef.current; tilemap.clear(); blocks.forEach((block) => { tilemap.tile(textures[`${block.name}.png`] ?? missingTexture, block.x * 16, block.y * 16); }); }; useEffect(() => { // Load the missing texture const loadMissingTexture = async () => { const mTexture = await PIXI.Texture.from("/blocks/missing.png"); setMissingTexture(mTexture); }; loadMissingTexture(); // Create tilemap const tilemap = new CompositeTilemap(); tilemapRef.current = tilemap; tilemap.cullable = true; app.stage.addChildAt(tilemap, 0); tileBlocks(); }, []); useEffect(tileBlocks, [blocks]); useEffect(() => { if (!tilemapRef.current) return; tileBlocks(); tilemapRef.current.x = coords.x; tilemapRef.current.y = coords.y; tilemapRef.current.scale.set(scale, scale); }, [coords, scale]); const findClosestBlock = (r: number, g: number, b: number, a: number) => { let closestBlock = ""; let closestDistance = Infinity; Object.entries(blocksData).forEach(([block, rgba]) => { const distance = Math.sqrt(Math.pow(r - rgba[0], 2) + Math.pow(g - rgba[1], 2) + Math.pow(b - rgba[2], 2) + Math.pow(a - rgba[3], 3)); if (distance < closestDistance) { closestDistance = distance; closestBlock = block; } }); return closestBlock; }; useEffect(() => { if (!image) return; const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); if (ctx) { canvas.width = imageDimensions.width; canvas.height = imageDimensions.height; ctx.drawImage(image, 0, 0, imageDimensions.width, imageDimensions.height); const imageData = ctx.getImageData(0, 0, imageDimensions.width, imageDimensions.height); const newBlocks: Block[] = []; for (let i = 0; i < imageData.data.length; i += 4) { const block = findClosestBlock(imageData.data[i], imageData.data[i + 1], imageData.data[i + 2], imageData.data[i + 3]); const x = Math.floor((i / 4) % imageData.width); const y = Math.floor(i / 4 / imageData.width); newBlocks.push({ name: block, x, y, }); } setBlocks(newBlocks); } }, [image, imageDimensions]); return null; } export default Blocks;