修复楼层点位展示与楼层匹配

This commit is contained in:
lyf
2026-06-28 00:38:42 +08:00
parent f043c85af0
commit 1ad77fcf48
4 changed files with 135 additions and 20 deletions

View File

@@ -132,6 +132,7 @@ interface ReusableModelResources {
interface PoiMarkerCacheEntry {
floorId: string
displayMode: PoiDisplayMode
pois: RenderPoi[]
group: THREE.Group
markerSize: number
@@ -393,20 +394,25 @@ const isExteriorModelNode = (object: THREE.Object3D) => (
))
)
const getKnownFloorIdsBySpecificity = () => (
const getKnownFloorMatchEntriesBySpecificity = () => (
floorIndex.value
.map((floor) => floor.floorId)
.sort((a, b) => b.length - a.length)
.flatMap((floor) => [floor.floorId, floor.label, ...(floor.modelMatchKeys || [])]
.filter((value): value is string => Boolean(value))
.map((key) => ({ key, floorId: floor.floorId })))
.sort((a, b) => b.key.length - a.key.length)
)
const getModelNodeFloorIdByName = (name: string) => {
if (!name) return ''
return getKnownFloorIdsBySpecificity().find((floorId) => (
name === floorId
|| name.startsWith(`${floorId}_`)
|| name.startsWith(`${floorId}中心`)
)) || ''
const normalizedName = normalizeModelMatchKey(name)
return getKnownFloorMatchEntriesBySpecificity().find(({ key }) => (
name === key
|| name.startsWith(`${key}_`)
|| name.startsWith(`${key}中心`)
|| normalizedName === normalizeModelMatchKey(key)
|| normalizedName.startsWith(normalizeModelMatchKey(key))
))?.floorId || ''
}
const getModelNodeFloorId = (object: THREE.Object3D) => (
@@ -489,6 +495,8 @@ const getPoiDisplayMode = (): PoiDisplayMode => {
return 'detail'
}
const getPoiMarkerCacheKey = (floorId: string, displayMode = getPoiDisplayMode()) => `${floorId}:${displayMode}`
const getPoiGlyph = (poi: RenderPoi) => (
poiGlyphMap[poi.iconType] || poi.name.charAt(0) || '点'
)
@@ -521,6 +529,7 @@ const getActiveModelSpan = () => {
}
const getPoiVisibilityTier = (): PoiVisibilityTier => {
if (activeView.value === 'floor') return 'full'
if (!controls || !activeModel) return 'full'
const ratio = controls.getDistance() / getActiveModelSpan()
@@ -2339,10 +2348,16 @@ const addFocusMarkerFromRequest = (request: TargetPoiFocusRequest) => {
return poi
}
const createPoiMarkerGroup = (floorId: string, pois: RenderPoi[], markerSize: number) => {
const createPoiMarkerGroup = (
floorId: string,
displayMode: PoiDisplayMode,
pois: RenderPoi[],
markerSize: number
) => {
const group = new THREE.Group()
group.name = `GuideModelPOI_${floorId}`
group.name = `GuideModelPOI_${floorId}_${displayMode}`
group.userData.floorId = floorId
group.userData.displayMode = displayMode
pois.forEach((poi) => {
const [x, y, z] = poi.positionGltf!
@@ -2374,22 +2389,25 @@ const loadFloorPOIs = async (floor: FloorIndexItem, loadToken?: number) => {
if (loadToken !== undefined && !isCurrentModelLoad(loadToken)) return
const cachedEntry = poiMarkerGroupCache.get(floor.floorId)
const displayMode = getPoiDisplayMode()
const markerCacheKey = getPoiMarkerCacheKey(floor.floorId, displayMode)
const cachedEntry = poiMarkerGroupCache.get(markerCacheKey)
if (cachedEntry) {
attachPoiMarkerGroup(cachedEntry)
return
}
const cachedPois = poiDataCache.get(floor.floorId)
const validPois = cachedPois || (await props.modelSource.loadFloorPois(floor.floorId))
.filter((poi) => shouldShowPoiInCurrentMode(poi))
const floorPois = cachedPois || await props.modelSource.loadFloorPois(floor.floorId)
if (loadToken !== undefined && !isCurrentModelLoad(loadToken)) return
poiDataCache.set(floor.floorId, validPois)
poiDataCache.set(floor.floorId, floorPois)
const validPois = floorPois.filter((poi) => shouldShowPoiInCurrentMode(poi))
const markerSize = getPoiMarkerSize()
const markerGroup = createPoiMarkerGroup(floor.floorId, validPois, markerSize)
const markerGroup = createPoiMarkerGroup(floor.floorId, displayMode, validPois, markerSize)
const entry: PoiMarkerCacheEntry = {
floorId: floor.floorId,
displayMode,
pois: validPois,
group: markerGroup,
markerSize
@@ -2400,7 +2418,7 @@ const loadFloorPOIs = async (floor: FloorIndexItem, loadToken?: number) => {
return
}
poiMarkerGroupCache.set(floor.floorId, entry)
poiMarkerGroupCache.set(markerCacheKey, entry)
attachPoiMarkerGroup(entry)
}

70
src/domain/guideFloor.ts Normal file
View File

@@ -0,0 +1,70 @@
import type {
MuseumFloor,
MuseumPoi
} from '@/domain/museum'
interface FloorLike {
id?: string | number | null
label?: string | null
floorId?: string | number | null
floorCode?: string | null
floorName?: string | null
name?: string | null
modelGroup?: string | null
}
const indoorFloorCodePattern = /^L-?\d+(?:\.\d+)?$/i
const indoorFloorLabelPattern = /^(?:B\d+(?:\.\d+)?|\d+(?:\.\d+)?F)$/i
const rawNumericIdPattern = /^\d{6,}$/
const nonNavigableFloorPattern = /(EXTERIOR|OUTDOOR|BUILDING|FACADE|EXTERNAL|外立面|建筑外观|建筑外立面|室外模型|室外场景|室外底图|室外建筑|室外外观|室外)$/i
const normalizeValue = (value: unknown) => (
value === null || typeof value === 'undefined' ? '' : String(value).trim()
)
export const isNonNavigableFloorToken = (value: unknown) => {
const normalized = normalizeValue(value)
return Boolean(normalized && nonNavigableFloorPattern.test(normalized))
}
export const isIndoorFloorToken = (value: unknown) => {
const normalized = normalizeValue(value)
if (!normalized || isNonNavigableFloorToken(normalized)) return false
return indoorFloorCodePattern.test(normalized)
|| indoorFloorLabelPattern.test(normalized)
|| rawNumericIdPattern.test(normalized)
}
export const isIndoorNavigableFloor = (floor: FloorLike | null | undefined) => {
if (!floor) return false
const tokens = [
floor.id,
floor.label,
floor.floorId,
floor.floorCode,
floor.floorName,
floor.name,
floor.modelGroup
]
if (tokens.some(isNonNavigableFloorToken)) return false
return tokens.some(isIndoorFloorToken)
}
export const isPoiOnIndoorNavigableFloor = (poi: Pick<MuseumPoi, 'floorId' | 'floorLabel'>) => (
isIndoorNavigableFloor({
floorId: poi.floorId,
label: poi.floorLabel
})
)
export const filterIndoorNavigableFloors = <T extends MuseumFloor>(floors: T[]) => (
floors.filter((floor) => isIndoorNavigableFloor(floor))
)
export const filterPoisOnIndoorNavigableFloors = <T extends Pick<MuseumPoi, 'floorId' | 'floorLabel'>>(pois: T[]) => (
pois.filter(isPoiOnIndoorNavigableFloor)
)

View File

@@ -15,6 +15,7 @@ export interface GuideModelFloorAsset {
order: number
modelUrl: string
sharedModelAsset?: boolean
modelMatchKeys?: string[]
}
export interface GuideModelRenderPackage {

View File

@@ -89,6 +89,26 @@ const resolveSgsAssetUrl = (url?: string | null) => {
const sortSgsFloors = (floors: SgsSdkFloorSummaryPayload[]) => [...floors]
.sort((a, b) => Number(a.sortOrder || 0) - Number(b.sortOrder || 0))
const isSharedSgsModelAsset = (
modelUrl: string,
overviewModelUrl: string,
allModelUrls: string[]
) => {
if (!modelUrl) return false
return modelUrl === overviewModelUrl || allModelUrls.filter((url) => url === modelUrl).length > 1
}
const getSgsFloorModelMatchKeys = (floor: SgsSdkFloorSummaryPayload, label: string) => (
[
String(floor.floorId),
floor.floorCode,
floor.floorName,
label
]
.filter((value): value is string => Boolean(value))
)
export interface GuideModelRepository extends GuideModelSource {}
export class StaticGuideModelRepository implements GuideModelRepository {
@@ -144,17 +164,18 @@ export class SgsSdkGuideModelRepository implements GuideModelRepository {
floors.map((floor) => this.provider.getFloorBundle(String(floor.floorId)))
)
const floorAssets: GuideModelFloorAsset[] = floors
const floorModelItems = floors
.map((floor, index) => {
const bundle = bundles[index]
const modelUrl = resolveSgsAssetUrl(bundle.model?.modelUrl || bundle.model?.fallbackModelUrl)
const label = formatSgsFloorLabel(floor.floorCode, floor.floorName)
return {
floorId: String(floor.floorId),
label: formatSgsFloorLabel(floor.floorCode, floor.floorName),
label,
order: Number(floor.sortOrder || 0),
modelUrl,
sharedModelAsset: false
modelMatchKeys: getSgsFloorModelMatchKeys(floor, label)
}
})
.filter((asset) => asset.modelUrl)
@@ -165,7 +186,12 @@ export class SgsSdkGuideModelRepository implements GuideModelRepository {
: null
const overviewModelUrl = resolveSgsAssetUrl(
overviewBundle?.model?.modelUrl || overviewBundle?.model?.fallbackModelUrl
) || floorAssets[0]?.modelUrl || ''
) || floorModelItems[0]?.modelUrl || ''
const floorModelUrls = floorModelItems.map((asset) => asset.modelUrl)
const floorAssets: GuideModelFloorAsset[] = floorModelItems.map((asset) => ({
...asset,
sharedModelAsset: isSharedSgsModelAsset(asset.modelUrl, overviewModelUrl, floorModelUrls)
}))
return {
overviewModelUrl,