platform-resource.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. import { Pos, Size } from "@/utils/math";
  2. import { extractConnectedSegments } from "@/utils/polygon";
  3. import { validNum } from "@/utils/shared";
  4. import { aiIconMap, iconGroups } from "../constant";
  5. import { Euler, Quaternion } from "three";
  6. export enum SCENE_TYPE {
  7. fuse = "fuse",
  8. mesh = "mesh",
  9. cloud = "cloud",
  10. }
  11. export type Scene = {
  12. type: SCENE_TYPE;
  13. m: string;
  14. title: string;
  15. id: string;
  16. };
  17. export type SceneFloor = {
  18. name: string;
  19. subgroup?: number;
  20. geos: (Pos & { z: number })[][];
  21. thumb?: string;
  22. box?: {
  23. bound: {
  24. x_min: number;
  25. x_max: number;
  26. y_min: number;
  27. y_max: number;
  28. z_min: number;
  29. z_max: number;
  30. };
  31. rotate: number;
  32. scale: number;
  33. };
  34. compass?: number;
  35. };
  36. export type SceneFloors = SceneFloor[];
  37. export type Taging = {
  38. url: string;
  39. position: Pos & { z: number };
  40. size?: Size;
  41. rotate?: number;
  42. name?: string;
  43. pixel?: boolean;
  44. subgroup?: string;
  45. };
  46. export const SceneTypeNames = {
  47. [SCENE_TYPE.fuse]: "融合场景",
  48. [SCENE_TYPE.mesh]: "Mesh场景",
  49. [SCENE_TYPE.cloud]: "点云场景",
  50. };
  51. export const getSceneApi = async (type: string | undefined, url: string) => {
  52. if (url[0] === "/") {
  53. url = url.substring(1, url.length);
  54. }
  55. let origin = type ? window.platform.resourceURLS[type] : '';
  56. if (origin[origin.length - 1] !== "/") {
  57. origin = origin + "/";
  58. }
  59. const uri = origin + url;
  60. // try {
  61. // uri = new URL(window.platform.resourceURLS[type]).toString();
  62. // } catch {
  63. // uri = window.platform.resourceURLS[type] + url;
  64. // }
  65. const res = await fetch(uri, { method: "HEAD" });
  66. if (res.status !== 200) {
  67. throw `${uri}链接错误`;
  68. }
  69. return uri;
  70. };
  71. export type Tagings = Taging[];
  72. export const compassGets = {
  73. [SCENE_TYPE.fuse]: () => void 0,
  74. [SCENE_TYPE.cloud]: () => void 0,
  75. [SCENE_TYPE.mesh]: async (scene: Scene) => {
  76. const prev = `/scene_view_data/${scene.m}`;
  77. const config = await getSceneApi("oss", `${prev}/data/scene.json`)
  78. .then((url) => fetch(url))
  79. .then((res) => res.json())
  80. .catch(() => ({ version: 0, billboards: 0, tags: 0, orientation: 0 }));
  81. const floorpanCompass = await getSceneApi(
  82. "oss",
  83. `${prev}/user/floorplan.json?_=${config.version}`
  84. )
  85. .then((url) => fetch(url))
  86. .then((res) => res.json())
  87. .then((data) => data.compass)
  88. .catch(() => null);
  89. return typeof floorpanCompass === "number"
  90. ? floorpanCompass
  91. : Number(config.orientation);
  92. },
  93. };
  94. export const taggingGets = {
  95. [SCENE_TYPE.fuse]: async (scene: Scene, options: string[]) => {
  96. if (!options.includes("hot")) return [];
  97. const reqOpts = { headers: { share: "1" } };
  98. const icons = await getSceneApi(
  99. scene.type,
  100. `/fusion/edit/hotIcon/list?caseId=${scene.m}`
  101. )
  102. .then((url) => fetch(url, reqOpts))
  103. .then((res) => res.json())
  104. .then((res) => res.data)
  105. .catch(() => []);
  106. const tagTypes: any[] = await getSceneApi(
  107. scene.type,
  108. `/fusion/caseTag/allList?caseId=${scene.m}`
  109. )
  110. .then((url) => fetch(url, reqOpts))
  111. .then((res) => res.json())
  112. .then((res) => res.data)
  113. .catch(() => []);
  114. const tags: Tagings = [];
  115. const reqs = tagTypes.map((type) =>
  116. getSceneApi(
  117. scene.type,
  118. `/fusion/caseTagPoint/allList?tagId=${type.tagId}`
  119. )
  120. .then((url) => fetch(url, reqOpts))
  121. .then((res) => res.json())
  122. .then((res) => res.data)
  123. .then((items) => {
  124. items.forEach((item: any) => {
  125. tags.push({
  126. url: icons.find((icon: any) => icon.iconId === type.hotIconId)
  127. ?.iconUrl,
  128. position: JSON.parse(item.tagPoint),
  129. });
  130. });
  131. })
  132. .catch(() => [])
  133. );
  134. await Promise.all(reqs);
  135. return tags;
  136. },
  137. [SCENE_TYPE.cloud]: async (scene: Scene, options: string[]) => {
  138. if (!options.includes("hot")) return [];
  139. const tags: Tagings = await getSceneApi(
  140. scene.type,
  141. `/laser/poi/${scene.m}/list`
  142. )
  143. .then((url) => fetch(url))
  144. .then((res) => res.json())
  145. .then((res) => res.data.list)
  146. .then((pois) =>
  147. pois.map((poi: any) => ({
  148. url: poi.hotStyleAtom.icon,
  149. position: poi.dataset_location,
  150. }))
  151. )
  152. .catch(() => []);
  153. return tags;
  154. },
  155. [SCENE_TYPE.mesh]: async (scene: Scene, options: string[]) => {
  156. const tags: Tagings = [];
  157. const prev = `/scene_view_data/${scene.m}`;
  158. const config = await getSceneApi("oss", `${prev}/data/scene.json`)
  159. .then((url) => fetch(url))
  160. .then((res) => res.json())
  161. .catch(() => ({ version: 0, billboards: 0, tags: 0, orientation: 0 }));
  162. if (options.includes("hot") && config.tags) {
  163. const medias = await getSceneApi(
  164. "oss",
  165. `${prev}/user/hot.json?_=${config.version}`
  166. )
  167. .then((url) => fetch(url))
  168. .then((res) => res.json())
  169. .catch(() => []);
  170. const reqs = medias.map((media: any) => {
  171. if (!validNum(media.position.x) || !validNum(media.position.y)) return;
  172. return getSceneApi("oss", `${prev}/user/${media.icon}`)
  173. .then((url) => {
  174. tags.push({ url, position: media.position });
  175. })
  176. .catch(() => {});
  177. });
  178. await Promise.all(reqs);
  179. }
  180. if (options.includes("signage") && config.billboards) {
  181. const signages = await getSceneApi(
  182. "oss",
  183. `${prev}/user/billboards.json?_=${config.version}`
  184. )
  185. .then((url) => fetch(url))
  186. .then((res) => res.json())
  187. .catch(() => []);
  188. const reqs = signages.map((signage: any) => {
  189. if (!validNum(signage.pos[0]) || !validNum(signage.pos[2])) return;
  190. console.error(signage)
  191. const getIcon =
  192. signage.icon.indexOf("style-") === 0
  193. ? getSceneApi(undefined, `/styles/${signage.icon}.svg`).catch((e) => {
  194. console.error(e)
  195. return getSceneApi(
  196. "ossRoot",
  197. `/sdk/images/billboard/${signage.icon}.png`
  198. )
  199. })
  200. : getSceneApi("oss", `${prev}/user/${signage.icon}`);
  201. const q = new Quaternion(...signage.qua)
  202. const yRotate = new Euler().setFromQuaternion(q, 'XYZ').y + Math.PI
  203. return getIcon
  204. .then((url) => {
  205. tags.push({
  206. url,
  207. position: {
  208. x: signage.pos[0],
  209. y: signage.pos[2],
  210. z:
  211. signage.pos[1] < 0
  212. ? Math.ceil(signage.pos[1] * 10) / 10
  213. : Math.floor(signage.pos[1] * 10) / 10,
  214. },
  215. rotate: yRotate,
  216. size: {
  217. width: signage.width * (signage.scaleRatio / 100),
  218. height: signage.height * (signage.scaleRatio / 100),
  219. },
  220. });
  221. })
  222. .catch(() => {});
  223. });
  224. await Promise.all(reqs);
  225. }
  226. await getSceneApi(
  227. "oss",
  228. `${prev}/data/floorplan/ai.json?_=${config.version}`
  229. )
  230. .then((url) => fetch(url))
  231. .then((res) => res.json())
  232. .then((datas) => {
  233. for (const data of datas) {
  234. const reg = data.imagePath.match(/floor_(\d)\.png/);
  235. const subgroup = reg ? Number(reg[1]) : undefined;
  236. for (const shape of data.shapes) {
  237. const pos = {
  238. x: (shape.bbox[0] + shape.bbox[2]) / 2 / data.imageWidth,
  239. y: (shape.bbox[1] + shape.bbox[3]) / 2 / data.imageHeight,
  240. z: undefined,
  241. };
  242. const size = {
  243. width: (shape.bbox[2] - shape.bbox[0]) / data.imageWidth,
  244. height: (shape.bbox[3] - shape.bbox[1]) / data.imageHeight,
  245. };
  246. const icon =
  247. shape.category in aiIconMap
  248. ? (aiIconMap as any)[shape.category]
  249. : shape.category;
  250. let name = "";
  251. for (const group of iconGroups) {
  252. for (const itemGroup of group.children) {
  253. for (const item of itemGroup.children) {
  254. if (item.icon === icon) {
  255. name = item.name;
  256. }
  257. }
  258. }
  259. }
  260. if (name) {
  261. tags.push({
  262. position: pos,
  263. url: `./icons/${icon ? icon : "circle"}.svg`,
  264. name,
  265. pixel: true,
  266. size,
  267. subgroup,
  268. } as any);
  269. } else {
  270. console.error("找不到ai家具", icon, name, pos);
  271. }
  272. }
  273. }
  274. })
  275. .catch((e) => {
  276. console.error(e);
  277. });
  278. console.log("tags", tags);
  279. return tags;
  280. },
  281. };
  282. export const getFloors = {
  283. [SCENE_TYPE.mesh]: async (scene: Scene) => {
  284. return getSceneApi(
  285. "oss",
  286. `/scene_view_data/${scene.m}/data/floorplan_cad.json?_=${Date.now()}`
  287. )
  288. .then((url) => fetch(url))
  289. .then((res) => res.json())
  290. .catch(() => ({ floors: [] }));
  291. },
  292. };
  293. export const lineGets = {
  294. [SCENE_TYPE.fuse]: async (scene: Scene) => {
  295. const tags = await taggingGets[SCENE_TYPE.fuse](scene, ["hot"]);
  296. return { name: "1楼", geos: [tags.map((item) => item.position)] };
  297. },
  298. [SCENE_TYPE.mesh]: async (scene: Scene, floorName?: string) => {
  299. const prev = `/scene_view_data/${scene.m}/data/`;
  300. const [{ floors }, bounds] = await Promise.all([
  301. getFloors[SCENE_TYPE.mesh](scene),
  302. getSceneApi("oss", `${prev}floorplan/info.json`)
  303. .then((url) => fetch(url))
  304. .then((res) => res.json())
  305. .then((data) => data.floors)
  306. .catch(() => []),
  307. ]);
  308. const data: any = [];
  309. const reqs = floors
  310. .filter((item: any) => !floorName || item.name === floorName)
  311. .map((floor: any) => {
  312. const bound = {
  313. ...(floor.cadInfo.cadBoundingBox || {}),
  314. ...(bounds.find((i: any) => i.subgroup === floor.subgroup)?.bound ||
  315. {}),
  316. };
  317. const item: any = {
  318. name: floor.name,
  319. subgroup: floor.subgroup,
  320. thumb: "",
  321. box: {
  322. bound: {
  323. ...bound,
  324. y_min: -bound.y_max,
  325. y_max: -bound.y_min,
  326. z_max: bound.z_max ? Number(bound.z_max) : bound.z_max,
  327. z_min: bound.z_min ? Number(bound.z_min) : bound.z_min,
  328. },
  329. rotate: floor.cadInfo.res,
  330. scale: floor.cadInfo.currentScale,
  331. },
  332. geos: extractConnectedSegments(floor.segment).map((geo) => {
  333. return geo.map((id) => {
  334. const p = floor["vertex-xy"].find((item: any) => item.id === id);
  335. return { x: p.x, y: -p.y } as Pos;
  336. });
  337. }),
  338. };
  339. data.push(item);
  340. return getSceneApi(
  341. "oss",
  342. `${prev}floorplan/floor_${floor.subgroup}.png`
  343. )
  344. .then((url) => (item.thumb = url))
  345. .catch(() => (item.thumb = ""));
  346. });
  347. await Promise.all(reqs);
  348. return data;
  349. },
  350. [SCENE_TYPE.cloud]: async (scene: Scene) => {
  351. const tags = await taggingGets[SCENE_TYPE.cloud](scene, ["hot"]);
  352. return { name: "1楼", geos: [tags.map((item) => item.position)] };
  353. },
  354. };