engine_web-ifc icon indicating copy to clipboard operation
engine_web-ifc copied to clipboard

Space boundaries not created - IFCCURVEBOUNDEDPLANE not supported

Open MadsHolten opened this issue 3 years ago • 12 comments

When I try to access the space boundaries' geometries I get an error "unexpected mesh type". I have tested the same model in ifcopenshell for Python where I am able to get the space boundaries, export them to STL and view them in ThreeJS.

const spaceBoundaries = ifcAPi.GetLineIDsWithType(modelID, IFCRELSPACEBOUNDARY);
for (let i = 0; i < spaceBoundaries.size(); i++)
{
    const expressID = spaceBoundaries.get(i);

    // Get the element's geometry
    const flatMesh: FlatMesh = ifcAPi.GetFlatMesh(modelID, expressID);
    
    const geometry: IfcGeometry = ifcAPi.GetGeometry(modelID, flatMesh.expressID);
    // Returns the geometry object but also prints "unexpected mesh type"
    
    const vertices = geometry.GetVertexData();
    console.log(vertices); // Returns 0

}

Tested with web-ifc version 0.0.35

As I traverse through the relations I can see that the boundaries are represented by IfcSurfaceOfLinearExtrusion and IfcCurveBoundedPlane

MadsHolten avatar Aug 11 '22 07:08 MadsHolten

Hello, I also came across "IFCCURVEBOUNDEDPLANE" geometry not being handled properly. I am parsing the geometry in a simple dedicated worker:

import { IfcAPI, Vector } from 'web-ifc/web-ifc-api';

interface UserInputModel {
    name: string;
    buffer: ArrayBuffer;
}

class IFCLoader {
    readonly data: { [item: number]: any } = {};
    private api = new IfcAPI();
    constructor() {
        this.api.SetWasmPath('/', true);
    }

    async loadIFC(name: string, buffer: ArrayBuffer) {
        await this.api.Init();
        console.log(this.api);

        const bufferui8 = new Uint8Array(buffer);
        console.log(`Loading IFC model ${name} with size ${bufferui8.length}`);
        const modelID = this.api.OpenModel(bufferui8);
        console.log(`Loading model ID: ${modelID}`);
        const lines = this.api.GetAllLines(modelID);
        this.getAllItemsFromLines(modelID, lines);
    }

    private getAllItemsFromLines(modelID: number, lines: Vector<number>) {
        for (let i = 0; i < lines.size(); i++) {
            this.saveProperties(modelID, lines, i);
        }
    }

    private saveProperties(modelID: number, lines: Vector<number>, index: number) {
        const itemID = lines.get(index);
        console.log(`Loading item ID: ${itemID}`);
        const props = this.api.GetLine(modelID, itemID);
        props.type = props.__proto__.constructor.name;
        this.data[itemID] = props;
    }
}

export function parseIFC(models: UserInputModel[]) {
    console.log(`Loading ${models.length} models`);
    models.forEach(async (model) => {
        if (model.name.endsWith('.ifc')) {
            console.log(`Loading IFC model ${model.name}`);
            const ifcLoader = new IFCLoader();
            await ifcLoader.loadIFC(model.name, model.buffer);
            console.log(ifcLoader.data);
        }
    });
}

self.onmessage = (e) => {
    parseIFC(e.data);
};

The error I get follows:

Uncaught (in promise) TypeError: Cannot read properties of null (reading 'map')
    at 2827736869 (web-ifc-api.js:11318:115)
    at IfcAPI2.GetLine (web-ifc-api.js:62076:83)
    at IFCLoader.saveProperties (IFC.worker.ts?type=module&worker_file:33:28)
    at IFCLoader.getAllItemsFromLines (IFC.worker.ts?type=module&worker_file:26:9)
    at IFCLoader.loadIFC (IFC.worker.ts?type=module&worker_file:21:5)
    at async IFC.worker.ts?type=module&worker_file:45:13

Any way I could help fixing this?

vojtatom avatar Mar 22 '23 15:03 vojtatom

To elaborate: the typical line in .ifc file that fails to load looks like this:

#1929=IFCCURVEBOUNDEDPLANE(#1928,#1926,$);

as the last parameter is $, it doesn't translate to anything with the .map attribute.

vojtatom avatar Mar 22 '23 15:03 vojtatom

Yes - this is for some reason being processed as a mandatory field here - not optional so something is wrong. I will take a look

beachtom avatar Mar 22 '23 15:03 beachtom

This should be fixed now! Please let me know if it is not

beachtom avatar Mar 28 '23 10:03 beachtom

Just to understand correctly. Does this mean that Space boundary geometries (IFCRELSPACEBOUNDARY) are now being processed? If so, that's great news!

MadsHolten avatar Mar 28 '23 10:03 MadsHolten

So my understanding is reading in IFCRELSPACEBOUNDARY was broken (which it was) so you should be able to read it in without errors

beachtom avatar Mar 28 '23 11:03 beachtom

✅ Successfully pulled 7d06a61069be6033e06c7e6114daa9c335047051 and built it locally, after parsing a file that was previously causing problems, no errors occurred, thank you.

Would be nice to update the package on NPM so we can just update the dependency, until then we'll be using the local build.

vojtatom avatar Mar 28 '23 12:03 vojtatom

Unfortunately I am still having problems with surface geometries of type IFCCURVEBOUNDEDPLANE and IFCSURFACEOFLINEAREXTRUSION. You can test with the below example that will fetch my demo model that contains space boundaries.

const { IfcAPI, IFCCURVEBOUNDEDPLANE, IFCSURFACEOFLINEAREXTRUSION} = require("../../dist/web-ifc-api-node.js");

console.log("Hello surface geometries!");

const ifcapi = new IfcAPI();

async function loadFile(fileURL) {
    // load model data as a string
    const res = await fetch(fileURL);
    const arrayBuffer = await res.arrayBuffer();
    const ifcData = Buffer.from( new Uint8Array(arrayBuffer) );

    await ifcapi.Init();

    const modelID = ifcapi.OpenModel(ifcData);

    console.log(`Loaded modelID ${modelID}`);

    // Get geometries of type IFCCURVEBOUNDEDPLANE
    const cbps = ifcapi.GetLineIDsWithType(modelID, IFCCURVEBOUNDEDPLANE);
    console.log(`Found ${cbps.size()} IFCCURVEBOUNDEDPLANEs`);
    const cbpsGeometries = getFlatMeshes(modelID, cbps);

    // Get geometries of type IFCSURFACEOFLINEAREXTRUSION
    const soles = ifcapi.GetLineIDsWithType(modelID, IFCSURFACEOFLINEAREXTRUSION);
    console.log(`Found ${soles.size()} IFCSURFACEOFLINEAREXTRUSIONs`);
    const solesGeometries = getFlatMeshes(modelID, soles);
    
    console.log(cbpsGeometries);
    console.log(solesGeometries);

    ifcapi.CloseModel(modelID);
    console.log(`Closed model`);

}

function getFlatMeshes(modelID, items) {
    let geometries = {};
    for (let i = 0; i < items.size(); i++) {
        const expressId = items.get(i);

        const flatMesh = ifcapi.GetFlatMesh(modelID, expressId);
        geometries[expressId] = [];
        for (let j = 0; j < flatMesh.geometries.size(); j++) {
            const geometry = ifcapi.GetGeometry(modelID, flatMesh.geometries.get(j).geometryExpressID);
            const geometryVertexArray = ifcapi.GetVertexArray(geometry.GetVertexData(), geometry.GetVertexDataSize());
            const geometryIndexData = ifcapi.GetIndexArray(geometry.GetIndexData(), geometry.GetIndexDataSize());
            geometries[expressId].push({geometryVertexArray, geometryIndexData});
        }

    }
    return geometries;
}

loadFile("https://raw.githubusercontent.com/MadsHolten/BOT-Duplex-house/master/Model%20files/IFC/Duplex.ifc");

I ran it from examples/nodejs both in the current main and in https://github.com/IFCjs/web-ifc/commit/7d06a61069be6033e06c7e6114daa9c335047051. Both throw "ERROR: unexpected mesh type"

MadsHolten avatar Apr 11 '23 11:04 MadsHolten

Can you give me the exact error? It may be we can now parse that IFC entity but do not have the geometric decoding for it yet

beachtom avatar Apr 11 '23 12:04 beachtom

I don't get more than ERROR: unexpected mesh type. Although I get it for every single element. How can I get more detailed logs?

MadsHolten avatar Apr 11 '23 12:04 MadsHolten

Sorry I found it now it is actually IFCCURVEBOUNDEDPLANE that we do not support right now. I'll re-open this

beachtom avatar Apr 11 '23 13:04 beachtom

I'm getting the same error with IfcRelSpaceBoundaries but with IfcFaceSurface, that is another IfcRepresentatonItem subclass, as geometric representation.

agonzalezesteve avatar Oct 23 '23 15:10 agonzalezesteve