diff --git a/src/components/canvas/Blocks.tsx b/src/components/canvas/Blocks.tsx index fff199f..cf640c1 100644 --- a/src/components/canvas/Blocks.tsx +++ b/src/components/canvas/Blocks.tsx @@ -16,6 +16,7 @@ interface Props { solidTextures: Record; image: HTMLImageElement | undefined; imageDimensions: Dimension; + usableBlocks: string[]; coords: Position; scale: number; version: number; @@ -25,7 +26,20 @@ interface Props { // Lifts 16,000 tiles limit settings.use32bitIndex = true; -function Blocks({ blocks, setBlocks, missingTexture, textures, solidTextures, image, imageDimensions, coords, scale, version, setLoading }: Props) { +function Blocks({ + blocks, + setBlocks, + missingTexture, + textures, + solidTextures, + image, + imageDimensions, + usableBlocks, + coords, + scale, + version, + setLoading, +}: Props) { const app = useApp(); const tilemapRef = useRef(); @@ -84,7 +98,7 @@ function Blocks({ blocks, setBlocks, missingTexture, textures, solidTextures, im const newBlocks: Block[] = []; for (let i = 0; i < imageData.data.length; i += 4) { - const block = findBlockFromRgb(blockData, imageData.data[i], imageData.data[i + 1], imageData.data[i + 2], imageData.data[i + 3]); + const block = findBlockFromRgb(usableBlocks, imageData.data[i], imageData.data[i + 1], imageData.data[i + 2], imageData.data[i + 3]); if (block == "air") continue; const x = Math.floor((i / 4) % imageData.width); diff --git a/src/components/canvas/Canvas.tsx b/src/components/canvas/Canvas.tsx index 0bd8331..451bc20 100644 --- a/src/components/canvas/Canvas.tsx +++ b/src/components/canvas/Canvas.tsx @@ -26,7 +26,7 @@ PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST; function Canvas() { const { stageSize, canvasSize, blocks, coords, scale, version, setStageSize, setBlocks, setCoords, setScale } = useContext(CanvasContext); - const { image, imageDimensions } = useContext(ImageContext); + const { image, imageDimensions, usableBlocks } = useContext(ImageContext); const { setLoading } = useContext(LoadingContext); const { settings } = useContext(SettingsContext); const { missingTexture, textures, solidTextures } = useContext(TexturesContext); @@ -308,6 +308,7 @@ function Canvas() { solidTextures={solidTextures} image={image} imageDimensions={imageDimensions} + usableBlocks={usableBlocks} coords={coords} scale={scale} version={version} diff --git a/src/components/dialogs/OpenImage.tsx b/src/components/dialogs/OpenImage.tsx index df2a56a..2288c30 100644 --- a/src/components/dialogs/OpenImage.tsx +++ b/src/components/dialogs/OpenImage.tsx @@ -21,11 +21,12 @@ import { Toggle } from "@/components/ui/toggle"; import { getBlockData } from "@/utils/getBlockData"; import BlockSelector from "./open-image/BlockSelector"; +import VersionCombobox from "../VersionCombobox"; function OpenImage({ close }: DialogProps) { - const { version } = useContext(CanvasContext); + const { version, setVersion } = useContext(CanvasContext); const { setLoading } = useContext(LoadingContext); - const { setImage: setContextImage, setImageDimensions: setContextImageDimensions } = useContext(ImageContext); + const { setImage: setContextImage, setImageDimensions: setContextImageDimensions, setUsableBlocks } = useContext(ImageContext); const { acceptedFiles, getRootProps, getInputProps } = useDropzone({ accept: { @@ -33,7 +34,9 @@ function OpenImage({ close }: DialogProps) { }, }); + const blockData = getBlockData(version); const divRef = useRef(null); + const userModifiedBlocks = useRef(false); const [image, setImage] = useState(); const [imageDimensions, setImageDimensions] = useState({ width: 0, height: 0 }); @@ -43,15 +46,13 @@ function OpenImage({ close }: DialogProps) { const [searchInput, setSearchInput] = useState(""); const [stageWidth, setStageWidth] = useState(0); - const [selectedBlocks, setSelectedBlocks] = useState(["stone"]); + const [selectedBlocks, setSelectedBlocks] = useState(Object.keys(blockData)); const [blockTypeCheckboxesChecked, setBlockTypeCheckboxesChecked] = useState({ creative: false, tile_entity: false, fallable: false, }); - const blockData = getBlockData(version); - useEffect(() => { if (acceptedFiles[0]) { const img = new Image(); @@ -89,9 +90,15 @@ function OpenImage({ close }: DialogProps) { setBlockTypeCheckboxesChecked((prev) => ({ ...prev, [property]: checked })); }; + const onBlockSelectionChange = (newValue: string[]) => { + userModifiedBlocks.current = true; + setSelectedBlocks(newValue); + }; + const onSubmit = () => { if (image) { setLoading(true); + setUsableBlocks(selectedBlocks); // Wait for loading indicator to appear setTimeout(() => { @@ -113,6 +120,12 @@ function OpenImage({ close }: DialogProps) { }); }, [selectedBlocks]); + useEffect(() => { + if (!userModifiedBlocks.current) { + setSelectedBlocks(Object.keys(blockData)); + } + }, [version]); + useEffect(() => { if (!divRef.current) return; setStageWidth(divRef.current.clientWidth); @@ -237,10 +250,10 @@ function OpenImage({ close }: DialogProps) {
- -
@@ -256,21 +269,24 @@ function OpenImage({ close }: DialogProps) { searchInput={searchInput} selectedBlocks={selectedBlocks} setSelectedBlocks={setSelectedBlocks} + userModifiedBlocks={userModifiedBlocks} /> - - {/* todo: add version selector here */} + + - - +
+ + +
); diff --git a/src/components/dialogs/open-image/BlockSelector.tsx b/src/components/dialogs/open-image/BlockSelector.tsx index 5ffec1f..026e2a7 100644 --- a/src/components/dialogs/open-image/BlockSelector.tsx +++ b/src/components/dialogs/open-image/BlockSelector.tsx @@ -12,9 +12,10 @@ interface Props { searchInput: string; selectedBlocks: string[]; setSelectedBlocks: React.Dispatch>; + userModifiedBlocks: React.MutableRefObject; } -function BlockSelector({ stageWidth, searchInput, selectedBlocks, setSelectedBlocks }: Props) { +function BlockSelector({ stageWidth, searchInput, selectedBlocks, setSelectedBlocks, userModifiedBlocks }: Props) { const { version } = useContext(CanvasContext); const { missingTexture, textures } = useContext(TexturesContext); const { isDark } = useContext(ThemeContext); @@ -27,6 +28,8 @@ function BlockSelector({ stageWidth, searchInput, selectedBlocks, setSelectedBlo const blocksPerColumn = Math.floor(stageWidth / (32 + 2)); const onClick = (block: string) => { + userModifiedBlocks.current = true; + if (selectedBlocks.includes(block)) { setSelectedBlocks((prev) => prev.filter((blockName) => blockName !== block)); } else { diff --git a/src/context/Image.tsx b/src/context/Image.tsx index f9ec807..33af7f6 100644 --- a/src/context/Image.tsx +++ b/src/context/Image.tsx @@ -3,8 +3,10 @@ import { createContext, ReactNode, useState } from "react"; interface Context { image: HTMLImageElement | undefined; imageDimensions: Dimension; + usableBlocks: string[]; setImage: React.Dispatch>; setImageDimensions: React.Dispatch>; + setUsableBlocks: React.Dispatch>; } interface Props { @@ -16,6 +18,11 @@ export const ImageContext = createContext({} as Context); export const ImageProvider = ({ children }: Props) => { const [image, setImage] = useState(); const [imageDimensions, setImageDimensions] = useState({ width: 0, height: 0 }); + const [usableBlocks, setUsableBlocks] = useState([]); - return {children}; + return ( + + {children} + + ); }; diff --git a/src/utils/findBlockFromRgb.ts b/src/utils/findBlockFromRgb.ts index dda8ccd..03b0460 100644 --- a/src/utils/findBlockFromRgb.ts +++ b/src/utils/findBlockFromRgb.ts @@ -1,8 +1,16 @@ -export const findBlockFromRgb = (data: BlockData, r: number, g: number, b: number, a: number): string => { - return Object.entries(data).reduce( - (closestBlock, [block, data]) => { +import _blockData from "@/data/blocks/data.json"; +const blockData: BlockData = _blockData; + +export const findBlockFromRgb = (data: BlockData | string[], r: number, g: number, b: number, a: number): string => { + const source = Array.isArray(data) ? Object.entries(blockData).filter(([block]) => data.includes(block)) : Object.entries(data); + + return source.reduce( + (closestBlock, [block, blockData]) => { const distance = Math.sqrt( - Math.pow(r - data.color[0], 2) + Math.pow(g - data.color[1], 2) + Math.pow(b - data.color[2], 2) + Math.pow(a - data.color[3], 2) + Math.pow(r - blockData.color[0], 2) + + Math.pow(g - blockData.color[1], 2) + + Math.pow(b - blockData.color[2], 2) + + Math.pow(a - blockData.color[3], 2) ); return distance < closestBlock.distance ? { block, distance } : closestBlock; },