import { ref } from 'vue' import config from '@/application/config' import type { Tile } from '@/application/types' import type { TileAnalysisResult, TileWorkerMessage } from '@/types/tileTypes' // Constants for image processing const DOWNSCALE_WIDTH = 32 const DOWNSCALE_HEIGHT = 16 const COLOR_SIMILARITY_THRESHOLD = 30 const EDGE_SIMILARITY_THRESHOLD = 20 const BATCH_SIZE = 4 export function useTileProcessingComposable() { const tileAnalysisCache = ref>(new Map()) const processingQueue = ref([]) let isProcessing = false const worker = new Worker(new URL('@/workers/tileAnalyzerWorker.ts', import.meta.url), { type: 'module' }) worker.onmessage = (e: MessageEvent) => { const { tileId, color, edge, namePrefix } = e.data tileAnalysisCache.value.set(tileId, { color, edge, namePrefix }) isProcessing = false processBatch() } async function processTileAsync(tile: Tile): Promise { if (tileAnalysisCache.value.has(tile.id)) return return new Promise((resolve) => { const img = new Image() img.crossOrigin = 'Anonymous' img.onload = () => { const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d') if (!ctx) { resolve() return } canvas.width = DOWNSCALE_WIDTH canvas.height = DOWNSCALE_HEIGHT ctx.drawImage(img, 0, 0, DOWNSCALE_WIDTH, DOWNSCALE_HEIGHT) const imageData = ctx.getImageData(0, 0, DOWNSCALE_WIDTH, DOWNSCALE_HEIGHT) const message: TileWorkerMessage = { imageData, tileId: tile.id, tileName: tile.name } worker.postMessage(message) resolve() } img.onerror = () => resolve() img.src = `${config.server_endpoint}/textures/tiles/${tile.id}.png` }) } function processBatch() { if (isProcessing || processingQueue.value.length === 0) return isProcessing = true const batch = processingQueue.value.splice(0, BATCH_SIZE) Promise.all(batch.map(tile => processTileAsync(tile))) .then(() => { isProcessing = false if (processingQueue.value.length > 0) { setTimeout(processBatch, 0) } }) } function processTile(tile: Tile) { if (!processingQueue.value.includes(tile)) { processingQueue.value.push(tile) processBatch() } } function areTilesRelated(tile1: Tile, tile2: Tile): boolean { const data1 = tileAnalysisCache.value.get(tile1.id) const data2 = tileAnalysisCache.value.get(tile2.id) if (!data1 || !data2) return false const colorDifference = Math.sqrt( Math.pow(data1.color.r - data2.color.r, 2) + Math.pow(data1.color.g - data2.color.g, 2) + Math.pow(data1.color.b - data2.color.b, 2) ) return ( colorDifference <= COLOR_SIMILARITY_THRESHOLD && Math.abs(data1.edge - data2.edge) <= EDGE_SIMILARITY_THRESHOLD && data1.namePrefix === data2.namePrefix ) } function cleanup() { worker.terminate() } return { processTile, areTilesRelated, cleanup } }