import { Pos, Size } from "@/utils/math"; import { extractConnectedSegments } from "@/utils/polygon"; import { validNum } from "@/utils/shared"; import { aiIconMap, iconGroups } from "../constant"; import { Euler, Quaternion } from "three"; export enum SCENE_TYPE { fuse = "fuse", mesh = "mesh", cloud = "cloud", } export type Scene = { type: SCENE_TYPE; m: string; title: string; id: string; }; export type SceneFloor = { name: string; subgroup?: number; geos: (Pos & { z: number })[][]; thumb?: string; box?: { bound: { x_min: number; x_max: number; y_min: number; y_max: number; z_min: number; z_max: number; }; rotate: number; scale: number; }; compass?: number; }; export type SceneFloors = SceneFloor[]; export type Taging = { url: string; position: Pos & { z: number }; size?: Size; rotate?: number; name?: string; pixel?: boolean; subgroup?: string; }; export const SceneTypeNames = { [SCENE_TYPE.fuse]: "融合场景", [SCENE_TYPE.mesh]: "Mesh场景", [SCENE_TYPE.cloud]: "点云场景", }; export const getSceneApi = async (type: string | undefined, url: string) => { if (url[0] === "/") { url = url.substring(1, url.length); } let origin = type ? window.platform.resourceURLS[type] : ''; if (origin[origin.length - 1] !== "/") { origin = origin + "/"; } const uri = origin + url; // try { // uri = new URL(window.platform.resourceURLS[type]).toString(); // } catch { // uri = window.platform.resourceURLS[type] + url; // } const res = await fetch(uri, { method: "HEAD" }); if (res.status !== 200) { throw `${uri}链接错误`; } return uri; }; export type Tagings = Taging[]; export const compassGets = { [SCENE_TYPE.fuse]: () => void 0, [SCENE_TYPE.cloud]: () => void 0, [SCENE_TYPE.mesh]: async (scene: Scene) => { const prev = `/scene_view_data/${scene.m}`; const config = await getSceneApi("oss", `${prev}/data/scene.json`) .then((url) => fetch(url)) .then((res) => res.json()) .catch(() => ({ version: 0, billboards: 0, tags: 0, orientation: 0 })); const floorpanCompass = await getSceneApi( "oss", `${prev}/user/floorplan.json?_=${config.version}` ) .then((url) => fetch(url)) .then((res) => res.json()) .then((data) => data.compass) .catch(() => null); return typeof floorpanCompass === "number" ? floorpanCompass : Number(config.orientation); }, }; export const taggingGets = { [SCENE_TYPE.fuse]: async (scene: Scene, options: string[]) => { if (!options.includes("hot")) return []; const reqOpts = { headers: { share: "1" } }; const icons = await getSceneApi( scene.type, `/fusion/edit/hotIcon/list?caseId=${scene.m}` ) .then((url) => fetch(url, reqOpts)) .then((res) => res.json()) .then((res) => res.data) .catch(() => []); const tagTypes: any[] = await getSceneApi( scene.type, `/fusion/caseTag/allList?caseId=${scene.m}` ) .then((url) => fetch(url, reqOpts)) .then((res) => res.json()) .then((res) => res.data) .catch(() => []); const tags: Tagings = []; const reqs = tagTypes.map((type) => getSceneApi( scene.type, `/fusion/caseTagPoint/allList?tagId=${type.tagId}` ) .then((url) => fetch(url, reqOpts)) .then((res) => res.json()) .then((res) => res.data) .then((items) => { items.forEach((item: any) => { tags.push({ url: icons.find((icon: any) => icon.iconId === type.hotIconId) ?.iconUrl, position: JSON.parse(item.tagPoint), }); }); }) .catch(() => []) ); await Promise.all(reqs); return tags; }, [SCENE_TYPE.cloud]: async (scene: Scene, options: string[]) => { if (!options.includes("hot")) return []; const tags: Tagings = await getSceneApi( scene.type, `/laser/poi/${scene.m}/list` ) .then((url) => fetch(url)) .then((res) => res.json()) .then((res) => res.data.list) .then((pois) => pois.map((poi: any) => ({ url: poi.hotStyleAtom.icon, position: poi.dataset_location, })) ) .catch(() => []); return tags; }, [SCENE_TYPE.mesh]: async (scene: Scene, options: string[]) => { const tags: Tagings = []; const prev = `/scene_view_data/${scene.m}`; const config = await getSceneApi("oss", `${prev}/data/scene.json`) .then((url) => fetch(url)) .then((res) => res.json()) .catch(() => ({ version: 0, billboards: 0, tags: 0, orientation: 0 })); if (options.includes("hot") && config.tags) { const medias = await getSceneApi( "oss", `${prev}/user/hot.json?_=${config.version}` ) .then((url) => fetch(url)) .then((res) => res.json()) .catch(() => []); const reqs = medias.map((media: any) => { if (!validNum(media.position.x) || !validNum(media.position.y)) return; return getSceneApi("oss", `${prev}/user/${media.icon}`) .then((url) => { tags.push({ url, position: media.position }); }) .catch(() => {}); }); await Promise.all(reqs); } if (options.includes("signage") && config.billboards) { const signages = await getSceneApi( "oss", `${prev}/user/billboards.json?_=${config.version}` ) .then((url) => fetch(url)) .then((res) => res.json()) .catch(() => []); const reqs = signages.map((signage: any) => { if (!validNum(signage.pos[0]) || !validNum(signage.pos[2])) return; console.error(signage) const getIcon = signage.icon.indexOf("style-") === 0 ? getSceneApi(undefined, `/styles/${signage.icon}.svg`).catch((e) => { console.error(e) return getSceneApi( "ossRoot", `/sdk/images/billboard/${signage.icon}.png` ) }) : getSceneApi("oss", `${prev}/user/${signage.icon}`); const q = new Quaternion(...signage.qua) const yRotate = new Euler().setFromQuaternion(q, 'XYZ').y + Math.PI return getIcon .then((url) => { tags.push({ url, position: { x: signage.pos[0], y: signage.pos[2], z: signage.pos[1] < 0 ? Math.ceil(signage.pos[1] * 10) / 10 : Math.floor(signage.pos[1] * 10) / 10, }, rotate: yRotate, size: { width: signage.width * (signage.scaleRatio / 100), height: signage.height * (signage.scaleRatio / 100), }, }); }) .catch(() => {}); }); await Promise.all(reqs); } await getSceneApi( "oss", `${prev}/data/floorplan/ai.json?_=${config.version}` ) .then((url) => fetch(url)) .then((res) => res.json()) .then((datas) => { for (const data of datas) { const reg = data.imagePath.match(/floor_(\d)\.png/); const subgroup = reg ? Number(reg[1]) : undefined; for (const shape of data.shapes) { const pos = { x: (shape.bbox[0] + shape.bbox[2]) / 2 / data.imageWidth, y: (shape.bbox[1] + shape.bbox[3]) / 2 / data.imageHeight, z: undefined, }; const size = { width: (shape.bbox[2] - shape.bbox[0]) / data.imageWidth, height: (shape.bbox[3] - shape.bbox[1]) / data.imageHeight, }; const icon = shape.category in aiIconMap ? (aiIconMap as any)[shape.category] : shape.category; let name = ""; for (const group of iconGroups) { for (const itemGroup of group.children) { for (const item of itemGroup.children) { if (item.icon === icon) { name = item.name; } } } } if (name) { tags.push({ position: pos, url: `./icons/${icon ? icon : "circle"}.svg`, name, pixel: true, size, subgroup, } as any); } else { console.error("找不到ai家具", icon, name, pos); } } } }) .catch((e) => { console.error(e); }); console.log("tags", tags); return tags; }, }; export const getFloors = { [SCENE_TYPE.mesh]: async (scene: Scene) => { return getSceneApi( "oss", `/scene_view_data/${scene.m}/data/floorplan_cad.json?_=${Date.now()}` ) .then((url) => fetch(url)) .then((res) => res.json()) .catch(() => ({ floors: [] })); }, }; export const lineGets = { [SCENE_TYPE.fuse]: async (scene: Scene) => { const tags = await taggingGets[SCENE_TYPE.fuse](scene, ["hot"]); return { name: "1楼", geos: [tags.map((item) => item.position)] }; }, [SCENE_TYPE.mesh]: async (scene: Scene, floorName?: string) => { const prev = `/scene_view_data/${scene.m}/data/`; const [{ floors }, bounds] = await Promise.all([ getFloors[SCENE_TYPE.mesh](scene), getSceneApi("oss", `${prev}floorplan/info.json`) .then((url) => fetch(url)) .then((res) => res.json()) .then((data) => data.floors) .catch(() => []), ]); const data: any = []; const reqs = floors .filter((item: any) => !floorName || item.name === floorName) .map((floor: any) => { const bound = { ...(floor.cadInfo.cadBoundingBox || {}), ...(bounds.find((i: any) => i.subgroup === floor.subgroup)?.bound || {}), }; const item: any = { name: floor.name, subgroup: floor.subgroup, thumb: "", box: { bound: { ...bound, y_min: -bound.y_max, y_max: -bound.y_min, z_max: bound.z_max ? Number(bound.z_max) : bound.z_max, z_min: bound.z_min ? Number(bound.z_min) : bound.z_min, }, rotate: floor.cadInfo.res, scale: floor.cadInfo.currentScale, }, geos: extractConnectedSegments(floor.segment).map((geo) => { return geo.map((id) => { const p = floor["vertex-xy"].find((item: any) => item.id === id); return { x: p.x, y: -p.y } as Pos; }); }), }; data.push(item); return getSceneApi( "oss", `${prev}floorplan/floor_${floor.subgroup}.png` ) .then((url) => (item.thumb = url)) .catch(() => (item.thumb = "")); }); await Promise.all(reqs); return data; }, [SCENE_TYPE.cloud]: async (scene: Scene) => { const tags = await taggingGets[SCENE_TYPE.cloud](scene, ["hot"]); return { name: "1楼", geos: [tags.map((item) => item.position)] }; }, };