Skip to content

Commit

Permalink
feat: restructure repo and document core
Browse files Browse the repository at this point in the history
  • Loading branch information
agviegas committed Sep 28, 2024
1 parent fc170a6 commit 2ef764d
Show file tree
Hide file tree
Showing 50 changed files with 1,957 additions and 1,363 deletions.
2 changes: 2 additions & 0 deletions packages/clay/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
"access": "public"
},
"devDependencies": {
"@thatopen/components": "^2.2.11",
"@thatopen/fragments": "2.2.0",
"@thatopen/ui": "^2.2.2",
"@types/earcut": "^2.1.4",
"@types/jest": "27.0.0",
"@types/node": "20.11.30",
Expand Down
14 changes: 0 additions & 14 deletions packages/clay/src/base/clay-object.ts

This file was deleted.

2 changes: 0 additions & 2 deletions packages/clay/src/base/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import * as THREE from "three";
import { IFC4X3 as IFC } from "web-ifc";
import { Element } from "../Element";
import { ElementType } from "../ElementType";
import { ClayElement } from "../Element";
import { ClayElementType } from "../ElementType";

export abstract class DynamicElementType<
T extends Element,
> extends ElementType {
/**
* Dynamic variation of {@link ClayElementType}, used in types that need geometry control at the instance level. It's less efficient but more flexible than {@link StaticClayElementType}.
*/
export abstract class DynamicClayElementType<
T extends ClayElement,
> extends ClayElementType {
/**
* {@link ClayElementType.attributes}
*/
abstract attributes: IFC.IfcElementType;

/**
* {@link ClayElementType.addInstance}. It creates a new fragment per instance, allowing for geometry control at the instance level.
*/
addInstance(): T {
const element = this.createElement();
const id = element.attributes.expressID;
Expand All @@ -23,6 +32,9 @@ export abstract class DynamicElementType<
return element;
}

/**
* {@link ClayElementType.addInstance}. Deletes a specific fragment.
*/
deleteInstance(id: number) {
const element = this.elements.get(id);
if (!element) {
Expand All @@ -47,6 +59,10 @@ export abstract class DynamicElementType<
}
}

/**
* Updates all the elements of this type.
* @param updateGeometry whether to update the element geometries or not. Remember that in dynamic types, each element has a
*/
update(updateGeometry = false) {
for (const [_id, element] of this.elements) {
element.update(updateGeometry);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,50 @@
import * as THREE from "three";
import * as FRAGS from "@thatopen/fragments";
import * as WEBIFC from "web-ifc";
import { v4 as uuidv4 } from "uuid";
import { IFC4X3 as IFC } from "web-ifc";
import { ClayObject, Model } from "../../../base";
import { ElementType } from "../ElementType";
import * as WEBIFC from "web-ifc";
import { ClayObject } from "../../Object";
import { Model } from "../../Model";
import { ClayElementType } from "../ElementType";
import { IfcUtils } from "../../../utils/ifc-utils";
import { SimpleOpening } from "../../Openings";

export abstract class Element extends ClayObject {
/**
* Any object with a physical representation in the IFC. It corresponds to the IFCELEMENT entity in the IFC schema.
*/
export abstract class ClayElement extends ClayObject {
/**
* {@link ClayObject.attributes}
*/
abstract attributes: IFC.IfcElement;

/**
* Position of this element in 3D space.
*/
position = new THREE.Vector3();

/**
* Rotation of this element in 3D space.
*/
rotation = new THREE.Euler();

type: ElementType;
/**
* The type of this element.
*/
type: ClayElementType;

/**
* The geometry IDs of this element.
*/
geometries = new Set<number>();

openings = new Map<number, IFC.IfcRelVoidsElement>();
/**
* The list of IFC links to the Elements that create subtractions in this element.
*/
subtractions = new Map<number, IFC.IfcRelVoidsElement>();

/**
* The list of all meshes of the fragments that compose this element.
*/
get meshes() {
const meshes: FRAGS.FragmentMesh[] = [];
for (const id of this.geometries) {
Expand All @@ -33,11 +57,15 @@ export abstract class Element extends ClayObject {
return meshes;
}

protected constructor(model: Model, type: ElementType) {
protected constructor(model: Model, type: ClayElementType) {
super(model);
this.type = type;
}

/**
* Updates this element both in the IFC model and in the 3D scene.
* @param updateGeometry whether to update the geometries of the fragments that compose this element.
*/
update(updateGeometry = false) {
this.updateIfcElement();
const modelID = this.model.modelID;
Expand Down Expand Up @@ -75,46 +103,41 @@ export abstract class Element extends ClayObject {
});
}

private updateIfcElement() {
const placement = this.model.get(
this.attributes.ObjectPlacement,
) as IFC.IfcLocalPlacement;

const relPlacement = this.model.get(
placement.RelativePlacement,
) as IFC.IfcAxis2Placement3D;

IfcUtils.setAxis2Placement(
this.model,
relPlacement,
this.position,
this.rotation,
);

this.model.set(this.attributes);
}
/**
* Adds a new subtraction to this element.
* @param subtraction the element that will be subtracted from this element.
*/
addSubtraction(subtraction: ClayElement) {
if (!(subtraction.attributes instanceof IFC.IfcFeatureElementSubtraction)) {
throw new Error(
"Only elements with attributes of type IfcFeatureElementSubtraction can be used to subtract",
);
}

addOpening(opening: SimpleOpening) {
const voids = new IFC.IfcRelVoidsElement(
new IFC.IfcGloballyUniqueId(uuidv4()),
null,
null,
null,
this.attributes,
opening.attributes,
subtraction.attributes,
);

this.model.set(voids);

const id = opening.attributes.expressID;
this.openings.set(id, voids);
const id = subtraction.attributes.expressID;
this.subtractions.set(id, voids);

this.model.update();
}

removeOpening(opening: SimpleOpening) {
const id = opening.attributes.expressID;
const found = this.openings.get(id);
/**
* Removes an existing subtraction from this element.
* @param subtraction the element whose subtraction will be removed from this element.
*/
removeSubtraction(subtraction: ClayElement) {
const id = subtraction.attributes.expressID;
const found = this.subtractions.get(id);
if (!found) return;
this.model.delete(found);
this.model.update();
Expand Down Expand Up @@ -150,4 +173,23 @@ export abstract class Element extends ClayObject {
geometry.setIndex(Array.from(index));
return geometry as FRAGS.IndexedGeometry;
}

private updateIfcElement() {
const placement = this.model.get(
this.attributes.ObjectPlacement,
) as IFC.IfcLocalPlacement;

const relPlacement = this.model.get(
placement.RelativePlacement,
) as IFC.IfcAxis2Placement3D;

IfcUtils.setAxis2Placement(
this.model,
relPlacement,
this.position,
this.rotation,
);

this.model.set(this.attributes);
}
}
51 changes: 51 additions & 0 deletions packages/clay/src/core/Elements/ElementType/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as FRAGS from "@thatopen/fragments";
import { IFC4X3 as IFC } from "web-ifc";
import * as THREE from "three";
import { ClayObject } from "../../Object";
import { ClayGeometry } from "../../Geometry";
import { ClayElement } from "../Element";

/**
* Base class of all element types in CLAY. In CLAY, types are the managers of instances. In other words: if you want to create a wall, you must first create a wall type, and then use it to create a wall instance. It manages all {@link ClayGeometry}, {@link ClayElement} and {@link FRAGS.Fragment} that belong to this type. It allows to create and delete element instances of this type.
*/
export abstract class ClayElementType<
T extends ClayElement = ClayElement,
> extends ClayObject {
/**
* The IFC data of this object type.
*/
abstract attributes: IFC.IfcElementType;

/**
* All {@link ClayGeometry} that belong to elements of this type.
*/
geometries = new Map<number, ClayGeometry>();

/**
* All {@link ClayElement} of this type.
*/
elements = new Map<number, T>();

/**
* All {@link FRAGS.Fragment} that belong to elements of this type.
*/
fragments = new Map<number, FRAGS.Fragment>();

/**
* Adds a new instance of this type.
*/
abstract addInstance(): T;

/**
* Deletes an existing instance of this type.
*/
abstract deleteInstance(id: number): void;

protected newFragment() {
const geometry = new THREE.BufferGeometry();
geometry.setIndex([]);
const fragment = new FRAGS.Fragment(geometry, this.model.material, 0);
fragment.mesh.frustumCulled = false;
return fragment;
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
import { IFC4X3 as IFC } from "web-ifc";
import * as THREE from "three";
import { Element } from "../Element";
import { ElementType } from "../ElementType";
import { ClayElement } from "../Element";
import { ClayElementType } from "../ElementType";

export abstract class StaticElementType<
T extends Element,
> extends ElementType<T> {
/**
* Static variation of {@link ClayElementType}, used in types that need geometry control at the type level. It's more efficient but less flexible than {@link StaticClayElementType}.
*/
export abstract class StaticClayElementType<
T extends ClayElement,
> extends ClayElementType<T> {
/**
* {@link ClayElementType.attributes}
*/
abstract attributes: IFC.IfcElementType;

/**
* The IFC data containing the geometries of this type (remember that all elements of static types share the same geometry).
*/
abstract shape: IFC.IfcProductDefinitionShape;

/**
* {@link ClayElementType.addInstance}. It creates a new instance to the fragments shared by all elements.
*/
addInstance(): T {
const element = this.createElement();
const id = element.attributes.expressID;
Expand All @@ -26,6 +38,9 @@ export abstract class StaticElementType<
return element;
}

/**
* {@link ClayElementType.addInstance}. Deletes a specific instance in the shared fragments.
*/
deleteInstance(id: number) {
const element = this.elements.get(id);
if (!element) {
Expand All @@ -40,9 +55,14 @@ export abstract class StaticElementType<
}
}

/**
* Updates all the elements of this type.
* @param updateGeometry whether to update the element geometries or not. Remember that in static types, all elements share the same geometries.
*/
update(updateGeometry = false) {
let first = updateGeometry;
for (const [_id, element] of this.elements) {
// Geometry is shared, so only update it in first instance
element.update(first);
first = false;
}
Expand Down
File renamed without changes.
Loading

0 comments on commit 2ef764d

Please sign in to comment.