Skip to content

Commit

Permalink
feat: implement fragment ifc loader
Browse files Browse the repository at this point in the history
  • Loading branch information
agviegas committed Apr 26, 2024
1 parent 4601946 commit 7c8ef69
Show file tree
Hide file tree
Showing 13 changed files with 323 additions and 398 deletions.
540 changes: 270 additions & 270 deletions packages/components/src/fragments/FragmentIfcLoader/example.ts

Large diffs are not rendered by default.

134 changes: 37 additions & 97 deletions packages/components/src/fragments/FragmentIfcLoader/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@ import * as FRAGS from "bim-fragment";
import { FragmentsGroup } from "bim-fragment";
import { SpatialStructure } from "./src/spatial-structure";
import { CivilReader, IfcFragmentSettings, IfcMetadataReader } from "./src";
import { Disposable, Event, UI, Component, UIElement } from "../../base-types";
import { FragmentManager } from "../FragmentManager";
import { Components, ToolComponent } from "../../core";
import { Component, Components, Event, Disposable } from "../../core";
import { IfcJsonExporter } from "../../ifc/IfcJsonExporter";

export class FragmentIfcLoader
extends Component<WEBIFC.IfcAPI>
implements Disposable, UI
{
export class FragmentIfcLoader extends Component implements Disposable {
static readonly uuid = "a659add7-1418-4771-a0d6-7d4d438e4624" as const;

readonly onIfcLoaded = new Event<FragmentsGroup>();
Expand All @@ -27,13 +23,12 @@ export class FragmentIfcLoader

enabled: boolean = true;

uiElement = new UIElement<{ main: Button; toast: ToastNotification }>();
webIfc = new WEBIFC.IfcAPI();

private _material = new THREE.MeshLambertMaterial();
private _spatialTree = new SpatialStructure();
private _metaData = new IfcMetadataReader();
private _fragmentInstances = new Map<string, Map<number, FRAGS.Item>>();
private _webIfc = new WEBIFC.IfcAPI();
private _civil = new CivilReader();
private _propertyExporter = new IfcJsonExporter();

Expand All @@ -49,24 +44,14 @@ export class FragmentIfcLoader

constructor(components: Components) {
super(components);
this.components.tools.add(FragmentIfcLoader.uuid, this);

if (components.uiEnabled) {
this.setupUI();
}

this.components.add(FragmentIfcLoader.uuid, this);
this.settings.excludedCategories.add(WEBIFC.IFCOPENINGELEMENT);
}

get(): WEBIFC.IfcAPI {
return this._webIfc;
}

async dispose() {
dispose() {
this.onIfcLoaded.reset();
await this.uiElement.dispose();
(this._webIfc as any) = null;
await this.onDisposed.trigger(FragmentIfcLoader.uuid);
(this.webIfc as any) = null;
this.onDisposed.trigger(FragmentIfcLoader.uuid);
this.onDisposed.reset();
}

Expand All @@ -75,115 +60,75 @@ export class FragmentIfcLoader
if (this.settings.autoSetWasm) {
await this.autoSetWasm();
}
await this.onSetup.trigger();
this.onSetup.trigger();
}

async load(data: Uint8Array, coordinate = true) {
const before = performance.now();
await this.onIfcStartedLoading.trigger();
this.onIfcStartedLoading.trigger();
await this.readIfcFile(data);
const group = await this.getAllGeometries();

const properties = await this._propertyExporter.export(this._webIfc, 0);
const properties = await this._propertyExporter.export(this.webIfc, 0);
group.setLocalProperties(properties);

this.cleanUp();
console.log(`Streaming the IFC took ${performance.now() - before} ms!`);

const fragments = this.components.tools.get(FragmentManager);
fragments.groups.push(group);
const fragments = this.components.get(FragmentManager);
fragments.groups.set(group.uuid, group);

for (const frag of group.items) {
fragments.list[frag.id] = frag;
fragments.list.set(frag.id, frag);
frag.mesh.uuid = frag.id;
frag.group = group;
this.components.meshes.add(frag.mesh);
}

if (coordinate) {
fragments.coordinate([group]);
}

await this.onIfcLoaded.trigger(group);
this.onIfcLoaded.trigger(group);

return group;
}

private setupUI() {
const main = new Button(this.components);
main.materialIcon = "upload_file";
main.tooltip = "Load IFC";

const toast = new ToastNotification(this.components, {
message: "IFC model successfully loaded!",
});

main.onClick.add(() => {
const fileOpener = document.createElement("input");
fileOpener.type = "file";
fileOpener.accept = ".ifc";
fileOpener.style.display = "none";

fileOpener.onchange = async () => {
const fragments = this.components.tools.get(FragmentManager);
if (fileOpener.files === null || fileOpener.files.length === 0) return;
const file = fileOpener.files[0];
const buffer = await file.arrayBuffer();
const data = new Uint8Array(buffer);
const model = await this.load(data);
const scene = this.components.scene.get();
scene.add(model);

toast.visible = true;
await fragments.updateWindow();
fileOpener.remove();
};

fileOpener.click();
});

this.components.ui.add(toast);
toast.visible = false;

this.uiElement.set({ main, toast });
}

async readIfcFile(data: Uint8Array) {
const { path, absolute, logLevel } = this.settings.wasm;
this._webIfc.SetWasmPath(path, absolute);
await this._webIfc.Init();
this.webIfc.SetWasmPath(path, absolute);
await this.webIfc.Init();
if (logLevel) {
this._webIfc.SetLogLevel(logLevel);
this.webIfc.SetLogLevel(logLevel);
}
return this._webIfc.OpenModel(data, this.settings.webIfc);
return this.webIfc.OpenModel(data, this.settings.webIfc);
}

private async getAllGeometries() {
// Precompute the level and category to which each item belongs
this._spatialTree.setUp(this._webIfc);
this._spatialTree.setUp(this.webIfc);

const allIfcEntities = this._webIfc.GetIfcEntityList(0);
const allIfcEntities = this.webIfc.GetIfcEntityList(0);

const group = new FRAGS.FragmentsGroup();

const { FILE_NAME, FILE_DESCRIPTION } = WEBIFC;
group.ifcMetadata = {
name: this._metaData.get(this._webIfc, FILE_NAME),
description: this._metaData.get(this._webIfc, FILE_DESCRIPTION),
schema: (this._webIfc.GetModelSchema(0) as FRAGS.IfcSchema) || "IFC2X3",
maxExpressID: this._webIfc.GetMaxExpressID(0),
name: this._metaData.get(this.webIfc, FILE_NAME),
description: this._metaData.get(this.webIfc, FILE_DESCRIPTION),
schema: (this.webIfc.GetModelSchema(0) as FRAGS.IfcSchema) || "IFC2X3",
maxExpressID: this.webIfc.GetMaxExpressID(0),
};

const ids: number[] = [];

for (const type of allIfcEntities) {
if (!this._webIfc.IsIfcElement(type) && type !== WEBIFC.IFCSPACE) {
if (!this.webIfc.IsIfcElement(type) && type !== WEBIFC.IFCSPACE) {
continue;
}
if (this.settings.excludedCategories.has(type)) {
continue;
}
const result = this._webIfc.GetLineIDsWithType(0, type);
const result = this.webIfc.GetLineIDsWithType(0, type);
const size = result.size();
for (let i = 0; i < size; i++) {
const itemID = result.get(i);
Expand All @@ -195,7 +140,7 @@ export class FragmentIfcLoader

this._spatialTree.cleanUp();

this._webIfc.StreamMeshes(0, ids, (mesh) => {
this.webIfc.StreamMeshes(0, ids, (mesh) => {
this.getMesh(mesh, group);
});

Expand All @@ -216,16 +161,16 @@ export class FragmentIfcLoader
fragment.add(items);
}

const matrix = this._webIfc.GetCoordinationMatrix(0);
const matrix = this.webIfc.GetCoordinationMatrix(0);
group.coordinationMatrix.fromArray(matrix);
group.civilData = this._civil.read(this._webIfc);
group.civilData = this._civil.read(this.webIfc);

return group;
}

cleanUp() {
(this._webIfc as any) = null;
this._webIfc = new WEBIFC.IfcAPI();
(this.webIfc as any) = null;
this.webIfc = new WEBIFC.IfcAPI();
this._visitedFragments.clear();
this._fragmentInstances.clear();
}
Expand All @@ -246,10 +191,7 @@ export class FragmentIfcLoader
// Create geometry if it doesn't exist

if (!this._visitedFragments.has(geometryID)) {
const bufferGeometry = this.getGeometry(
this._webIfc,
geometryExpressID
);
const bufferGeometry = this.getGeometry(this.webIfc, geometryExpressID);

const material = transparent ? this._materialT : this._material;
const fragment = new FRAGS.Fragment(bufferGeometry, material, 1);
Expand Down Expand Up @@ -311,12 +253,12 @@ export class FragmentIfcLoader

const index = webIfc.GetIndexArray(
geometry.GetIndexData(),
geometry.GetIndexDataSize()
geometry.GetIndexDataSize(),
) as Uint32Array;

const vertexData = webIfc.GetVertexArray(
geometry.GetVertexData(),
geometry.GetVertexDataSize()
geometry.GetVertexDataSize(),
) as Float32Array;

const position = new Float32Array(vertexData.length / 2);
Expand Down Expand Up @@ -346,18 +288,18 @@ export class FragmentIfcLoader

private async autoSetWasm() {
const componentsPackage = await fetch(
`https://unpkg.com/openbim-components@${Components.release}/package.json`
`https://unpkg.com/openbim-components@${Components.release}/package.json`,
);
if (!componentsPackage.ok) {
console.warn(
"Couldn't get openbim-components package.json. Set wasm settings manually."
"Couldn't get openbim-components package.json. Set wasm settings manually.",
);
return;
}
const componentsPackageJSON = await componentsPackage.json();
if (!("web-ifc" in componentsPackageJSON.peerDependencies ?? {})) {
console.warn(
"Couldn't get web-ifc from peer dependencies in openbim-components. Set wasm settings manually."
"Couldn't get web-ifc from peer dependencies in openbim-components. Set wasm settings manually.",
);
} else {
const webIfcVer = componentsPackageJSON.peerDependencies["web-ifc"];
Expand All @@ -366,5 +308,3 @@ export class FragmentIfcLoader
}
}
}

ToolComponent.libraryUUIDs.add(FragmentIfcLoader.uuid);
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import * as THREE from "three";
import * as WEBIFC from "web-ifc";
import * as FRAGS from "bim-fragment";
import { Alignment } from "bim-fragment";
import { IfcCivil } from "./types";

export class CivilReader {
defLineMat = new THREE.LineBasicMaterial({ color: 0xffffff });
Expand All @@ -12,7 +10,7 @@ export class CivilReader {
const IfcCrossSection2D = webIfc.GetAllCrossSections2D(0);
const IfcCrossSection3D = webIfc.GetAllCrossSections3D(0);

const civilItems: IfcCivil = {
const civilItems = {
IfcAlignment,
IfcCrossSection2D,
IfcCrossSection3D,
Expand All @@ -26,7 +24,7 @@ export class CivilReader {
const alignments = new Map<number, FRAGS.Alignment>();

for (const ifcAlign of civilItems.IfcAlignment) {
const alignment = new Alignment();
const alignment = new FRAGS.Alignment();
alignment.absolute = this.getCurves(ifcAlign.curve3D, alignment);
alignment.horizontal = this.getCurves(ifcAlign.horizontal, alignment);
alignment.vertical = this.getCurves(ifcAlign.vertical, alignment);
Expand All @@ -38,7 +36,7 @@ export class CivilReader {
return undefined;
}

private getCurves(ifcAlignData: any, alignment: Alignment) {
private getCurves(ifcAlignData: any, alignment: FRAGS.Alignment) {
const curves: FRAGS.CivilCurve[] = [];
let index = 0;
for (const curve of ifcAlignData) {
Expand Down Expand Up @@ -69,7 +67,7 @@ export class CivilReader {
data,
alignment,
geometry,
this.defLineMat
this.defLineMat,
);

curves.push(mesh.curve);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class SpatialStructure {
try {
const spatialRels = webIfc.GetLineIDsWithType(
0,
WEBIFC.IFCRELCONTAINEDINSPATIALSTRUCTURE
WEBIFC.IFCRELCONTAINEDINSPATIALSTRUCTURE,
);

const allRooms = new Set<number>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,23 +181,12 @@ export class TransformHelper {
vz = vz.setLength(newDimz);

baseHelper.matrix = new THREE.Matrix4();
// prettier-ignore
baseHelper.matrix.set(
vx.x,
vx.y,
vx.z,
0,
vy.x,
vy.y,
vy.z,
0,
vz.x,
vz.y,
vz.z,
0,
cen.x,
cen.y,
cen.z,
1
vx.x, vx.y, vx.z, 0,
vy.x, vy.y, vy.z, 0,
vz.x, vz.y, vz.z, 0,
cen.x,cen.y,cen.z, 1,
);

baseHelper.matrix.transpose();
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class IfcJsonExporter {
webIfc: WEBIFC.IfcAPI,
modelID: number,
indirect = false,
recursiveSpatial = true
recursiveSpatial = true,
) {
const properties: FRAG.IfcProperties = {};

Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 2 additions & 0 deletions packages/components/src/ifc/Utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./ifc-categories";
export * from "./ifc-elements-map";
2 changes: 2 additions & 0 deletions packages/components/src/ifc/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./IfcJsonExporter";
export * from "./Utils";
1 change: 1 addition & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./core";
export * from "./fragments";
export * from "./ifc";
export * from "./utils";

0 comments on commit 7c8ef69

Please sign in to comment.