diff --git a/src/IFC/BaseDefinitions.ts b/src/IFC/BaseDefinitions.ts new file mode 100644 index 0000000..ca9dbcf --- /dev/null +++ b/src/IFC/BaseDefinitions.ts @@ -0,0 +1,227 @@ +import { BufferAttribute, BufferGeometry, Material, Matrix4, Mesh, Object3D } from 'three'; +// TODO: Remove ts ignore comments when @types/three gets updated +// @ts-ignore +import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils'; +import { + FlatMesh, + IfcGeometry, + IFCRELAGGREGATES, IFCRELASSOCIATESMATERIAL, + IFCRELCONTAINEDINSPATIALSTRUCTURE, + IFCRELDEFINESBYPROPERTIES, + IFCRELDEFINESBYTYPE, LoaderError, LoaderSettings, RawLineData, Vector +} from 'web-ifc'; +import {ParserProgress} from "./components/IFCParser"; + +export const IdAttrName = 'expressID'; + +export type IdAttributeByMaterial = { [id: number]: number }; +export type IdAttributesByMaterials = { [materialID: string]: IdAttributeByMaterial }; + +//TODO: Rename "scene" to "parent" in the next major release +export interface BaseSubsetConfig { + scene?: Object3D; + ids: number[]; + removePrevious: boolean; + material?: Material; + customID?: string; + applyBVH?: boolean; +} + +export interface SubsetConfig extends BaseSubsetConfig { + modelID: number; +} + +export const DEFAULT = 'default'; + +export type MapFaceindexID = { [key: number]: number }; + +export interface TypesMap { + [key: number]: number; +} + + +export interface IfcModel { + modelID: number; + mesh: IfcMesh; + types: TypesMap; + jsonData: { [id: number]: JSONObject }; +} + +export interface JSONObject { + expressID: number; + type: string; + [key: string]: any; +} + +export interface Worker { + active: boolean; + path: string; +} + +export interface IfcState { + models: { [modelID: number]: IfcModel }; + api: WebIfcAPI; + useJSON: boolean; + worker: Worker; + webIfcSettings?: LoaderSettings; + onProgress?: (event: ParserProgress) => void; + coordinationMatrix?: Matrix4, + wasmPath?: string; +} + +export interface IfcMesh extends Mesh { + modelID: number; +} + +export interface Node { + expressID: number; + type: string; + children: Node[]; +} + +export interface pName { + name: number; + relating: string; + related: string; + key: string; +} + +export interface NewIfcModel { + schema: string; + name?: string; + description?: string[]; + authors?: string[]; + organizations?: string[]; + authorization?: string; +} + +export const PropsNames = { + aggregates: { + name: IFCRELAGGREGATES, + relating: 'RelatingObject', + related: 'RelatedObjects', + key: 'children' + }, + spatial: { + name: IFCRELCONTAINEDINSPATIALSTRUCTURE, + relating: 'RelatingStructure', + related: 'RelatedElements', + key: 'children' + }, + psets: { + name: IFCRELDEFINESBYPROPERTIES, + relating: 'RelatingPropertyDefinition', + related: 'RelatedObjects', + key: 'hasPsets' + }, + materials: { + name: IFCRELASSOCIATESMATERIAL, + relating: 'RelatingMaterial', + related: 'RelatedObjects', + key: 'hasMaterial' + }, + type: { + name: IFCRELDEFINESBYTYPE, + relating: 'RelatingType', + related: 'RelatedObjects', + key: 'hasType' + } +}; + +export interface WebIfcAPI { + + wasmModule: any; + + Init(): void | Promise; + + // To close the web worker + Close?: () => void; + + /** + * Opens a model and returns a modelID number + * @data Buffer containing IFC data (bytes) + * @data Settings settings for loading the model + */ + OpenModel(data: string | Uint8Array, settings?: LoaderSettings): number | Promise; + + GetHeaderLine(modelID: number, headerType: number): any | Promise; + + /** + * Creates a new model and returns a modelID number + * @data Settings settings for generating data the model + */ + CreateModel(model: NewIfcModel, settings?: LoaderSettings): number | Promise; + + ExportFileAsIFC(modelID: number): Uint8Array | Promise; + + /** + * Opens a model and returns a modelID number + * @modelID Model handle retrieved by OpenModel, model must not be closed + * @data Buffer containing IFC data (bytes) + */ + GetGeometry(modelID: number, geometryExpressID: number): IfcGeometry | Promise; + + GetLine(modelID: number, expressID: number, flatten?: boolean): any | Promise; + + GetAndClearErrors(modelID: number): Vector | Promise>; + + WriteLine(modelID: number, lineObject: any): void | Promise; + + FlattenLine(modelID: number, line: any): void | Promise; + + GetRawLineData(modelID: number, expressID: number): RawLineData | Promise; + + WriteRawLineData(modelID: number, data: RawLineData): any | Promise; + + GetLineIDsWithType(modelID: number, type: number): Vector | Promise>; + + GetAllLines(modelID: Number): Vector | Promise>; + + SetGeometryTransformation(modelID: number, transformationMatrix: Array): void | Promise; + + GetCoordinationMatrix(modelID: number): Array | Promise>; + + GetVertexArray(ptr: number, size: number): Float32Array | Promise; + + GetIndexArray(ptr: number, size: number): Uint32Array | Promise; + + GetNameFromTypeCode(type:number): string | Promise; + + GetTypeCodeFromName(typeName:string): number | Promise; + + GetIfcEntityList(modelID: number) : Array | Promise>; + + CreateIfcGuidToExpressIdMapping(modelID: number): Map | Promise>; + + getSubArray(heap: any, startPtr: any, sizeBytes: any): any | Promise; + + /** + * Closes a model and frees all related memory + * @modelID Model handle retrieved by OpenModel, model must not be closed + */ + CloseModel(modelID: number): void | Promise; + + StreamAllMeshes(modelID: number, meshCallback: (mesh: FlatMesh) => void): void | Promise; + + StreamAllMeshesWithTypes(modelID: number, types: Array, meshCallback: (mesh: FlatMesh) => void): void | Promise; + + /** + * Checks if a specific model ID is open or closed + * @modelID Model handle retrieved by OpenModel + */ + IsModelOpen(modelID: number): boolean | Promise; + + /** + * Load all geometry in a model + * @modelID Model handle retrieved by OpenModel + */ + LoadAllGeometry(modelID: number): Vector | Promise>; + + /** + * Load geometry for a single element + * @modelID Model handle retrieved by OpenModel + */ + GetFlatMesh(modelID: number, expressID: number): FlatMesh | Promise; + + SetWasmPath(path: string): void | Promise; +} diff --git a/src/IFC/web-workers/BaseDefinitions.ts b/src/IFC/web-workers/BaseDefinitions.ts new file mode 100644 index 0000000..6e8719c --- /dev/null +++ b/src/IFC/web-workers/BaseDefinitions.ts @@ -0,0 +1,176 @@ +import { IfcState, WebIfcAPI } from '../BaseDefinitions'; + +export interface IfcWorkerAPI { + post: (data: any) => void; + initializeAPI: (api: WebIfcAPI) => void; + state?: IfcState; + postCallback: (data: any, result: any, serializer?: any) => void; +} + +export enum WorkerActions { + // Worker State Actions + updateStateUseJson = 'updateStateUseJson', + updateStateWebIfcSettings = 'updateStateWebIfcSettings', + updateModelStateTypes = 'updateModelStateTypes', + updateModelStateJsonData = 'updateModelStateJsonData', + loadJsonDataFromWorker = 'loadJsonDataFromWorker', + dispose = 'dispose', + + // WebIFC Actions + Close = 'Close', + DisposeWebIfc = 'DisposeWebIfc', + Init = 'Init', + OpenModel = 'OpenModel', + CreateModel = 'CreateModel', + ExportFileAsIFC = 'ExportFileAsIFC', + GetGeometry = 'GetGeometry', + GetLine = 'GetLine', + GetAndClearErrors = 'GetAndClearErrors', + WriteLine = 'WriteLine', + FlattenLine = 'FlattenLine', + GetRawLineData = 'GetRawLineData', + WriteRawLineData = 'WriteRawLineData', + GetLineIDsWithType = 'GetLineIDsWithType', + GetAllLines = 'GetAllLines', + SetGeometryTransformation = 'SetGeometryTransformation', + GetCoordinationMatrix = 'GetCoordinationMatrix', + GetVertexArray = 'GetVertexArray', + GetIndexArray = 'GetIndexArray', + getSubArray = 'getSubArray', + CloseModel = 'CloseModel', + StreamAllMeshes = 'StreamAllMeshes', + StreamAllMeshesWithTypes = 'StreamAllMeshesWithTypes', + IsModelOpen = 'IsModelOpen', + LoadAllGeometry = 'LoadAllGeometry', + GetFlatMesh = 'GetFlatMesh', + SetWasmPath = 'SetWasmPath', + GetNameFromTypeCode = 'GetNameFromTypeCode', + GetIfcEntityList = 'GetIfcEntityList', + CreateIfcGuidToExpressIdMapping = 'CreateIfcGuidToExpressIdMapping', + GetTypeCodeFromName ='GetTypeCodeFromName', + + // Parser + parse = 'parse', + setupOptionalCategories = 'setupOptionalCategories', + + // Properties + getExpressId = 'getExpressId', + initializeProperties = 'initializeProperties', + getAllItemsOfType = 'getAllItemsOfType', + getItemProperties = 'getItemProperties', + getMaterialsProperties = 'getMaterialsProperties', + getPropertySets = 'getPropertySets', + getSpatialStructure = 'getSpatialStructure', + getTypeProperties = 'getTypeProperties', + getHeaderLine = 'getHeaderLine', +} + +export enum WorkerAPIs { + workerState = 'workerState', + webIfc = 'webIfc', + properties = 'properties', + parser = 'parser', +} + +export interface IfcEventData { + worker: WorkerAPIs; + action: WorkerActions; + args: any; + id: number; + result: any; + onProgress: boolean; +} + +export interface RootWorker { + [WorkerAPIs.workerState]: WorkerStateAPI; + [WorkerAPIs.webIfc]: WebIfcWorkerAPI; + [WorkerAPIs.properties]: PropertyWorkerAPI; +} + +export interface BaseWorkerAPI { + API: WorkerAPIs; +} + +export type IfcWorkerEventHandler = (data: IfcEventData) => void; + +export interface WorkerStateAPI extends BaseWorkerAPI { + [WorkerActions.updateStateUseJson]: IfcWorkerEventHandler; + [WorkerActions.updateStateWebIfcSettings]: IfcWorkerEventHandler; + [WorkerActions.updateModelStateTypes]: IfcWorkerEventHandler; + [WorkerActions.updateModelStateJsonData]: IfcWorkerEventHandler; + [WorkerActions.loadJsonDataFromWorker]: IfcWorkerEventHandler; + [WorkerActions.dispose]: IfcWorkerEventHandler; +} + +export interface PropertyWorkerAPI extends BaseWorkerAPI { + [WorkerActions.getAllItemsOfType]: IfcWorkerEventHandler; + [WorkerActions.getItemProperties]: IfcWorkerEventHandler; + [WorkerActions.getMaterialsProperties]: IfcWorkerEventHandler; + [WorkerActions.getPropertySets]: IfcWorkerEventHandler; + [WorkerActions.getSpatialStructure]: IfcWorkerEventHandler; + [WorkerActions.getTypeProperties]: IfcWorkerEventHandler; +} + +export interface ParserWorkerAPI extends BaseWorkerAPI { + [WorkerActions.parse]: IfcWorkerEventHandler; + [WorkerActions.setupOptionalCategories]: IfcWorkerEventHandler; +} + +export interface WebIfcWorkerAPI extends BaseWorkerAPI { + [WorkerActions.Init]: IfcWorkerEventHandler; + [WorkerActions.Close]: IfcWorkerEventHandler; + [WorkerActions.DisposeWebIfc]: IfcWorkerEventHandler; + [WorkerActions.OpenModel]: IfcWorkerEventHandler; + [WorkerActions.CreateModel]: IfcWorkerEventHandler; + [WorkerActions.ExportFileAsIFC]: IfcWorkerEventHandler; + [WorkerActions.GetGeometry]: IfcWorkerEventHandler; + [WorkerActions.GetLine]: IfcWorkerEventHandler; + [WorkerActions.GetAndClearErrors]: IfcWorkerEventHandler; + [WorkerActions.WriteLine]: IfcWorkerEventHandler; + [WorkerActions.FlattenLine]: IfcWorkerEventHandler; + [WorkerActions.GetRawLineData]: IfcWorkerEventHandler; + [WorkerActions.WriteRawLineData]: IfcWorkerEventHandler; + [WorkerActions.GetLineIDsWithType]: IfcWorkerEventHandler; + [WorkerActions.GetAllLines]: IfcWorkerEventHandler; + [WorkerActions.SetGeometryTransformation]: IfcWorkerEventHandler; + [WorkerActions.GetCoordinationMatrix]: IfcWorkerEventHandler; + [WorkerActions.GetVertexArray]: IfcWorkerEventHandler; + [WorkerActions.GetIndexArray]: IfcWorkerEventHandler; + [WorkerActions.getSubArray]: IfcWorkerEventHandler; + [WorkerActions.CloseModel]: IfcWorkerEventHandler; + [WorkerActions.StreamAllMeshes]: IfcWorkerEventHandler; + [WorkerActions.StreamAllMeshesWithTypes]: IfcWorkerEventHandler; + [WorkerActions.IsModelOpen]: IfcWorkerEventHandler; + [WorkerActions.LoadAllGeometry]: IfcWorkerEventHandler; + [WorkerActions.GetFlatMesh]: IfcWorkerEventHandler; + [WorkerActions.SetWasmPath]: IfcWorkerEventHandler; + [WorkerActions.GetNameFromTypeCode]: IfcWorkerEventHandler; + [WorkerActions.GetIfcEntityList]: IfcWorkerEventHandler; + [WorkerActions.CreateIfcGuidToExpressIdMapping]: IfcWorkerEventHandler; + [WorkerActions.GetTypeCodeFromName]: IfcWorkerEventHandler; +} + +export interface SerializedVector { + [key: number]: any; + + size: number; +} + +export interface SerializedIfcGeometry { + GetVertexData: number; + GetVertexDataSize: number; + GetIndexData: number; + GetIndexDataSize: number; +} + +export interface SerializedFlatMesh { + geometries: SerializedVector; + expressID: number; +} + +export const ErrorStateNotAvailable = 'The state of the worker does not exist'; +export const ErrorRootStateNotAvailable = 'The root worker does not have any state'; +export const ErrorPropertiesNotAvailable = 'Error: Properties not available from web worker'; +export const ErrorParserNotAvailable = 'Error: Parser not available from web worker'; +export const ErrorBadJsonPath = 'Error: Model not available from web worker'; +export const ErrorBadJson = 'Error: The given Json could not be read as a JS object'; diff --git a/src/IFC/web-workers/handlers/WebIfcHandler.ts b/src/IFC/web-workers/handlers/WebIfcHandler.ts new file mode 100644 index 0000000..14c0116 --- /dev/null +++ b/src/IFC/web-workers/handlers/WebIfcHandler.ts @@ -0,0 +1,172 @@ +import { WebIfcAPI } from '../../BaseDefinitions'; +import { + + SerializedFlatMesh, + SerializedIfcGeometry, + SerializedVector, + WorkerActions, + WorkerAPIs +} from '../BaseDefinitions'; + +import { NewIfcModel, FlatMesh, IfcGeometry, LoaderError, LoaderSettings, RawLineData, Vector } from 'web-ifc'; +import { IFCWorkerHandler } from '../IFCWorkerHandler'; +import { Serializer } from '../serializer/Serializer'; + +export class WebIfcHandler implements WebIfcAPI { + + wasmModule: any; + API = WorkerAPIs.webIfc; + + constructor(private handler: IFCWorkerHandler, private serializer: Serializer) { + } + + async Init(): Promise { + this.wasmModule = true; + return this.handler.request(this.API, WorkerActions.Init); + } + + async OpenModel(data: string | Uint8Array, settings?: LoaderSettings): Promise { + return this.handler.request(this.API, WorkerActions.OpenModel, { data, settings }); + } + + async CreateModel(model: NewIfcModel, settings?: LoaderSettings): Promise { + return this.handler.request(this.API, WorkerActions.CreateModel, { model, settings }); + } + + async ExportFileAsIFC(modelID: number): Promise { + return this.handler.request(this.API, WorkerActions.ExportFileAsIFC, { modelID }); + } + + async GetHeaderLine(modelID: number, headerType: number): Promise { + return this.handler.request(this.API, WorkerActions.getHeaderLine, { modelID, headerType }); + } + + async GetGeometry(modelID: number, geometryExpressID: number): Promise { + this.handler.serializeHandlers[this.handler.requestID] = (geom: SerializedIfcGeometry) => { + return this.serializer.reconstructIfcGeometry(geom); + } + return this.handler.request(this.API, WorkerActions.GetGeometry, { modelID, geometryExpressID }); + } + + async GetLine(modelID: number, expressID: number, flatten?: boolean): Promise { + return this.handler.request(this.API, WorkerActions.GetLine, { modelID, expressID, flatten }); + } + + async GetAndClearErrors(modelID: number): Promise> { + this.handler.serializeHandlers[this.handler.requestID] = (vector: SerializedVector) => { + return this.serializer.reconstructVector(vector); + } + return this.handler.request(this.API, WorkerActions.GetAndClearErrors, { modelID }); + } + + async GetNameFromTypeCode(type:number): Promise { + return this.handler.request(this.API, WorkerActions.GetNameFromTypeCode, { type }); + } + + async GetIfcEntityList(modelID: number) : Promise { + return this.handler.request(this.API, WorkerActions.GetIfcEntityList, { modelID }); + } + + async CreateIfcGuidToExpressIdMapping(modelID: number): Promise> { + return this.handler.request(this.API, WorkerActions.CreateIfcGuidToExpressIdMapping, { modelID }); + } + + async GetTypeCodeFromName(typeName:string): Promise { + return this.handler.request(this.API, WorkerActions.GetTypeCodeFromName, { typeName }); + } + + async WriteLine(modelID: number, lineObject: any): Promise { + return this.handler.request(this.API, WorkerActions.WriteLine, { modelID, lineObject }); + } + + async FlattenLine(modelID: number, line: any): Promise { + return this.handler.request(this.API, WorkerActions.FlattenLine, { modelID, line }); + } + + async GetRawLineData(modelID: number, expressID: number): Promise { + return this.handler.request(this.API, WorkerActions.GetRawLineData, { modelID, expressID }); + } + + async WriteRawLineData(modelID: number, data: RawLineData): Promise { + return this.handler.request(this.API, WorkerActions.WriteRawLineData, { modelID, data }); + } + + async GetLineIDsWithType(modelID: number, type: number): Promise> { + this.handler.serializeHandlers[this.handler.requestID] = (vector: SerializedVector) => { + return this.serializer.reconstructVector(vector); + } + return this.handler.request(this.API, WorkerActions.GetLineIDsWithType, { modelID, type }); + } + + async GetAllLines(modelID: number): Promise> { + this.handler.serializeHandlers[this.handler.requestID] = (vector: SerializedVector) => { + return this.serializer.reconstructVector(vector); + } + return this.handler.request(this.API, WorkerActions.GetAllLines, { modelID }); + } + + async SetGeometryTransformation(modelID: number, transformationMatrix: number[]): Promise { + return this.handler.request(this.API, WorkerActions.SetGeometryTransformation, { + modelID, + transformationMatrix + }); + } + + async GetCoordinationMatrix(modelID: number): Promise { + return this.handler.request(this.API, WorkerActions.GetCoordinationMatrix, { modelID }); + } + + async GetVertexArray(ptr: number, size: number): Promise { + return this.handler.request(this.API, WorkerActions.GetVertexArray, { ptr, size }); + } + + async GetIndexArray(ptr: number, size: number): Promise { + return this.handler.request(this.API, WorkerActions.GetIndexArray, { ptr, size }); + } + + async getSubArray(heap: any, startPtr: any, sizeBytes: any): Promise { + return this.handler.request(this.API, WorkerActions.getSubArray, { heap, startPtr, sizeBytes }); + } + + async CloseModel(modelID: number): Promise { + return this.handler.request(this.API, WorkerActions.CloseModel, { modelID }); + } + + async StreamAllMeshes(modelID: number, meshCallback: (mesh: FlatMesh) => void): Promise { + this.handler.callbackHandlers[this.handler.requestID] = { + action: meshCallback, + serializer: this.serializer.reconstructFlatMesh + }; + return this.handler.request(this.API, WorkerActions.StreamAllMeshes, { modelID }); + } + + async StreamAllMeshesWithTypes(modelID: number, types: number[], meshCallback: (mesh: FlatMesh) => void): Promise { + this.handler.callbackHandlers[this.handler.requestID] = { + action: meshCallback, + serializer: this.serializer.reconstructFlatMesh + }; + return this.handler.request(this.API, WorkerActions.StreamAllMeshesWithTypes, { modelID, types }); + } + + async IsModelOpen(modelID: number): Promise { + return this.handler.request(this.API, WorkerActions.IsModelOpen, { modelID }); + } + + async LoadAllGeometry(modelID: number): Promise> { + this.handler.serializeHandlers[this.handler.requestID] = (vector: SerializedVector) => { + return this.serializer.reconstructFlatMeshVector(vector); + } + return this.handler.request(this.API, WorkerActions.LoadAllGeometry, { modelID }); + } + + async GetFlatMesh(modelID: number, expressID: number): Promise { + this.handler.serializeHandlers[this.handler.requestID] = (flatMesh: SerializedFlatMesh) => { + return this.serializer.reconstructFlatMesh(flatMesh); + } + return this.handler.request(this.API, WorkerActions.GetFlatMesh, { modelID, expressID }); + } + + async SetWasmPath(path: string): Promise { + return this.handler.request(this.API, WorkerActions.SetWasmPath, { path }); + } +} diff --git a/src/IFC/web-workers/workers/WebIfcWorker.ts b/src/IFC/web-workers/workers/WebIfcWorker.ts new file mode 100644 index 0000000..8b8246d --- /dev/null +++ b/src/IFC/web-workers/workers/WebIfcWorker.ts @@ -0,0 +1,203 @@ +import { IfcEventData, IfcWorkerAPI, WebIfcWorkerAPI, WorkerAPIs } from '../BaseDefinitions'; +import { IfcAPI } from 'web-ifc'; +import { Serializer } from '../serializer/Serializer'; + +export class WebIfcWorker implements WebIfcWorkerAPI { + + webIFC: IfcAPI; + API = WorkerAPIs.webIfc; + + constructor(private worker: IfcWorkerAPI, private serializer: Serializer) { + this.webIFC = new IfcAPI(); + this.worker.initializeAPI(this.webIFC); + } + + async Init(data: IfcEventData) { + await this.webIFC.Init(); + this.worker.post(data); + }; + + async Close(data: IfcEventData) { + this.nullifyWebIfc(); + this.webIFC = new IfcAPI(); + await this.webIFC.Init(); + this.worker.post(data); + }; + + async DisposeWebIfc(data: IfcEventData) { + this.nullifyWebIfc(); + this.worker.post(data); + } + + CloseModel(data: IfcEventData) { + this.webIFC.CloseModel(data.args.modelID); + this.worker.post(data); + } + + CreateModel(data: IfcEventData) { + data.result = this.webIFC.CreateModel(data.args.settings); + this.worker.post(data); + } + + ExportFileAsIFC(data: IfcEventData) { + data.result = this.webIFC.ExportFileAsIFC(data.args.modelID); + this.worker.post(data); + } + + FlattenLine(data: IfcEventData) { + this.webIFC.FlattenLine(data.args.modelID, data.args.line); + this.worker.post(data); + } + + GetAllLines(data: IfcEventData) { + const vector = this.webIFC.GetAllLines(data.args.modelID); + data.result = this.serializer.serializeVector(vector); + this.worker.post(data); + } + + GetAndClearErrors(data: IfcEventData) { + const vector = this.webIFC.GetAndClearErrors(data.args.modelID); + data.result = this.serializer.serializeVector(vector); + this.worker.post(data); + } + + GetCoordinationMatrix(data: IfcEventData) { + data.result = this.webIFC.GetCoordinationMatrix(data.args.modelID); + this.worker.post(data); + } + + GetFlatMesh(data: IfcEventData) { + const flatMesh = this.webIFC.GetFlatMesh(data.args.modelID, data.args.expressID); + data.result = this.serializer.serializeFlatMesh(flatMesh); + this.worker.post(data); + } + + GetGeometry(data: IfcEventData) { + const ifcGeometry = this.webIFC.GetGeometry(data.args.modelID, data.args.geometryExpressID); + data.result = this.serializer.serializeIfcGeometry(ifcGeometry); + this.worker.post(data); + } + + GetIndexArray(data: IfcEventData) { + data.result = this.webIFC.GetIndexArray(data.args.ptr, data.args.size); + this.worker.post(data); + } + + GetLine(data: IfcEventData) { + const args = data.args; + try { + data.result = this.webIFC.GetLine(args.modelID, args.expressID, args.flatten); + } catch (e) { + console.log(`There was a problem getting the properties of the item ${args.expressID}`); + data.result = {}; + } + this.worker.post(data); + } + + GetLineIDsWithType(data: IfcEventData) { + const vector = this.webIFC.GetLineIDsWithType(data.args.modelID, data.args.type); + data.result = this.serializer.serializeVector(vector); + this.worker.post(data); + } + + GetRawLineData(data: IfcEventData) { + data.result = this.webIFC.GetRawLineData(data.args.modelID, data.args.expressID); + this.worker.post(data); + } + + GetVertexArray(data: IfcEventData) { + data.result = this.webIFC.GetVertexArray(data.args.ptr, data.args.size); + this.worker.post(data); + } + + IsModelOpen(data: IfcEventData) { + data.result = this.webIFC.IsModelOpen(data.args.modelID); + this.worker.post(data); + } + + LoadAllGeometry(data: IfcEventData) { + const flatMeshVector = this.webIFC.LoadAllGeometry(data.args.modelID); + data.result = this.serializer.serializeFlatMeshVector(flatMeshVector); + this.worker.post(data); + } + + OpenModel(data: IfcEventData) { + data.result = this.webIFC.OpenModel(data.args.data, data.args.settings); + this.worker.post(data); + } + + SetGeometryTransformation(data: IfcEventData) { + this.webIFC.SetGeometryTransformation(data.args.modelID, data.args.transformationMatrix); + this.worker.post(data); + } + + SetWasmPath(data: IfcEventData) { + this.webIFC.SetWasmPath(data.args.path); + this.worker.post(data); + } + + StreamAllMeshes(data: IfcEventData) { + const serializer = this.serializer.serializeFlatMesh; + const callback = (result: any) => this.worker.postCallback(data, result, serializer); + this.webIFC.StreamAllMeshes(data.args.modelID, callback); + } + + StreamAllMeshesWithTypes(data: IfcEventData) { + const args = data.args; + const serializer = this.serializer.serializeFlatMesh; + const callback = (result: any) => this.worker.postCallback(data, result, serializer); + this.webIFC.StreamAllMeshesWithTypes(args.modelID, args.types, callback); + } + + WriteLine(data: IfcEventData) { + const modelID = data.args.modelID; + const serializedObject = data.args.lineObject; + + // This is necessary because of the serialization of the web worker + const object = this.webIFC.GetLine(modelID, serializedObject.expressID); + Object.keys(serializedObject).forEach(propName => { + if(object[propName] !== undefined) { + object[propName] = serializedObject[propName]; + } + }) + + this.webIFC.WriteLine(data.args.modelID, object); + this.worker.post(data); + } + + WriteRawLineData(data: IfcEventData) { + this.webIFC.WriteRawLineData(data.args.modelID, data.args.data); + this.worker.post(data); + } + + getSubArray(data: IfcEventData) { + const args = data.args; + this.webIFC.getSubArray(args.heap, args.startPtr, args.sizeBytes); + this.worker.post(data); + } + + GetNameFromTypeCode(data: IfcEventData) { + data.result=this.webIFC.GetNameFromTypeCode(data.args.modelID); + this.worker.post(data); + } + + GetIfcEntityList(data: IfcEventData) { + data.result=this.webIFC.GetIfcEntityList(data.args.modelID); + this.worker.post(data); + } + + CreateIfcGuidToExpressIdMapping(data: IfcEventData) { + data.result = this.webIFC.CreateIfcGuidToExpressIdMapping(data.args.modelID); + this.worker.post(data); + } + + GetTypeCodeFromName(data: IfcEventData) { + data.result=this.webIFC.GetTypeCodeFromName(data.args.typeName); + this.worker.post(data); + } + + private nullifyWebIfc() { + // @ts-ignore + this.webIFC = null; + } +} diff --git a/test/IFC/WebWorkers/WebIfcHandler.test.ts b/test/IFC/WebWorkers/WebIfcHandler.test.ts new file mode 100644 index 0000000..f0106b9 --- /dev/null +++ b/test/IFC/WebWorkers/WebIfcHandler.test.ts @@ -0,0 +1,23 @@ +import { WebIfcHandler } from "../../../src/IFC/web-workers/handlers/WebIfcHandler"; +import { WorkerActions, WorkerAPIs } from "../../../src/IFC/web-workers/BaseDefinitions"; + +describe("WebIfcHandler", () => { + test("CreateIfcGuidToExpressIdMapping forwards the request to the web-ifc worker", async () => { + const expected = new Map([["guid", 42]]); + const handler = { + requestID: 0, + request: jest.fn().mockResolvedValue(expected) + }; + const serializer = {}; + const webIfcHandler = new WebIfcHandler(handler as any, serializer as any); + + const result = await webIfcHandler.CreateIfcGuidToExpressIdMapping(7); + + expect(handler.request).toHaveBeenCalledWith( + WorkerAPIs.webIfc, + WorkerActions.CreateIfcGuidToExpressIdMapping, + { modelID: 7 } + ); + expect(result).toBe(expected); + }); +});