@ecogis/gis-web-ifc-three
Version:
This library is the implementation of [web-ifc](https://github.com/tomvandig/web-ifc) for [THREE.js](https://github.com/mrdoob/three.js/). This is the official IFCLoader of Three.js.
1 lines • 239 kB
Source Map (JSON)
{"version":3,"file":"IFCLoader.umd.cjs","sources":["../src/IFC/components/IFCModel.ts","../src/IFC/components/IFCParser.ts","../src/IFC/components/subsets/ItemsMap.ts","../src/IFC/components/subsets/SubsetUtils.ts","../src/IFC/components/subsets/SubsetCreator.ts","../src/IFC/components/subsets/SubsetManager.ts","../src/IFC/BaseDefinitions.ts","../src/IFC/components/properties/BasePropertyManager.ts","../src/IFC/components/properties/WebIfcPropertyManager.ts","../src/IFC/components/properties/JSONPropertyManager.ts","../src/IFC/components/properties/GeometryTypes.ts","../src/IFC/components/properties/PropertySerializer.ts","../src/IFC/components/properties/PropertyManager.ts","../src/IFC/components/TypeManager.ts","../src/IFC/components/BvhManager.ts","../src/IFC/web-workers/BaseDefinitions.ts","../src/IFC/web-workers/serializer/Vector.ts","../src/IFC/web-workers/serializer/IfcGeometry.ts","../src/IFC/web-workers/serializer/FlatMesh.ts","../src/IFC/web-workers/serializer/FlatMeshVector.ts","../src/IFC/web-workers/serializer/Material.ts","../src/IFC/web-workers/serializer/Geometry.ts","../src/IFC/web-workers/serializer/Mesh.ts","../src/IFC/web-workers/serializer/Serializer.ts","../src/IFC/web-workers/handlers/PropertyHandler.ts","../src/IFC/web-workers/handlers/WebIfcHandler.ts","../src/IFC/web-workers/handlers/WorkerStateHandler.ts","../src/IFC/indexedDB/IndexedDatabase.ts","../src/IFC/web-workers/handlers/ParserHandler.ts","../src/IFC/web-workers/IFCWorkerHandler.ts","../src/IFC/components/MemoryCleaner.ts","../src/IFC/components/IFCUtils.ts","../src/IFC/components/sequence/Data.ts","../src/IFC/components/IFCManager.ts","../src/IFCLoader.ts"],"sourcesContent":["import { BufferGeometry, Material, Mesh, Object3D, Scene } from 'three';\r\nimport { IFCManager } from './IFCManager';\r\nimport { BaseSubsetConfig } from '../BaseDefinitions';\r\n\r\nconst nullIfcManagerErrorMessage = 'IfcManager is null!';\r\n\r\n/**\r\n * Represents an IFC model. This object is returned by the `IFCLoader` after loading an IFC.\r\n * @geometry `THREE.Buffergeometry`, see Three.js documentation.\r\n * @materials `THREE.Material[]`, see Three.js documentation.\r\n * @manager contains all the logic to work with IFC.\r\n */\r\nexport class IFCModel extends Mesh {\r\n\r\n private static modelIdCounter = 0;\r\n\r\n static dispose() {\r\n IFCModel.modelIdCounter = 0;\r\n }\r\n\r\n modelID = IFCModel.modelIdCounter++;\r\n ifcManager: IFCManager | null = null;\r\n\r\n /**\r\n * @deprecated `IfcModel` is already a mesh; you can place it in the scene directly.\r\n */\r\n mesh = this;\r\n\r\n setIFCManager(manager: IFCManager) {\r\n this.ifcManager = manager;\r\n }\r\n\r\n /**\r\n * @deprecated Use `IfcModel.ifcManager.setWasmPath` instead.\r\n *\r\n * Sets the relative path of web-ifc.wasm file in the project.\r\n * Beware: you **must** serve this file in your page; this means\r\n * that you have to copy this files from *node_modules/web-ifc*\r\n * to your deployment directory.\r\n *\r\n * If you don't use this methods,\r\n * IFC.js assumes that you are serving it in the root directory.\r\n *\r\n * Example if web-ifc.wasm is in dist/wasmDir:\r\n * `ifcLoader.setWasmPath(\"dist/wasmDir/\");`\r\n *\r\n * @path Relative path to web-ifc.wasm.\r\n */\r\n setWasmPath(path: string) {\r\n if (this.ifcManager === null) throw new Error(nullIfcManagerErrorMessage);\r\n this.ifcManager.setWasmPath(path);\r\n }\r\n\r\n /**\r\n * @deprecated Use `IfcModel.ifcManager.close` instead.\r\n *\r\n * Closes the specified model and deletes it from the [scene](https://threejs.org/docs/#api/en/scenes/Scene).\r\n * @scene Scene where the model is (if it's located in a scene).\r\n */\r\n close(scene?: Scene) {\r\n if (this.ifcManager === null) throw new Error(nullIfcManagerErrorMessage);\r\n this.ifcManager.close(this.modelID, scene);\r\n }\r\n\r\n /**\r\n * @deprecated Use `IfcModel.ifcManager.getExpressId` instead.\r\n *\r\n * Gets the **Express ID** to which the given face belongs.\r\n * This ID uniquely identifies this entity within this IFC file.\r\n * @geometry The geometry of the IFC model.\r\n * @faceIndex The index of the face of a geometry.You can easily get this index using the [Raycaster](https://threejs.org/docs/#api/en/core/Raycaster).\r\n */\r\n getExpressId(geometry: BufferGeometry, faceIndex: number) {\r\n if (this.ifcManager === null) throw new Error(nullIfcManagerErrorMessage);\r\n return this.ifcManager.getExpressId(geometry, faceIndex);\r\n }\r\n\r\n /**\r\n * @deprecated Use `IfcModel.ifcManager.getAllItemsOfType` instead.\r\n *\r\n * Returns all items of the specified type. You can import\r\n * the types from *web-ifc*.\r\n *\r\n * Example to get all the standard walls of a project:\r\n * ```js\r\n * import { IFCWALLSTANDARDCASE } from 'web-ifc';\r\n * const walls = ifcLoader.getAllItemsOfType(IFCWALLSTANDARDCASE);\r\n * ```\r\n * @type The type of IFC items to get.\r\n * @verbose If false (default), this only gets IDs. If true, this also gets the native properties of all the fetched items.\r\n */\r\n getAllItemsOfType(type: number, verbose: boolean) {\r\n if (this.ifcManager === null) throw new Error(nullIfcManagerErrorMessage);\r\n return this.ifcManager.getAllItemsOfType(this.modelID, type, verbose);\r\n }\r\n\r\n /**\r\n * @deprecated Use `IfcModel.ifcManager.getItemProperties` instead.\r\n *\r\n * Gets the native properties of the given element.\r\n * @id Express ID of the element.\r\n * @recursive Wether you want to get the information of the referenced elements recursively.\r\n */\r\n getItemProperties(id: number, recursive = false) {\r\n if (this.ifcManager === null) throw new Error(nullIfcManagerErrorMessage);\r\n return this.ifcManager.getItemProperties(this.modelID, id, recursive);\r\n }\r\n\r\n /**\r\n * @deprecated Use `IfcModel.ifcManager.getPropertySets` instead.\r\n *\r\n * Gets the [property sets](https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifckernel/lexical/ifcpropertyset.htm)\r\n * assigned to the given element.\r\n * @id Express ID of the element.\r\n * @recursive If true, this gets the native properties of the referenced elements recursively.\r\n */\r\n getPropertySets(id: number, recursive = false) {\r\n if (this.ifcManager === null) throw new Error(nullIfcManagerErrorMessage);\r\n return this.ifcManager.getPropertySets(this.modelID, id, recursive);\r\n }\r\n\r\n /**\r\n * @deprecated Use `IfcModel.ifcManager.getTypeProperties` instead.\r\n *\r\n * Gets the properties of the type assigned to the element.\r\n * For example, if applied to a wall (IfcWall), this would get back the information\r\n * contained in the IfcWallType assigned to it, if any.\r\n * @id Express ID of the element.\r\n * @recursive If true, this gets the native properties of the referenced elements recursively.\r\n */\r\n getTypeProperties(id: number, recursive = false) {\r\n if (this.ifcManager === null) throw new Error(nullIfcManagerErrorMessage);\r\n return this.ifcManager.getTypeProperties(this.modelID, id, recursive);\r\n }\r\n\r\n /**\r\n * @deprecated Use `IfcModel.ifcManager.getIfcType` instead.\r\n *\r\n * Gets the ifc type of the specified item.\r\n * @id Express ID of the element.\r\n */\r\n getIfcType(id: number) {\r\n if (this.ifcManager === null) throw new Error(nullIfcManagerErrorMessage);\r\n return this.ifcManager.getIfcType(this.modelID, id);\r\n }\r\n\r\n /**\r\n * @deprecated Use `IfcModel.ifcManager.getSpatialStructure` instead.\r\n *\r\n * Gets the spatial structure of the project. The\r\n * [spatial structure](https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcproductextension/lexical/ifcspatialstructureelement.htm)\r\n * is the hierarchical structure that organizes every IFC project (all physical items\r\n * are referenced to an element of the spatial structure). It is formed by\r\n * one IfcProject that contains one or more IfcSites, that contain one or more\r\n * IfcBuildings, that contain one or more IfcBuildingStoreys, that contain\r\n * one or more IfcSpaces.\r\n */\r\n getSpatialStructure() {\r\n if (this.ifcManager === null) throw new Error(nullIfcManagerErrorMessage);\r\n return this.ifcManager.getSpatialStructure(this.modelID);\r\n }\r\n\r\n /**\r\n * @deprecated Use `IfcModel.ifcManager.getSubset` instead.\r\n *\r\n * Gets the mesh of the subset with the specified [material](https://threejs.org/docs/#api/en/materials/Material).\r\n * If no material is given, this returns the subset with the original materials.\r\n * @material Material assigned to the subset, if any.\r\n */\r\n getSubset(material?: Material) {\r\n if (this.ifcManager === null) throw new Error(nullIfcManagerErrorMessage);\r\n return this.ifcManager.getSubset(this.modelID, material);\r\n }\r\n\r\n /**\r\n * @deprecated Use `IfcModel.ifcManager.removeSubset` instead.\r\n *\r\n * Removes the specified subset.\r\n * @parent The parent where the subset is (can be any `THREE.Object3D`).\r\n * @material Material assigned to the subset, if any.\r\n */\r\n removeSubset(material?: Material, customID?: string) {\r\n if (this.ifcManager === null) throw new Error(nullIfcManagerErrorMessage);\r\n this.ifcManager.removeSubset(this.modelID, material, customID);\r\n }\r\n\r\n /**\r\n * @deprecated Use `IfcModel.ifcManager.createSubset` instead.\r\n *\r\n * Creates a new geometric subset.\r\n * @config A configuration object with the following options:\r\n * - **scene**: `THREE.Object3D` where the model is located.\r\n * - **ids**: Express IDs of the items of the model that will conform the subset.\r\n * - **removePrevious**: Wether to remove the previous subset of this model with this material.\r\n * - **material**: (optional) Wether to apply a material to the subset\r\n */\r\n createSubset(config: BaseSubsetConfig) {\r\n if (this.ifcManager === null) throw new Error(nullIfcManagerErrorMessage);\r\n const modelConfig = { ...config, modelID: this.modelID };\r\n return this.ifcManager.createSubset(modelConfig);\r\n }\r\n}\r\n","//@ts-ignore\r\nimport {\r\n PlacedGeometry,\r\n Color as ifcColor,\r\n IfcGeometry,\r\n IFCSPACE,\r\n FlatMesh,\r\n IFCOPENINGELEMENT,\r\n IFCPRODUCTDEFINITIONSHAPE\r\n} from 'web-ifc';\r\nimport { IfcState, IfcMesh } from '../BaseDefinitions';\r\nimport {\r\n Color,\r\n MeshLambertMaterial,\r\n DoubleSide,\r\n Matrix4,\r\n BufferGeometry,\r\n BufferAttribute,\r\n Mesh\r\n} from 'three';\r\nimport { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils';\r\nimport { BvhManager } from './BvhManager';\r\nimport { IFCModel } from './IFCModel';\r\n\r\nexport interface ParserProgress {\r\n loaded: number;\r\n total: number;\r\n}\r\n\r\nexport interface OptionalCategories {\r\n [category: number]: boolean\r\n}\r\n\r\nexport interface ParserAPI {\r\n parse(buffer: any, coordinationMatrix?: number[]): Promise<IFCModel>;\r\n\r\n getAndClearErrors(_modelId: number): void;\r\n\r\n setupOptionalCategories(config: OptionalCategories): Promise<void>;\r\n\r\n optionalCategories: OptionalCategories;\r\n}\r\n\r\nexport interface GeometriesByMaterial {\r\n [materialID: string]: {\r\n material: MeshLambertMaterial,\r\n geometries: BufferGeometry[]\r\n }\r\n}\r\n\r\n/**\r\n * Reads all the geometry of the IFC file and generates an optimized `THREE.Mesh`.\r\n */\r\nexport class IFCParser implements ParserAPI {\r\n loadedModels = 0;\r\n\r\n optionalCategories: OptionalCategories = {\r\n [IFCSPACE]: true,\r\n [IFCOPENINGELEMENT]: false\r\n };\r\n\r\n private geometriesByMaterials: GeometriesByMaterial = {};\r\n\r\n private loadingState = {\r\n total: 0,\r\n current: 0,\r\n step: 0.1\r\n }\r\n\r\n // Represents the index of the model in webIfcAPI\r\n private currentWebIfcID = -1;\r\n // When using JSON data for optimization, webIfcAPI is reinitialized every time a model is loaded\r\n // This means that currentID is always 0, while currentModelID is the real index of stored models\r\n private currentModelID = -1;\r\n\r\n // BVH is optional because when using workers we have to apply it in the main thread,\r\n // once the model has been serialized and reconstructed\r\n constructor(private state: IfcState, private BVH?: BvhManager) {\r\n }\r\n\r\n async setupOptionalCategories(config: OptionalCategories) {\r\n this.optionalCategories = config;\r\n }\r\n\r\n async parse(buffer: any, coordinationMatrix?: number[]) {\r\n if (this.state.api.wasmModule === undefined) await this.state.api.Init();\r\n await this.newIfcModel(buffer);\r\n this.loadedModels++;\r\n if (coordinationMatrix) {\r\n await this.state.api.SetGeometryTransformation(this.currentWebIfcID, coordinationMatrix);\r\n }\r\n return this.loadAllGeometry(this.currentWebIfcID);\r\n }\r\n\r\n getAndClearErrors(_modelId: number) {\r\n // return this.state.api.GetAndClearErrors(modelId);\r\n }\r\n\r\n private notifyProgress(loaded: number, total: number) {\r\n if (this.state.onProgress) this.state.onProgress({ loaded, total });\r\n }\r\n\r\n private async newIfcModel(buffer: any) {\r\n const data = new Uint8Array(buffer);\r\n this.currentWebIfcID = await this.state.api.OpenModel(data, this.state.webIfcSettings);\r\n this.currentModelID = this.state.useJSON ? this.loadedModels : this.currentWebIfcID;\r\n this.state.models[this.currentModelID] = {\r\n modelID: this.currentModelID,\r\n mesh: {} as IfcMesh,\r\n types: {},\r\n jsonData: {}\r\n };\r\n }\r\n\r\n private async loadAllGeometry(modelID: number) {\r\n this.addOptionalCategories(modelID);\r\n await this.initializeLoadingState(modelID);\r\n\r\n this.state.api.StreamAllMeshes(modelID, (mesh: FlatMesh) => {\r\n this.updateLoadingState();\r\n // only during the lifetime of this function call, the geometry is available in memory\r\n this.streamMesh(modelID, mesh);\r\n });\r\n\r\n this.notifyLoadingEnded();\r\n const geometries: BufferGeometry[] = [];\r\n const materials: MeshLambertMaterial[] = [];\r\n\r\n Object.keys(this.geometriesByMaterials).forEach((key) => {\r\n const geometriesByMaterial = this.geometriesByMaterials[key].geometries;\r\n const merged = mergeBufferGeometries(geometriesByMaterial);\r\n materials.push(this.geometriesByMaterials[key].material);\r\n geometries.push(merged);\r\n });\r\n\r\n const combinedGeometry = mergeBufferGeometries(geometries, true);\r\n this.cleanUpGeometryMemory(geometries);\r\n if (this.BVH) this.BVH.applyThreeMeshBVH(combinedGeometry);\r\n const model = new IFCModel(combinedGeometry, materials);\r\n this.state.models[this.currentModelID].mesh = model;\r\n return model;\r\n }\r\n\r\n private async initializeLoadingState(modelID: number) {\r\n const shapes = await this.state.api.GetLineIDsWithType(modelID, IFCPRODUCTDEFINITIONSHAPE);\r\n this.loadingState.total = shapes.size();\r\n this.loadingState.current = 0;\r\n this.loadingState.step = 0.1;\r\n }\r\n\r\n private notifyLoadingEnded() {\r\n this.notifyProgress(this.loadingState.total, this.loadingState.total);\r\n }\r\n\r\n private updateLoadingState() {\r\n const realCurrentItem = Math.min(this.loadingState.current++, this.loadingState.total);\r\n if(realCurrentItem / this.loadingState.total >= this.loadingState.step) {\r\n const currentProgress = Math.ceil(this.loadingState.total * this.loadingState.step);\r\n this.notifyProgress(currentProgress, this.loadingState.total);\r\n this.loadingState.step += 0.1;\r\n }\r\n }\r\n\r\n // Some categories (like IfcSpace and IfcOpeningElement) need to be set explicitly\r\n private addOptionalCategories(modelID: number) {\r\n\r\n const optionalTypes: number[] = [];\r\n\r\n for (let key in this.optionalCategories) {\r\n if (this.optionalCategories.hasOwnProperty(key)) {\r\n const category = parseInt(key);\r\n if (this.optionalCategories[category]) optionalTypes.push(category);\r\n }\r\n }\r\n\r\n this.state.api.StreamAllMeshesWithTypes(this.currentWebIfcID, optionalTypes, (mesh: FlatMesh) => {\r\n this.streamMesh(modelID, mesh);\r\n });\r\n }\r\n\r\n private streamMesh(modelID: number, mesh: FlatMesh) {\r\n const placedGeometries = mesh.geometries;\r\n const size = placedGeometries.size();\r\n\r\n for (let i = 0; i < size; i++) {\r\n const placedGeometry = placedGeometries.get(i);\r\n let itemMesh = this.getPlacedGeometry(modelID, mesh.expressID, placedGeometry);\r\n let geom = itemMesh.geometry.applyMatrix4(itemMesh.matrix);\r\n this.storeGeometryByMaterial(placedGeometry.color, geom);\r\n }\r\n }\r\n\r\n private getPlacedGeometry(modelID: number, expressID: number, placedGeometry: PlacedGeometry) {\r\n const geometry = this.getBufferGeometry(modelID, expressID, placedGeometry);\r\n const mesh = new Mesh(geometry);\r\n mesh.matrix = this.getMeshMatrix(placedGeometry.flatTransformation);\r\n mesh.matrixAutoUpdate = false;\r\n return mesh;\r\n }\r\n\r\n private getBufferGeometry(modelID: number, expressID: number, placedGeometry: PlacedGeometry) {\r\n const geometry = this.state.api.GetGeometry(modelID, placedGeometry.geometryExpressID) as IfcGeometry;\r\n const verts = this.state.api.GetVertexArray(geometry.GetVertexData(), geometry.GetVertexDataSize()) as Float32Array;\r\n const indices = this.state.api.GetIndexArray(geometry.GetIndexData(), geometry.GetIndexDataSize()) as Uint32Array;\r\n const buffer = this.ifcGeometryToBuffer(expressID, verts, indices);\r\n //@ts-ignore\r\n geometry.delete();\r\n return buffer;\r\n }\r\n\r\n private storeGeometryByMaterial(color: ifcColor, geometry: BufferGeometry) {\r\n let colID = `${color.x}${color.y}${color.z}${color.w}`;\r\n if (this.geometriesByMaterials[colID]) {\r\n this.geometriesByMaterials[colID].geometries.push(geometry);\r\n return;\r\n }\r\n\r\n // Assume RGB components are in sRGB-Rec709-D65 colorspace, and specify\r\n // this so three.js can convert if THREE.ColorManagement APIs are enabled.\r\n const col = new Color().setRGB(color.x, color.y, color.z, 'srgb');\r\n const material = new MeshLambertMaterial({ color: col, side: DoubleSide });\r\n material.transparent = color.w !== 1;\r\n if (material.transparent) material.opacity = color.w;\r\n this.geometriesByMaterials[colID] = { material, geometries: [geometry] };\r\n }\r\n\r\n private getMeshMatrix(matrix: Array<number>) {\r\n const mat = new Matrix4();\r\n mat.fromArray(matrix);\r\n return mat;\r\n }\r\n\r\n private ifcGeometryToBuffer(expressID: number, vertexData: Float32Array, indexData: Uint32Array) {\r\n const geometry = new BufferGeometry();\r\n\r\n const posFloats = new Float32Array(vertexData.length / 2);\r\n const normFloats = new Float32Array(vertexData.length / 2);\r\n const idAttribute = new Uint32Array(vertexData.length / 6);\r\n\r\n for (let i = 0; i < vertexData.length; i += 6) {\r\n posFloats[i / 2] = vertexData[i];\r\n posFloats[i / 2 + 1] = vertexData[i + 1];\r\n posFloats[i / 2 + 2] = vertexData[i + 2];\r\n\r\n normFloats[i / 2] = vertexData[i + 3];\r\n normFloats[i / 2 + 1] = vertexData[i + 4];\r\n normFloats[i / 2 + 2] = vertexData[i + 5];\r\n\r\n idAttribute[i / 6] = expressID;\r\n }\r\n\r\n geometry.setAttribute(\r\n 'position',\r\n new BufferAttribute(posFloats, 3));\r\n geometry.setAttribute(\r\n 'normal',\r\n new BufferAttribute(normFloats, 3));\r\n geometry.setAttribute(\r\n 'expressID',\r\n new BufferAttribute(idAttribute, 1));\r\n\r\n geometry.setIndex(new BufferAttribute(indexData, 1));\r\n return geometry;\r\n }\r\n\r\n // Three.js geometry has to be manually deallocated\r\n private cleanUpGeometryMemory(geometries: BufferGeometry[]) {\r\n geometries.forEach(geometry => geometry.dispose());\r\n\r\n Object.keys(this.geometriesByMaterials).forEach((materialID) => {\r\n const geometriesByMaterial = this.geometriesByMaterials[materialID];\r\n geometriesByMaterial.geometries.forEach(geometry => geometry.dispose());\r\n geometriesByMaterial.geometries = [];\r\n // @ts-ignore\r\n geometriesByMaterial.material = null;\r\n });\r\n this.geometriesByMaterials = {};\r\n }\r\n}\r\n","import { IfcState } from '../../BaseDefinitions';\r\nimport { BufferAttribute, BufferGeometry, Material } from 'three';\r\n\r\n// The number array has the meaning: [start, end, start, end, start, end...]\r\nexport interface Indices {\r\n [materialID: number]: number[]\r\n}\r\n\r\nexport interface IndexedGeometry extends BufferGeometry {\r\n index: BufferAttribute;\r\n}\r\n\r\nexport interface Group {\r\n start: number,\r\n count: number,\r\n materialIndex?: number\r\n}\r\n\r\nexport interface Items {\r\n indexCache: Uint32Array,\r\n map: Map<number, Indices>\r\n}\r\n\r\nexport interface IndicesMap {\r\n [modelID: number]: {\r\n indexCache: Uint32Array;\r\n map: Map<number, Indices>;\r\n }\r\n}\r\n\r\nexport class ItemsMap {\r\n\r\n constructor(private state: IfcState) {\r\n }\r\n\r\n map: IndicesMap = {};\r\n\r\n generateGeometryIndexMap(modelID: number) {\r\n if (this.map[modelID]) return;\r\n const geometry = this.getGeometry(modelID);\r\n const items = this.newItemsMap(modelID, geometry);\r\n for (const group of geometry.groups) {\r\n this.fillItemsWithGroupInfo(group, geometry, items);\r\n }\r\n }\r\n\r\n getSubsetID(modelID: number, material?: Material, customID = 'DEFAULT') {\r\n const baseID = modelID;\r\n const materialID = material ? material.uuid : 'DEFAULT';\r\n return `${baseID} - ${materialID} - ${customID}`;\r\n }\r\n\r\n // Use this only for destroying the current IFCLoader instance\r\n dispose() {\r\n Object.values(this.map).forEach(model => {\r\n (model.indexCache as any) = null;\r\n (model.map as any) = null;\r\n });\r\n\r\n (this.map as any) = null;\r\n }\r\n\r\n private getGeometry(modelID: number) {\r\n const geometry = this.state.models[modelID].mesh.geometry;\r\n if (!geometry) throw new Error('Model without geometry.');\r\n if (!geometry.index) throw new Error('Geometry must be indexed');\r\n return geometry as IndexedGeometry;\r\n }\r\n\r\n private newItemsMap(modelID: number, geometry: IndexedGeometry) {\r\n const startIndices = geometry.index.array as Uint32Array;\r\n this.map[modelID] = {\r\n indexCache: startIndices.slice(0, geometry.index.array.length),\r\n map: new Map()\r\n };\r\n return this.map[modelID] as Items;\r\n }\r\n\r\n private fillItemsWithGroupInfo(group: Group, geometry: IndexedGeometry, items: Items) {\r\n let prevExpressID = -1;\r\n\r\n const materialIndex = group.materialIndex as number;\r\n const materialStart = group.start;\r\n const materialEnd = materialStart + group.count - 1;\r\n\r\n let objectStart = -1;\r\n let objectEnd = -1;\r\n\r\n for (let i = materialStart; i <= materialEnd; i++) {\r\n const index = geometry.index.array[i];\r\n const bufferAttr = geometry.attributes.expressID as BufferAttribute;\r\n const expressID = bufferAttr.array[index];\r\n\r\n // First iteration\r\n if (prevExpressID === -1) {\r\n prevExpressID = expressID;\r\n objectStart = i;\r\n }\r\n\r\n // It's the end of the material, which also means end of the object\r\n const isEndOfMaterial = i === materialEnd;\r\n if (isEndOfMaterial) {\r\n const store = this.getMaterialStore(items.map, expressID, materialIndex);\r\n store.push(objectStart, materialEnd);\r\n break;\r\n }\r\n\r\n // Still going through the same object\r\n if (prevExpressID === expressID) continue;\r\n\r\n // New object starts; save previous object\r\n\r\n // Store previous object\r\n const store = this.getMaterialStore(items.map, prevExpressID, materialIndex);\r\n objectEnd = i - 1;\r\n store.push(objectStart, objectEnd);\r\n\r\n // Get ready to process next object\r\n prevExpressID = expressID;\r\n objectStart = i;\r\n }\r\n }\r\n\r\n private getMaterialStore(map: Map<number, Indices>, id: number, matIndex: number) {\r\n // If this object wasn't store before, add it to the map\r\n if (map.get(id) === undefined) {\r\n map.set(id, {});\r\n }\r\n const storedIfcItem = map.get(id);\r\n if (storedIfcItem === undefined) throw new Error('Geometry map generation error');\r\n\r\n // If this material wasn't stored for this object before, add it to the object\r\n if (storedIfcItem[matIndex] === undefined) {\r\n storedIfcItem[matIndex] = [];\r\n }\r\n return storedIfcItem[matIndex];\r\n }\r\n\r\n}","export class SubsetUtils {\r\n\r\n // If flatten, all indices are in the same array; otherwise, indices are split in subarrays by material\r\n static getAllIndicesOfGroup(modelID: number, ids: number[], materialIndex: number, items: any, flatten = true) {\r\n const indicesByGroup: any = [];\r\n for (const expressID of ids) {\r\n const entry = items.map.get(expressID);\r\n if (!entry) continue;\r\n const value = entry[materialIndex];\r\n if (!value) continue;\r\n SubsetUtils.getIndexChunk(value, indicesByGroup, materialIndex, items, flatten);\r\n }\r\n return indicesByGroup;\r\n }\r\n\r\n private static getIndexChunk(value: number[], indicesByGroup: any, materialIndex: number, items: any, flatten: boolean) {\r\n const pairs = value.length / 2;\r\n for (let pair = 0; pair < pairs; pair++) {\r\n const pairIndex = pair * 2;\r\n const start = value[pairIndex];\r\n const end = value[pairIndex + 1];\r\n for (let j = start; j <= end; j++) {\r\n if(flatten) indicesByGroup.push(items.indexCache[j]);\r\n else {\r\n if (!indicesByGroup[materialIndex]) indicesByGroup[materialIndex] = [];\r\n indicesByGroup[materialIndex].push(items.indexCache[j]);\r\n }\r\n }\r\n }\r\n }\r\n}","import { BufferGeometry, Mesh } from 'three';\r\nimport { IfcState, SubsetConfig } from '../../BaseDefinitions';\r\nimport { IndexedGeometry, ItemsMap } from './ItemsMap';\r\nimport { Subset, Subsets } from './SubsetManager';\r\nimport { SubsetUtils } from './SubsetUtils';\r\nimport { BvhManager } from '../BvhManager';\r\n\r\nexport class SubsetCreator {\r\n\r\n private tempIndex: number[] = [];\r\n\r\n constructor(private state: IfcState, private items: ItemsMap, private subsets: Subsets, private BVH: BvhManager) {\r\n }\r\n\r\n createSubset(config: SubsetConfig, subsetID: string) {\r\n if (!this.items.map[config.modelID]) this.items.generateGeometryIndexMap(config.modelID);\r\n if (!this.subsets[subsetID]) this.initializeSubset(config, subsetID);\r\n this.filterIndices(config, subsetID);\r\n this.constructSubsetByMaterial(config, subsetID);\r\n config.ids.forEach(id => this.subsets[subsetID].ids.add(id));\r\n this.subsets[subsetID].mesh.geometry.setIndex(this.tempIndex);\r\n this.tempIndex.length = 0;\r\n const subset = this.subsets[subsetID].mesh;\r\n if (config.applyBVH) this.BVH.applyThreeMeshBVH(subset.geometry);\r\n if (config.scene) config.scene.add(subset);\r\n return this.subsets[subsetID].mesh;\r\n }\r\n\r\n dispose() {\r\n this.tempIndex = [];\r\n }\r\n\r\n private initializeSubset(config: SubsetConfig, subsetID: string) {\r\n const model = this.state.models[config.modelID].mesh;\r\n const subsetGeom = new BufferGeometry();\r\n this.initializeSubsetAttributes(subsetGeom, model);\r\n if (!config.material) this.initializeSubsetGroups(subsetGeom, model);\r\n const mesh = new Mesh(subsetGeom, config.material || model.material) as Subset;\r\n mesh.modelID = config.modelID;\r\n const bvh = Boolean(config.applyBVH);\r\n this.subsets[subsetID] = { ids: new Set<number>(), mesh, bvh };\r\n model.add(mesh);\r\n }\r\n\r\n // The subset shares the same attributes as the original (no memory consumed)\r\n private initializeSubsetAttributes(subsetGeom: BufferGeometry, model: Mesh) {\r\n subsetGeom.setAttribute('position', model.geometry.attributes.position);\r\n subsetGeom.setAttribute('normal', model.geometry.attributes.normal);\r\n subsetGeom.setAttribute('expressID', model.geometry.attributes.expressID);\r\n subsetGeom.setIndex([]);\r\n }\r\n\r\n // If the subset has original materials, initialize the groups for the subset\r\n private initializeSubsetGroups(subsetGeom: BufferGeometry, model: Mesh) {\r\n subsetGeom.groups = JSON.parse(JSON.stringify(model.geometry.groups));\r\n this.resetGroups(subsetGeom);\r\n }\r\n\r\n // Remove previous indices or filter the given ones to avoid repeating items\r\n private filterIndices(config: SubsetConfig, subsetID: string) {\r\n const geometry = this.subsets[subsetID].mesh.geometry as IndexedGeometry;\r\n if (config.removePrevious) {\r\n geometry.setIndex([]);\r\n this.resetGroups(geometry);\r\n return;\r\n }\r\n const previousIndices = geometry.index.array;\r\n const previousIDs = this.subsets[subsetID].ids;\r\n config.ids = config.ids.filter(id => !previousIDs.has(id));\r\n this.tempIndex = Array.from(previousIndices);\r\n }\r\n\r\n private constructSubsetByMaterial(config: SubsetConfig, subsetID: string) {\r\n const model = this.state.models[config.modelID].mesh;\r\n const newIndices = { count: 0 };\r\n for (let i = 0; i < model.geometry.groups.length; i++) {\r\n this.insertNewIndices(config, subsetID, i, newIndices);\r\n }\r\n }\r\n\r\n // If this subset has original materials, insert indices in correct position and update groups\r\n // Otherwise, just insert indices at any position\r\n private insertNewIndices(config: SubsetConfig, subsetID: string, materialIndex: number, newIndices: any) {\r\n const items = this.items.map[config.modelID];\r\n const indicesOfOneMaterial = SubsetUtils.getAllIndicesOfGroup(config.modelID, config.ids, materialIndex, items) as number[];\r\n\r\n if (!config.material) {\r\n this.insertIndicesAtGroup(subsetID, indicesOfOneMaterial, materialIndex, newIndices);\r\n } else {\r\n indicesOfOneMaterial.forEach(index => this.tempIndex.push(index));\r\n }\r\n }\r\n\r\n private insertIndicesAtGroup(subsetID: string, indicesByGroup: number[], index: number, newIndices: any) {\r\n const currentGroup = this.getCurrentGroup(subsetID, index);\r\n currentGroup.start += newIndices.count;\r\n let newIndicesPosition = currentGroup.start + currentGroup.count;\r\n newIndices.count += indicesByGroup.length;\r\n if (indicesByGroup.length > 0) {\r\n let position = newIndicesPosition;\r\n const start = this.tempIndex.slice(0, position);\r\n const end = this.tempIndex.slice(position);\r\n this.tempIndex = Array.prototype.concat.apply([], [start, indicesByGroup, end]);\r\n currentGroup.count += indicesByGroup.length;\r\n }\r\n }\r\n\r\n private getCurrentGroup(subsetID: string, groupIndex: number) {\r\n const geometry = this.subsets[subsetID].mesh.geometry as IndexedGeometry;\r\n return geometry.groups[groupIndex];\r\n }\r\n\r\n private resetGroups(geometry: BufferGeometry) {\r\n geometry.groups.forEach((group) => {\r\n group.start = 0;\r\n group.count = 0;\r\n });\r\n }\r\n}","import { Material, Mesh, Object3D } from 'three';\r\nimport { SubsetConfig, IfcState } from '../../BaseDefinitions';\r\nimport { BvhManager } from '../BvhManager';\r\nimport { ItemsMap } from './ItemsMap';\r\nimport { SubsetCreator } from './SubsetCreator';\r\n\r\nexport interface Subset extends Mesh {\r\n modelID: number;\r\n}\r\n\r\nexport type Subsets = {\r\n [subsetID: string]: { ids: Set<number>, mesh: Subset, bvh: boolean };\r\n};\r\n\r\n/**\r\n * Contains the logic to get, create and delete geometric subsets of an IFC model. For example,\r\n * this can extract all the items in a specific IfcBuildingStorey and create a new Mesh.\r\n */\r\nexport class SubsetManager {\r\n readonly items: ItemsMap;\r\n private readonly BVH: BvhManager;\r\n private state: IfcState;\r\n private subsets: Subsets = {};\r\n private subsetCreator: SubsetCreator;\r\n\r\n constructor(state: IfcState, BVH: BvhManager) {\r\n this.state = state;\r\n this.items = new ItemsMap(state);\r\n this.BVH = BVH;\r\n this.subsetCreator = new SubsetCreator(state, this.items, this.subsets, this.BVH);\r\n }\r\n\r\n getAllSubsets(){\r\n return this.subsets\r\n }\r\n\r\n getSubset(modelID: number, material?: Material, customId?: string) {\r\n const subsetID = this.getSubsetID(modelID, material, customId);\r\n return this.subsets[subsetID].mesh;\r\n }\r\n\r\n removeSubset(modelID: number, material?: Material, customID?: string) {\r\n const subsetID = this.getSubsetID(modelID, material, customID);\r\n const subset = this.subsets[subsetID];\r\n if (!subset) return;\r\n if (subset.mesh.parent) subset.mesh.removeFromParent();\r\n subset.mesh.geometry.attributes = {};\r\n subset.mesh.geometry.index = null;\r\n subset.mesh.geometry.dispose();\r\n // @ts-ignore\r\n subset.mesh.geometry = null;\r\n delete this.subsets[subsetID];\r\n }\r\n\r\n createSubset(config: SubsetConfig) {\r\n const subsetID = this.getSubsetID(config.modelID, config.material, config.customID);\r\n return this.subsetCreator.createSubset(config, subsetID);\r\n }\r\n\r\n removeFromSubset(modelID: number, ids: number[], customID?: string, material?: Material) {\r\n const subsetID = this.getSubsetID(modelID, material, customID);\r\n if (!this.subsets[subsetID]) return;\r\n\r\n const previousIDs = this.subsets[subsetID].ids;\r\n ids.forEach((id) => {\r\n if(previousIDs.has(id)) previousIDs.delete(id);\r\n })\r\n\r\n return this.createSubset({\r\n modelID,\r\n removePrevious: true,\r\n material,\r\n customID,\r\n applyBVH: this.subsets[subsetID].bvh,\r\n ids: Array.from(previousIDs),\r\n scene: this.subsets[subsetID].mesh.parent as Object3D\r\n });\r\n }\r\n\r\n clearSubset(modelID: number, customID?: string, material?: Material) {\r\n const subsetID = this.getSubsetID(modelID, material, customID);\r\n if (!this.subsets[subsetID]) return;\r\n this.subsets[subsetID].ids.clear();\r\n const subset = this.getSubset(modelID, material, customID);\r\n subset.geometry.setIndex([]);\r\n }\r\n\r\n // Use this only for destroying the current IFCLoader instance\r\n dispose() {\r\n this.items.dispose();\r\n this.subsetCreator.dispose();\r\n\r\n Object.values(this.subsets).forEach(subset => {\r\n (subset.ids as any) = null;\r\n subset.mesh.removeFromParent();\r\n const mats = subset.mesh.material;\r\n if(Array.isArray(mats)) mats.forEach(mat => mat.dispose());\r\n else mats.dispose();\r\n subset.mesh.geometry.index = null;\r\n subset.mesh.geometry.dispose();\r\n const geom = subset.mesh.geometry as any;\r\n if(geom.disposeBoundsTree) geom.disposeBoundsTree();\r\n (subset.mesh as any) = null;\r\n });\r\n (this.subsets as any) = null;\r\n }\r\n\r\n private getSubsetID(modelID: number, material?: Material, customID = 'DEFAULT') {\r\n const baseID = modelID;\r\n const materialID = material ? material.uuid : 'DEFAULT';\r\n return `${baseID} - ${materialID} - ${customID}`;\r\n }\r\n}\r\n","import { BufferAttribute, BufferGeometry, Material, Matrix4, Mesh, Object3D } from 'three';\r\n// TODO: Remove ts ignore comments when @types/three gets updated\r\n// @ts-ignore\r\nimport { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils';\r\nimport {\r\n FlatMesh,\r\n IfcGeometry,\r\n IFCRELAGGREGATES, IFCRELASSOCIATESMATERIAL,\r\n IFCRELCONTAINEDINSPATIALSTRUCTURE,\r\n IFCRELDEFINESBYPROPERTIES,\r\n IFCRELDEFINESBYTYPE, LoaderError, LoaderSettings, RawLineData, Vector\r\n} from 'web-ifc';\r\nimport {ParserProgress} from \"./components/IFCParser\";\r\n\r\nexport const IdAttrName = 'expressID';\r\n\r\nexport type IdAttributeByMaterial = { [id: number]: number };\r\nexport type IdAttributesByMaterials = { [materialID: string]: IdAttributeByMaterial };\r\n\r\n//TODO: Rename \"scene\" to \"parent\" in the next major release\r\nexport interface BaseSubsetConfig {\r\n scene?: Object3D;\r\n ids: number[];\r\n removePrevious: boolean;\r\n material?: Material;\r\n customID?: string;\r\n applyBVH?: boolean;\r\n}\r\n\r\nexport interface SubsetConfig extends BaseSubsetConfig {\r\n modelID: number;\r\n}\r\n\r\nexport const DEFAULT = 'default';\r\n\r\nexport type MapFaceindexID = { [key: number]: number };\r\n\r\nexport interface TypesMap {\r\n [key: number]: number;\r\n}\r\n\r\n\r\nexport interface IfcModel {\r\n modelID: number;\r\n mesh: IfcMesh;\r\n types: TypesMap;\r\n jsonData: { [id: number]: JSONObject };\r\n}\r\n\r\nexport interface JSONObject {\r\n expressID: number;\r\n type: string;\r\n [key: string]: any;\r\n}\r\n\r\nexport interface Worker {\r\n active: boolean;\r\n path: string;\r\n}\r\n\r\nexport interface IfcState {\r\n models: { [modelID: number]: IfcModel };\r\n api: WebIfcAPI;\r\n useJSON: boolean;\r\n worker: Worker;\r\n webIfcSettings?: LoaderSettings;\r\n onProgress?: (event: ParserProgress) => void;\r\n coordinationMatrix?: Matrix4,\r\n wasmPath?: string;\r\n}\r\n\r\nexport interface IfcMesh extends Mesh {\r\n modelID: number;\r\n}\r\n\r\nexport interface Node {\r\n expressID: number;\r\n type: string;\r\n children: Node[];\r\n}\r\n\r\nexport interface pName {\r\n name: number;\r\n relating: string;\r\n related: string;\r\n key: string;\r\n}\r\n\r\nexport interface NewIfcModel {\r\n schema: string;\r\n name?: string;\r\n description?: string[];\r\n authors?: string[];\r\n organizations?: string[];\r\n authorization?: string;\r\n}\r\n\r\nexport const PropsNames = {\r\n aggregates: {\r\n name: IFCRELAGGREGATES,\r\n relating: 'RelatingObject',\r\n related: 'RelatedObjects',\r\n key: 'children'\r\n },\r\n spatial: {\r\n name: IFCRELCONTAINEDINSPATIALSTRUCTURE,\r\n relating: 'RelatingStructure',\r\n related: 'RelatedElements',\r\n key: 'children'\r\n },\r\n psets: {\r\n name: IFCRELDEFINESBYPROPERTIES,\r\n relating: 'RelatingPropertyDefinition',\r\n related: 'RelatedObjects',\r\n key: 'hasPsets'\r\n },\r\n materials: {\r\n name: IFCRELASSOCIATESMATERIAL,\r\n relating: 'RelatingMaterial',\r\n related: 'RelatedObjects',\r\n key: 'hasMaterial'\r\n },\r\n type: {\r\n name: IFCRELDEFINESBYTYPE,\r\n relating: 'RelatingType',\r\n related: 'RelatedObjects',\r\n key: 'hasType'\r\n }\r\n};\r\n\r\nexport interface WebIfcAPI {\r\n\r\n wasmModule: any;\r\n\r\n Init(): void | Promise<void>;\r\n\r\n // To close the web worker\r\n Close?: () => void;\r\n\r\n /**\r\n * Opens a model and returns a modelID number\r\n * @data Buffer containing IFC data (bytes)\r\n * @data Settings settings for loading the model\r\n */\r\n OpenModel(data: string | Uint8Array, settings?: LoaderSettings): number | Promise<number>;\r\n\r\n GetHeaderLine(modelID: number, headerType: number): any | Promise<any>;\r\n\r\n /**\r\n * Creates a new model and returns a modelID number\r\n * @data Settings settings for generating data the model\r\n */\r\n CreateModel(model: NewIfcModel, settings?: LoaderSettings): number | Promise<number>;\r\n\r\n ExportFileAsIFC(modelID: number): Uint8Array | Promise<Uint8Array>;\r\n\r\n /**\r\n * Opens a model and returns a modelID number\r\n * @modelID Model handle retrieved by OpenModel, model must not be closed\r\n * @data Buffer containing IFC data (bytes)\r\n */\r\n GetGeometry(modelID: number, geometryExpressID: number): IfcGeometry | Promise<IfcGeometry>;\r\n\r\n GetLine(modelID: number, expressID: number, flatten?: boolean): any | Promise<any>;\r\n\r\n GetAndClearErrors(modelID: number): Vector<LoaderError> | Promise<Vector<LoaderError>>;\r\n\r\n WriteLine(modelID: number, lineObject: any): void | Promise<void>;\r\n\r\n FlattenLine(modelID: number, line: any): void | Promise<void>;\r\n\r\n GetRawLineData(modelID: number, expressID: number): RawLineData | Promise<RawLineData>;\r\n\r\n WriteRawLineData(modelID: number, data: RawLineData): any | Promise<any>;\r\n\r\n GetLineIDsWithType(modelID: number, type: number): Vector<number> | Promise<Vector<number>>;\r\n\r\n GetAllLines(modelID: Number): Vector<number> | Promise<Vector<number>>;\r\n\r\n SetGeometryTransformation(modelID: number, transformationMatrix: Array<number>): void | Promise<void>;\r\n\r\n GetCoordinationMatrix(modelID: number): Array<number> | Promise<Array<number>>;\r\n\r\n GetVertexArray(ptr: number, size: number): Float32Array | Promise<Float32Array>;\r\n\r\n GetIndexArray(ptr: number, size: number): Uint32Array | Promise<Uint32Array>;\r\n\r\n GetNameFromTypeCode(type:number): string | Promise<string>;\r\n\r\n GetTypeCodeFromName(modelID: number,typeName:string): number | Promise<number>;\r\n\r\n GetIfcEntityList(modelID: number) : Array<number> | Promise<Array<number>>;\r\n\r\n getSubArray(heap: any, startPtr: any, sizeBytes: any): any | Promise<any>;\r\n\r\n /**\r\n * Closes a model and frees all related memory\r\n * @modelID Model handle retrieved by OpenModel, model must not be closed\r\n */\r\n CloseModel(modelID: number): void | Promise<void>;\r\n\r\n StreamAllMeshes(modelID: number, meshCallback: (mesh: FlatMesh) => void): void | Promise<void>;\r\n\r\n StreamAllMeshesWithTypes(modelID: number, types: Array<number>, meshCallback: (mesh: FlatMesh) => void): void | Promise<void>;\r\n\r\n /**\r\n * Checks if a specific model ID is open or closed\r\n * @modelID Model handle retrieved by OpenModel\r\n */\r\n IsModelOpen(modelID: number): boolean | Promise<boolean>;\r\n\r\n /**\r\n * Load all geometry in a model\r\n * @modelID Model handle retrieved by OpenModel\r\n */\r\n LoadAllGeometry(modelID: number): Vector<FlatMesh> | Promise<Vector<FlatMesh>>;\r\n\r\n /**\r\n * Load geometry for a single element\r\n * @modelID Model handle retrieved by OpenModel\r\n */\r\n GetFlatMesh(modelID: number, expressID: number): FlatMesh | Promise<FlatMesh>;\r\n\r\n SetWasmPath(path: string): void | Promise<void>;\r\n}\r\n","import { IfcState, pName, PropsNames, Node } from '../../BaseDefinitions';\r\n\r\nexport class BasePropertyManager {\r\n\r\n constructor(protected state: IfcState) {\r\n }\r\n\r\n async getPropertySets(modelID: number, elementID: number, recursive = false) {\r\n return await this.getProperty(modelID, elementID, recursive, PropsNames.psets);\r\n }\r\n\r\n async getTypeProperties(modelID: number, elementID: number, recursive = false) {\r\n return await this.getProperty(modelID, elementID, recursive, PropsNames.type);\r\n }\r\n\r\n async getMaterialsProperties(modelID: number, elementID: number, recursive = false) {\r\n return await this.getProperty(modelID, elementID, recursive, PropsNames.materials);\r\n }\r\n\r\n protected async getSpatialNode(modelID: number, node: Node, treeChunks: any, includeProperties?: boolean) {\r\n await this.getChildren(modelID, node, treeChunks, PropsNames.aggregates, includeProperties);\r\n await this.getChildren(modelID, node, treeChunks, PropsNames.spatial, includeProperties);\r\n }\r\n\r\n protected async getChildren(modelID: number, node: Node, treeChunks: any, propNames: pName, includeProperties?: boolean) {\r\n const children = treeChunks[node.expressID];\r\n if (children == undefined) return;\r\n const prop = propNames.key as keyof Node;\r\n const nodes: any[] = [];\r\n for(let i = 0; i < children.length; i++){\r\n const child = children[i];\r\n let node = this.newNode(modelID, child);\r\n if (includeProperties) {\r\n const properties = await this.getItemProperties(modelID, node.expressID) as any;\r\n node = { ...properties, ...node };\r\n }\r\n await this.getSpatialNode(modelID, node, treeChunks, includeProperties);\r\n nodes.push(node);\r\n }\r\n (node[prop] as Node[]) = nodes;\r\n }\r\n\r\n protected newNode(modelID: number, id: number) {\r\n const typeName = this.getNodeType(modelID, id);\r\n return {\r\n expressID: id,\r\n type: typeName,\r\n children: []\r\n };\r\n }\r\n\r\n protected async getSpatialTreeChunks(modelID: number) {\r\n const treeChunks: any = {};\r\n await this.getChunks(modelID, treeChunks, PropsNames.aggregates);\r\n await this.getChunks(modelID, treeChunks, PropsNames.spatial);\r\n return treeChunks;\r\n }\r\n\r\n protected saveChunk(chunks: any, propNames: pName, rel: any) {\r\n const relating = rel[propNames.relating].value;\r\n const related = rel[propNames.related].map((r: any) => r.value);\r\n if (chunks[relating] == undefined) {\r\n chunks[relating] = related;\r\n } else {\r\n chunks[relating] = chunks[relating].concat(related);\r\n }\r\n }\r\n\r\n protected getRelated(rel: any, propNames: pName, IDs: number[]) {\r\n const element = rel[propNames.relating];\r\n if(!element) {\r\n return console.warn(`The object with ID ${rel.expressID} has a broken reference.`);\r\n }\r\n if (!Array.isArray(element)) IDs.push(element.value);\r\n else element.forEach((ele) => IDs.push(ele.value));\r\n }\r\n\r\n protected static isRelated(id: number, rel: any, propNames: pName) {\r\n const relatedItems = rel[propNames.related];\r\n if (Array.isArray(relatedItems)) {\r\n const values = relatedItems.map((item) => item.value);\r\n return values.includes(id);\r\n }\r\n return relatedItems.value === id;\r\n }\r\n\r\n protected static newIfcProject(id: number) {\r\n return {\r\n expressID: id,\r\n type: 'IFCPROJECT',\r\n children: []\r\n };\r\n }\r\n\r\n async getProperty(modelID: number, elementID: number, recursive = false, propName: pName): Promise<any> {\r\n }\r\n\r\n protected async getChunks(modelID: number, chunks: any, propNames: pName): Promise<void> {\r\n }\r\n\r\n protected async getItemProperties(modelID: number, expressID: number, recursive = false): Promise<any> {\r\n }\r\n\r\n protected getNodeType(modelID: number, id: number): any {\r\n }\r\n}","import { BasePropertyManager } from './BasePropertyManager';\r\nimport { IFCPROJEC