diff --git a/src/components/gameMaster/mapEditor/partials/TileList.vue b/src/components/gameMaster/mapEditor/partials/TileList.vue index bd7afa1..4a21a32 100644 --- a/src/components/gameMaster/mapEditor/partials/TileList.vue +++ b/src/components/gameMaster/mapEditor/partials/TileList.vue @@ -29,7 +29,7 @@ :src="`${config.server_endpoint}/textures/tiles/${group.parent.id}.png`" :alt="group.parent.name" @click="openGroup(group)" - @load="() => processTile(group.parent)" + @load="() => tileProcessor.processTile(group.parent)" :class="{ 'border-cyan shadow-lg': isActiveTile(group.parent), 'border-transparent hover:border-gray-300': !isActiveTile(group.parent) @@ -88,13 +88,14 @@ import config from '@/application/config' import type { Tile } from '@/application/types' import { useMapEditorComposable } from '@/composables/useMapEditorComposable' +import { useTileProcessingComposable } from '@/composables/useTileProcessingComposable' import { TileStorage } from '@/storage/storages' -import { liveQuery } from 'dexie' -import { computed, onMounted, onUnmounted, ref, useTemplateRef } from 'vue' +import { computed, onMounted, onUnmounted, ref } from 'vue' const isOpen = ref(false) const tileStorage = new TileStorage() const mapEditor = useMapEditorComposable() +const tileProcessor = useTileProcessingComposable() const searchQuery = ref('') const selectedTags = ref([]) const tileCategories = ref>(new Map()) @@ -121,7 +122,7 @@ const groupedTiles = computed(() => { }) filteredTiles.forEach((tile) => { - const parentGroup = groups.find((group) => areTilesRelated(group.parent, tile)) + const parentGroup = groups.find((group) => tileProcessor.areTilesRelated(group.parent, tile)) if (parentGroup && parentGroup.parent.id !== tile.id) { parentGroup.children.push(tile) } else { @@ -132,32 +133,6 @@ const groupedTiles = computed(() => { return groups }) -const tileColorData = ref>(new Map()) -const tileEdgeData = ref>(new Map()) - -function areTilesRelated(tile1: Tile, tile2: Tile): boolean { - const colorSimilarityThreshold = 30 // Adjust this value as needed - const edgeComplexitySimilarityThreshold = 20 // Adjust this value as needed - - const color1 = tileColorData.value.get(tile1.id) - const color2 = tileColorData.value.get(tile2.id) - const edge1 = tileEdgeData.value.get(tile1.id) - const edge2 = tileEdgeData.value.get(tile2.id) - - if (!color1 || !color2 || edge1 === undefined || edge2 === undefined) { - return false - } - - const colorDifference = Math.sqrt(Math.pow(color1.r - color2.r, 2) + Math.pow(color1.g - color2.g, 2) + Math.pow(color1.b - color2.b, 2)) - - const edgeComplexityDifference = Math.abs(edge1 - edge2) - - const namePrefix1 = tile1.name.split('_')[0] - const namePrefix2 = tile2.name.split('_')[0] - - return colorDifference <= colorSimilarityThreshold && edgeComplexityDifference <= edgeComplexitySimilarityThreshold && namePrefix1 === namePrefix2 -} - const toggleTag = (tag: string) => { if (selectedTags.value.includes(tag)) { selectedTags.value = selectedTags.value.filter((t) => t !== tag) @@ -166,59 +141,6 @@ const toggleTag = (tag: string) => { } } -function processTile(tile: Tile) { - const img = new Image() - img.crossOrigin = 'Anonymous' - img.onload = () => { - const canvas = document.createElement('canvas') - const ctx = canvas.getContext('2d') - canvas.width = img.width - canvas.height = img.height - ctx!.drawImage(img, 0, 0, img.width, img.height) - - const imageData = ctx!.getImageData(0, 0, canvas.width, canvas.height) - tileColorData.value.set(tile.id, getDominantColor(imageData)) - tileEdgeData.value.set(tile.id, getEdgeComplexity(imageData)) - } - img.src = `${config.server_endpoint}/textures/tiles/${tile.id}.png` -} - -function getDominantColor(imageData: ImageData) { - let r = 0, - g = 0, - b = 0, - total = 0 - - for (let i = 0; i < imageData.data.length; i += 4) { - if (imageData.data[i + 3] > 0) { - // Only consider non-transparent pixels - r += imageData.data[i] - g += imageData.data[i + 1] - b += imageData.data[i + 2] - total++ - } - } - - return { - r: Math.round(r / total), - g: Math.round(g / total), - b: Math.round(b / total) - } -} - -function getEdgeComplexity(imageData: ImageData) { - let edgePixels = 0 - for (let y = 0; y < imageData.height; y++) { - for (let x = 0; x < imageData.width; x++) { - const i = (y * imageData.width + x) * 4 - if (imageData.data[i + 3] > 0 && (x === 0 || y === 0 || x === imageData.width - 1 || y === imageData.height - 1 || imageData.data[i - 1] === 0 || imageData.data[i + 7] === 0)) { - edgePixels++ - } - } - } - return edgePixels -} - function getTileCategory(tile: Tile): string { return tileCategories.value.get(tile.id) || '' } @@ -239,22 +161,19 @@ function isActiveTile(tile: Tile): boolean { return mapEditor.selectedTile.value === tile.id } -let subscription: any = null +onMounted(async () => { + tiles.value = await tileStorage.getAll() + const initialBatchSize = 20 + const initialTiles = tiles.value.slice(0, initialBatchSize) + initialTiles.forEach((tile) => tileProcessor.processTile(tile)) -onMounted(() => { - subscription = liveQuery(() => tileStorage.liveQuery()).subscribe({ - next: (result) => { - tiles.value = result - }, - error: (error) => { - console.error('Failed to fetch tiles:', error) - } - }) + // Process remaining tiles in background + setTimeout(() => { + tiles.value.slice(initialBatchSize).forEach((tile) => tileProcessor.processTile(tile)) + }, 1000) }) onUnmounted(() => { - if (subscription) { - subscription.unsubscribe() - } + tileProcessor.cleanup() })