Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IfcRelationsIndexer improved by 300x #384

Merged
merged 2 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 80 additions & 84 deletions packages/components/src/ifc/IfcRelationsIndexer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { FragmentsGroup } from "@thatopen/fragments";
import { Disposable, Event, Component, Components } from "../../core";
import { FragmentManager } from "../../fragments/FragmentManager";
import { IfcPropertiesUtils } from "../Utils";
import { getRelationMap } from "./src/getRelationMap";
import {
RelationsMap,
ModelsRelationMap,
Expand Down Expand Up @@ -32,11 +31,7 @@ export class IfcRelationsIndexer extends Component implements Disposable {

private _relToAttributesMap = relToAttributesMap;

/**
* Array of inverse attribute names.
* This array is used to define the inverse attributes that the indexer will process and index.
*/
readonly inverseAttributes: InverseAttributes = [
private _inverseAttributes: InverseAttributes = [
"IsDecomposedBy",
"Decomposes",
"AssociatedTo",
Expand All @@ -53,6 +48,17 @@ export class IfcRelationsIndexer extends Component implements Disposable {
"ContainsElements",
];

private _ifcRels = [
WEBIFC.IFCRELAGGREGATES,
WEBIFC.IFCRELASSOCIATESMATERIAL,
WEBIFC.IFCRELASSOCIATESCLASSIFICATION,
WEBIFC.IFCRELASSIGNSTOGROUP,
WEBIFC.IFCRELDEFINESBYPROPERTIES,
WEBIFC.IFCRELDEFINESBYTYPE,
WEBIFC.IFCRELDEFINESBYTEMPLATE,
WEBIFC.IFCRELCONTAINEDINSPATIALSTRUCTURE,
] as const;

/**
* Holds the relationship mappings for each model processed by the indexer.
* The structure is a map where each key is a model's UUID, and the value is another map.
Expand All @@ -78,15 +84,6 @@ export class IfcRelationsIndexer extends Component implements Disposable {
delete this.relationMaps[data.groupID];
};

private getAttributeRels(value: string) {
const keys: number[] = [];
for (const [rel, attribute] of this._relToAttributesMap.entries()) {
const { forRelating, forRelated } = attribute;
if (forRelating === value || forRelated === value) keys.push(rel);
}
return keys;
}

/**
* Adds a relation map to the model's relations map.
*
Expand Down Expand Up @@ -125,46 +122,43 @@ export class IfcRelationsIndexer extends Component implements Disposable {

const relationsMap: RelationsMap = new Map();

for (const attribute of this.inverseAttributes) {
const rels = this.getAttributeRels(attribute);
for (const rel of rels) {
await IfcPropertiesUtils.getRelationMap(
model,
rel,
async (relatingID, relatedID) => {
const inverseAttributes = this._relToAttributesMap.get(rel);
if (!inverseAttributes) return;
const { forRelated: related, forRelating: relating } =
inverseAttributes;
if (relating) {
const currentMap =
relationsMap.get(relatingID) ?? new Map<number, number[]>();
// TODO: indexOf might be slow. Better a Map<string, number>?
const index = this.inverseAttributes.indexOf(relating);
currentMap.set(index, relatedID);
relationsMap.set(relatingID, currentMap);
}
if (related) {
for (const id of relatedID) {
const currentMap =
relationsMap.get(id) ?? new Map<number, number[]>();
const index = this.inverseAttributes.indexOf(related);
const relations = currentMap.get(index) ?? [];
relations.push(relatingID);
currentMap.set(index, relations);
relationsMap.set(id, currentMap);
}
}
},
);
}
for (const rel of this._ifcRels) {
await IfcPropertiesUtils.getRelationMap(
model,
rel,
async (relatingID, relatedIDs) => {
const inverseAttributes = this._relToAttributesMap.get(rel);
if (!inverseAttributes) return;
const { forRelated: related, forRelating: relating } =
inverseAttributes;

// forRelating
const currentMap =
relationsMap.get(relatingID) ?? new Map<number, number[]>();
const index = this._inverseAttributes.indexOf(relating);
currentMap.set(index, relatedIDs);
relationsMap.set(relatingID, currentMap);

// forRelated
for (const id of relatedIDs) {
const currentMap =
relationsMap.get(id) ?? new Map<number, number[]>();
const index = this._inverseAttributes.indexOf(related);
const relations = currentMap.get(index) ?? [];
relations.push(relatingID);
currentMap.set(index, relations);
relationsMap.set(id, currentMap);
}
},
);
}

this.setRelationMap(model, relationsMap);
return relationsMap;
}

/**
* Processes a given model from a WebIfc API to index its IFC entities relations based on predefined inverse attributes.
* Processes a given model from a WebIfc API to index its IFC entities relations.
*
* @param ifcApi - The WebIfc API instance from which to retrieve the model's properties.
* @param modelID - The unique identifier of the model within the WebIfc API.
Expand All @@ -174,42 +168,44 @@ export class IfcRelationsIndexer extends Component implements Disposable {
async processFromWebIfc(ifcApi: WEBIFC.IfcAPI, modelID: number) {
const relationsMap: RelationsMap = new Map();

const properties: Record<string, Record<string, any>> = {};
const lines = ifcApi.GetAllLines(modelID);
for (const relType of this._ifcRels) {
const relInverseAttributes = this._relToAttributesMap.get(relType);
if (!relInverseAttributes) continue;
const { forRelated: related, forRelating: relating } =
relInverseAttributes;
const relExpressIDs = ifcApi.GetLineIDsWithType(modelID, relType);
for (let i = 0; i < relExpressIDs.size(); i++) {
const relAttrs = await ifcApi.properties.getItemProperties(
modelID,
relExpressIDs.get(i),
);
const relatingKey = Object.keys(relAttrs).find((key) =>
key.startsWith("Relating"),
);
const relatedKey = Object.keys(relAttrs).find((key) =>
key.startsWith("Related"),
);
if (!(relatingKey && relatedKey)) continue;
const relatingID = relAttrs[relatingKey].value;
const relatedIDs = relAttrs[relatedKey].map((el: any) => el.value);

for (let i = 0; i < lines.size(); i++) {
const line = lines.get(i);
const attrs = await ifcApi.properties.getItemProperties(modelID, line);
properties[line] = attrs;
}
// forRelating
const currentMap =
relationsMap.get(relatingID) ?? new Map<number, number[]>();
const index = this._inverseAttributes.indexOf(relating);
currentMap.set(index, relatedIDs);
relationsMap.set(relatingID, currentMap);

for (const attribute of this.inverseAttributes) {
const rels = this.getAttributeRels(attribute);
for (const rel of rels) {
getRelationMap(properties, rel, (relatingID, relatedID) => {
const inverseAttributes = this._relToAttributesMap.get(rel);
if (!inverseAttributes) return;
const { forRelated: related, forRelating: relating } =
inverseAttributes;
if (relating) {
const currentMap =
relationsMap.get(relatingID) ?? new Map<number, number[]>();
const index = this.inverseAttributes.indexOf(relating);
currentMap.set(index, relatedID);
relationsMap.set(relatingID, currentMap);
}
if (related) {
for (const id of relatedID) {
const currentMap =
relationsMap.get(id) ?? new Map<number, number[]>();
const index = this.inverseAttributes.indexOf(related);
const relations = currentMap.get(index) ?? [];
relations.push(relatingID);
currentMap.set(index, relations);
relationsMap.set(id, currentMap);
}
}
});
// forRelated
for (const id of relatedIDs) {
const currentMap =
relationsMap.get(id) ?? new Map<number, number[]>();
const index = this._inverseAttributes.indexOf(related);
const relations = currentMap.get(index) ?? [];
relations.push(relatingID);
currentMap.set(index, relations);
relationsMap.set(id, currentMap);
}
}
}

Expand Down Expand Up @@ -240,7 +236,7 @@ export class IfcRelationsIndexer extends Component implements Disposable {
const indexMap = this.relationMaps[model.uuid];
if (!indexMap) return null;
const entityRelations = indexMap.get(expressID);
const attributeIndex = this.inverseAttributes.indexOf(relationName);
const attributeIndex = this._inverseAttributes.indexOf(relationName);
if (!entityRelations || attributeIndex === -1) return null;
const relations = entityRelations.get(attributeIndex);
if (!relations) return null;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { InverseAttribute } from "./types";
// TODO: Construct this based on the IFC EXPRESS long form schema?
export const relToAttributesMap = new Map<
number,
{ forRelating?: InverseAttribute; forRelated?: InverseAttribute }
{ forRelating: InverseAttribute; forRelated: InverseAttribute }
>([
[
WEBIFC.IFCRELAGGREGATES,
Expand Down