修复楼层点位展示与楼层匹配
This commit is contained in:
@@ -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
70
src/domain/guideFloor.ts
Normal 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)
|
||||
)
|
||||
@@ -15,6 +15,7 @@ export interface GuideModelFloorAsset {
|
||||
order: number
|
||||
modelUrl: string
|
||||
sharedModelAsset?: boolean
|
||||
modelMatchKeys?: string[]
|
||||
}
|
||||
|
||||
export interface GuideModelRenderPackage {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user