From fb957cfdc9bffb09c035f505835a64dce19c1e93 Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Thu, 20 Jun 2024 20:30:22 -0500 Subject: [PATCH 01/12] feat(core): adds first poc of bcf integration --- packages/core/src/index.ts | 1 + .../core/src/openbim/BCFManager/example.html | 32 +++ .../core/src/openbim/BCFManager/example.ts | 85 ++++++ packages/core/src/openbim/BCFManager/index.ts | 112 ++++++++ .../src/openbim/BCFManager/src/Exporter.ts | 0 .../src/openbim/BCFManager/src/Importer.ts | 0 .../core/src/openbim/BCFManager/src/Topic.ts | 23 ++ .../src/openbim/BCFManager/src/Viewpoint.ts | 272 ++++++++++++++++++ .../core/src/openbim/BCFManager/src/index.ts | 3 + .../core/src/openbim/BCFManager/src/types.ts | 59 ++++ packages/core/src/openbim/index.ts | 1 + 11 files changed, 588 insertions(+) create mode 100644 packages/core/src/openbim/BCFManager/example.html create mode 100644 packages/core/src/openbim/BCFManager/example.ts create mode 100644 packages/core/src/openbim/BCFManager/index.ts create mode 100644 packages/core/src/openbim/BCFManager/src/Exporter.ts create mode 100644 packages/core/src/openbim/BCFManager/src/Importer.ts create mode 100644 packages/core/src/openbim/BCFManager/src/Topic.ts create mode 100644 packages/core/src/openbim/BCFManager/src/Viewpoint.ts create mode 100644 packages/core/src/openbim/BCFManager/src/index.ts create mode 100644 packages/core/src/openbim/BCFManager/src/types.ts create mode 100644 packages/core/src/openbim/index.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 69e98255c..e49907fa0 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -3,3 +3,4 @@ export * from "./fragments"; export * from "./ifc"; export * from "./measurement"; export * from "./utils"; +export * from "./openbim"; diff --git a/packages/core/src/openbim/BCFManager/example.html b/packages/core/src/openbim/BCFManager/example.html new file mode 100644 index 000000000..d037adc26 --- /dev/null +++ b/packages/core/src/openbim/BCFManager/example.html @@ -0,0 +1,32 @@ + + + + + + + Codestin Search App + + + + + + + + + \ No newline at end of file diff --git a/packages/core/src/openbim/BCFManager/example.ts b/packages/core/src/openbim/BCFManager/example.ts new file mode 100644 index 000000000..925a35063 --- /dev/null +++ b/packages/core/src/openbim/BCFManager/example.ts @@ -0,0 +1,85 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import * as BUI from "@thatopen/ui"; +import * as OBC from "../.."; + +BUI.Manager.init(); + +const components = new OBC.Components(); + +const worlds = components.get(OBC.Worlds); +const world = worlds.create(); + +const sceneComponent = new OBC.SimpleScene(components); +sceneComponent.setup(); +world.scene = sceneComponent; + +const viewport = document.createElement("bim-viewport"); +const rendererComponent = new OBC.SimpleRenderer(components, viewport); +world.renderer = rendererComponent; + +const cameraComponent = new OBC.SimpleCamera(components); +world.camera = cameraComponent; + +viewport.addEventListener("resize", () => { + rendererComponent.resize(); + cameraComponent.updateAspect(); +}); + +const viewerGrids = components.get(OBC.Grids); +viewerGrids.create(world); + +components.init(); + +const ifcLoader = components.get(OBC.IfcLoader); +await ifcLoader.setup(); + +const indexer = components.get(OBC.IfcRelationsIndexer); + +const fragmentsManager = components.get(OBC.FragmentsManager); +fragmentsManager.onFragmentsLoaded.add(async (model) => { + world.scene.three.add(model); + if (model.hasProperties) await indexer.process(model); +}); + +const file = await fetch( + "https://thatopen.github.io/engine_components/resources/small.ifc", +); +const data = await file.arrayBuffer(); +const buffer = new Uint8Array(data); +await ifcLoader.load(buffer); + +// BCF Manager +const viewpoint = new OBC.Viewpoint(components); +viewpoint.world = world; + +console.log(viewpoint); + +const panel = BUI.Component.create(() => { + const onUpdateViewpoint = () => { + console.log({ ...viewpoint.camera }); + viewpoint.update(); + console.log({ ...viewpoint.camera }); + }; + + return BUI.html` + + + + viewpoint.go()}> + + + `; +}); + +const app = document.getElementById("app") as BUI.Grid; +app.layouts = { + main: { + template: ` + "panel viewport" + / 30rem 1fr + `, + elements: { panel, viewport }, + }, +}; + +app.layout = "main"; diff --git a/packages/core/src/openbim/BCFManager/index.ts b/packages/core/src/openbim/BCFManager/index.ts new file mode 100644 index 000000000..6def39178 --- /dev/null +++ b/packages/core/src/openbim/BCFManager/index.ts @@ -0,0 +1,112 @@ +import { Component, Disposable, Event } from "../../core/Types"; +import { Topic } from "./src"; + +export class BCFManager extends Component implements Disposable { + static uuid = "de977976-e4f6-4e4f-a01a-204727839802" as const; + + static types: Set = new Set(); + static readonly onTypeAdded = new Event(); + static addType(...name: string[]) { + for (const type of name) { + BCFManager.types.add(type); + BCFManager.onTypeAdded.trigger(type); + } + } + + static statuses: Set = new Set(); + static readonly onStatusAdded = new Event(); + static addStatus(...name: string[]) { + for (const status of name) { + BCFManager.statuses.add(status); + BCFManager.onStatusAdded.trigger(status); + } + } + + static priorities: Set = new Set(); + static readonly onPriorityAdded = new Event(); + static addPriority(...name: string[]) { + for (const priority of name) { + BCFManager.priorities.add(priority); + BCFManager.onPriorityAdded.trigger(priority); + } + return BCFManager.priorities; + } + + static labels: Set = new Set(); + static readonly onLabelAdded = new Event(); + static addLabel(...name: string[]) { + for (const label of name) { + BCFManager.labels.add(label); + BCFManager.onLabelAdded.trigger(label); + } + } + + static stages: Set = new Set(); + static readonly onStageAdded = new Event(); + static addStage(...name: string[]) { + for (const stage of name) { + BCFManager.stages.add(stage); + BCFManager.onStageAdded.trigger(stage); + } + } + + static users: Set = new Set(); + static readonly onUserAdded = new Event(); + static addUser(...name: string[]) { + for (const user of name) { + BCFManager.users.add(user); + BCFManager.onUserAdded.trigger(user); + } + return BCFManager.users; + } + + static getXMLExtensions() { + let types = ""; + for (const type of BCFManager.types) { + types += `${type}`; + } + let statuses = ""; + for (const type of BCFManager.statuses) { + statuses += `${type}`; + } + let priorities = ""; + for (const type of BCFManager.priorities) { + priorities += `${type}`; + } + let labels = ""; + for (const type of BCFManager.labels) { + labels += `${type}`; + } + let stages = ""; + for (const type of BCFManager.stages) { + stages += `${type}`; + } + let users = ""; + for (const type of BCFManager.users) { + users += `${type}`; + } + return ` + + + ${types} + ${statuses} + ${priorities} + ${labels} + ${stages} + ${users} + + `; + } + + enabled = true; + readonly list: Topic[] = []; + + readonly onDisposed = new Event(); + dispose() { + (this.list as any) = []; + this.onDisposed.trigger(); + this.onDisposed.reset(); + } +} + +export * from "./src"; diff --git a/packages/core/src/openbim/BCFManager/src/Exporter.ts b/packages/core/src/openbim/BCFManager/src/Exporter.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/core/src/openbim/BCFManager/src/Importer.ts b/packages/core/src/openbim/BCFManager/src/Importer.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/core/src/openbim/BCFManager/src/Topic.ts b/packages/core/src/openbim/BCFManager/src/Topic.ts new file mode 100644 index 000000000..bcaa9dd4c --- /dev/null +++ b/packages/core/src/openbim/BCFManager/src/Topic.ts @@ -0,0 +1,23 @@ +import { UUID } from "../../../utils"; +import { BCFTopic, BCFTopicComment, BCFViewpoint } from "./types"; + +export class Topic implements BCFTopic { + guid = UUID.create(); + title = "BCF Topic"; + type = "Issue"; + creationAuthor = "jhon.doe@example.com"; + creationDate = new Date(); + status = "Active"; + comments: BCFTopicComment[] = []; + viewpoints: BCFViewpoint[] = []; + customData: Record = {}; + description?: string | undefined; + serverAssignedId?: string | undefined; + priority?: string | undefined; + stage?: string | undefined; + labels?: string[] | undefined; + assignedTo?: string | undefined; + dueDate?: Date | undefined; + modifiedAuthor?: string | undefined; + modifiedDate?: Date | undefined; +} diff --git a/packages/core/src/openbim/BCFManager/src/Viewpoint.ts b/packages/core/src/openbim/BCFManager/src/Viewpoint.ts new file mode 100644 index 000000000..5f98043ac --- /dev/null +++ b/packages/core/src/openbim/BCFManager/src/Viewpoint.ts @@ -0,0 +1,272 @@ +import * as FRAGS from "@thatopen/fragments"; +import * as THREE from "three"; +import { + BCFViewpoint, + ViewpointOrthographicCamera, + ViewpointPerspectiveCamera, +} from "./types"; +import { UUID } from "../../../utils"; +import { Components } from "../../../core/Components"; +import { World } from "../../../core/Types/src/world"; +import { FragmentsManager } from "../../../fragments/FragmentsManager"; +import { + CameraProjection, + OrthoPerspectiveCamera, +} from "../../../core/OrthoPerspectiveCamera"; + +export class Viewpoint implements BCFViewpoint { + guid = UUID.create(); + coordinationMatrix = new THREE.Matrix4(); + camera: ViewpointPerspectiveCamera | ViewpointOrthographicCamera = { + aspect: 0, + fov: 0, + direction: { x: 0, y: 0, z: 0 }, + position: { x: 0, y: 0, z: 0 }, + }; + + selectionComponents: FRAGS.FragmentIdMap = {}; + spacesVisible = false; + spaceBoundariesVisible = false; + openingsVisible = false; + defaultVisibility = true; + + get cameraProjection(): CameraProjection { + if ("fov" in this.camera) { + return "Perspective"; + } + return "Orthographic"; + } + + get cameraPosition() { + const { position } = this.camera; + const { x, y, z } = position; + return new THREE.Vector3(x, y, z); + } + + get cameraDirection() { + const { direction } = this.camera; + const { x, y, z } = direction; + return new THREE.Vector3(x, y, z); + } + + private _world: World | null = null; + + get world(): World { + if (!this._world) { + throw new Error("Viewport: no world has been set."); + } + return this._world; + } + + set world(world: World | null) { + this._world = world; + if (world) this.update(); + } + + private _components: Components; + + constructor(components: Components) { + this._components = components; + } + + set(data: Partial) { + const { + guid, + camera, + selectionComponents, + spacesVisible, + spaceBoundariesVisible, + openingsVisible, + defaultVisibility, + } = data; + if (guid) { + this.guid = guid; + } + if (camera) { + this.camera = camera; + } + if (selectionComponents) { + this.selectionComponents = selectionComponents as FRAGS.FragmentIdMap; + } + if (spacesVisible) { + this.spacesVisible = spacesVisible; + } + if (spaceBoundariesVisible) { + this.spaceBoundariesVisible = spaceBoundariesVisible; + } + if (openingsVisible) { + this.openingsVisible = openingsVisible; + } + if (defaultVisibility) { + this.defaultVisibility = defaultVisibility; + } + } + + async go(transition = true) { + const { camera } = this.world; + if (!camera.hasCameraControls()) { + throw new Error("Viewpoint: camera controls are needed."); + } + + if (camera instanceof OrthoPerspectiveCamera) { + camera.projection.set(this.cameraProjection); + } + + const controls = camera.controls; + const position = this.cameraPosition; + + const fragments = this._components.get(FragmentsManager); + const baseModel = fragments.groups.get(fragments.baseCoordinationModel); + if (baseModel) { + position.applyMatrix4(baseModel.coordinationMatrix.clone().invert()); + } + + const target = { + x: position.x + this.cameraDirection.x, + y: position.y + this.cameraDirection.y, + z: position.z + this.cameraDirection.z, + }; + + await controls.setLookAt( + position.x, + position.y, + position.z, + target.x, + target.y, + target.z, + transition, + ); + } + + update() { + const { camera, renderer } = this.world; + if (!renderer) { + throw new Error("Viewpoint: the world needs to have a renderer!"); + } + + if (!camera.hasCameraControls()) { + throw new Error("Viewpoint: world's camera need camera controls!"); + } + + const position = new THREE.Vector3(); + camera.controls.getPosition(position); + + const threeCamera = camera.three; + + const direction = new THREE.Vector3(0, 0, -1).applyEuler( + threeCamera.rotation, + ); + + const { width, height } = renderer.getSize(); + let aspect = width / height; + + // If the renderer exists but there is no HTMLElement, then aspect will be 0 / 0. In that case, use 1 as a fallback. + if (Number.isNaN(aspect)) aspect = 1; + + const fragments = this._components.get(FragmentsManager); + const baseModel = fragments.groups.get(fragments.baseCoordinationModel); + if (baseModel) position.applyMatrix4(baseModel.coordinationMatrix); + + const partialCamera = { + aspect, + position: { x: position.x, y: position.y, z: position.z }, + direction: { x: direction.x, y: direction.y, z: direction.z }, + }; + + if (threeCamera instanceof THREE.PerspectiveCamera) { + this.camera = { + ...partialCamera, + fov: threeCamera.fov, + }; + } else if (threeCamera instanceof THREE.OrthographicCamera) { + this.camera = { + ...partialCamera, + viewtoWorld: threeCamera.top - threeCamera.bottom, + }; + } + } + + async getXML() { + const fragmentManager = this._components.get(FragmentsManager); + + let componentSelection = ""; + for (const fragmentID in this.selectionComponents) { + const fragment = fragmentManager.list.get(fragmentID); + if (!(fragment && fragment.group)) continue; + const model = fragment.group; + const expressIDs = this.selectionComponents[fragmentID]; + for (const expressID of expressIDs) { + const attrs = await model.getProperties(expressID); + if (!attrs) continue; + const globalID = attrs.GlobalId?.value as string | undefined; + const tag = attrs.Tag?.value as string | undefined; + if (!(globalID || tag)) continue; + const componentAttributes = `${globalID ? `IfcGuid="${globalID}"` : ""} ${tag ? `AuthoringToolId="${tag}"` : ""}`; + componentSelection += `\n`; + } + } + + const coordinationMatrix = new THREE.Matrix4(); + const baseModel = fragmentManager.groups.get( + fragmentManager.baseCoordinationModel, + ); + if (baseModel) { + coordinationMatrix.copy(baseModel.coordinationMatrix); + } + const position = this.cameraPosition + .clone() + .applyMatrix4(this.coordinationMatrix.clone().invert()); + position.applyMatrix4(coordinationMatrix); + + const cameraViewpointXML = ` + ${position.x} + ${-position.z} + ${position.y} + `; + + const cameraDirectionXML = ` + ${this.camera.direction.x} + ${-this.camera.direction.z} + ${this.camera.direction.y} + `; + + const cameraUpVectorXML = ` + 0 + 0 + 1 + `; + + const cameraRatioXML = `${this.camera.aspect}`; + + let cameraXML = ""; + if ("viewToWorld" in this.camera) { + cameraXML = ` + ${cameraViewpointXML} + ${cameraDirectionXML} + ${cameraUpVectorXML} + ${cameraRatioXML} + ${this.camera.viewToWorld} + `; + } else if ("fov" in this.camera) { + cameraXML = ` + ${cameraViewpointXML} + ${cameraDirectionXML} + ${cameraUpVectorXML} + ${cameraRatioXML} + ${this.camera.fov} + `; + } + + return ` + + + + + ${componentSelection} + + + + ${cameraXML} + `; + } +} diff --git a/packages/core/src/openbim/BCFManager/src/index.ts b/packages/core/src/openbim/BCFManager/src/index.ts new file mode 100644 index 000000000..fe43e8414 --- /dev/null +++ b/packages/core/src/openbim/BCFManager/src/index.ts @@ -0,0 +1,3 @@ +export * from "./Topic"; +export * from "./Viewpoint"; +export * from "./types"; diff --git a/packages/core/src/openbim/BCFManager/src/types.ts b/packages/core/src/openbim/BCFManager/src/types.ts new file mode 100644 index 000000000..60c5577f1 --- /dev/null +++ b/packages/core/src/openbim/BCFManager/src/types.ts @@ -0,0 +1,59 @@ +import * as FRAGS from "@thatopen/fragments"; + +export interface ViewpointPerspectiveCamera { + aspect: number; + fov: number; + direction: { x: number; y: number; z: number }; + position: { x: number; y: number; z: number }; +} + +export interface ViewpointOrthographicCamera { + aspect: number; + viewtoWorld: number; + direction: { x: number; y: number; z: number }; + position: { x: number; y: number; z: number }; +} + +export interface BCFViewpoint { + guid: string; + camera: ViewpointPerspectiveCamera | ViewpointOrthographicCamera; + selectionComponents: + | FRAGS.FragmentIdMap + | Record + | Record; + spacesVisible: boolean; + spaceBoundariesVisible: boolean; + openingsVisible: boolean; + defaultVisibility: boolean; +} + +export interface BCFTopicComment extends Record { + author: string; + guid: string; + comment: string; + date: Date; + viewpoint: string; + modifiedAuthor?: string; + modifiedDate?: Date; +} + +export interface BCFTopic extends Record { + guid: string; + title: string; + type: string; + creationAuthor: string; + creationDate: Date; + status: string; + comments: BCFTopicComment[]; + viewpoints: BCFViewpoint[]; + customData: Record; + description?: string; + serverAssignedId?: string; + priority?: string; + stage?: string; + labels?: string[]; + assignedTo?: string; + dueDate?: Date; + modifiedAuthor?: string; + modifiedDate?: Date; +} diff --git a/packages/core/src/openbim/index.ts b/packages/core/src/openbim/index.ts new file mode 100644 index 000000000..47fb5d8b2 --- /dev/null +++ b/packages/core/src/openbim/index.ts @@ -0,0 +1 @@ +export * from "./BCFManager"; From ce3cb072e4ba7ffda3c3413f53fcd35b3ad5f09d Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Thu, 18 Jul 2024 09:00:39 -0500 Subject: [PATCH 02/12] restructures folders --- .../Viewpoint.ts => core/Viewpoints/index.ts} | 37 ++++++++++++------- .../core/src/core/Viewpoints/src/index.ts | 1 + .../core/src/core/Viewpoints/src/types.ts | 28 ++++++++++++++ packages/core/src/core/index.ts | 1 + .../core/src/openbim/BCFManager/src/Topic.ts | 21 ++++++----- .../core/src/openbim/BCFManager/src/index.ts | 2 +- .../core/src/openbim/BCFManager/src/types.ts | 29 +-------------- 7 files changed, 67 insertions(+), 52 deletions(-) rename packages/core/src/{openbim/BCFManager/src/Viewpoint.ts => core/Viewpoints/index.ts} (90%) create mode 100644 packages/core/src/core/Viewpoints/src/index.ts create mode 100644 packages/core/src/core/Viewpoints/src/types.ts diff --git a/packages/core/src/openbim/BCFManager/src/Viewpoint.ts b/packages/core/src/core/Viewpoints/index.ts similarity index 90% rename from packages/core/src/openbim/BCFManager/src/Viewpoint.ts rename to packages/core/src/core/Viewpoints/index.ts index 5f98043ac..9aa0de2ce 100644 --- a/packages/core/src/openbim/BCFManager/src/Viewpoint.ts +++ b/packages/core/src/core/Viewpoints/index.ts @@ -4,19 +4,27 @@ import { BCFViewpoint, ViewpointOrthographicCamera, ViewpointPerspectiveCamera, -} from "./types"; -import { UUID } from "../../../utils"; -import { Components } from "../../../core/Components"; -import { World } from "../../../core/Types/src/world"; -import { FragmentsManager } from "../../../fragments/FragmentsManager"; +} from "./src"; +import { UUID } from "../../utils"; +import { World, Component } from "../Types"; +import { Components } from "../Components"; +import { FragmentsManager } from "../../fragments/FragmentsManager"; import { CameraProjection, OrthoPerspectiveCamera, -} from "../../../core/OrthoPerspectiveCamera"; +} from "../OrthoPerspectiveCamera"; + +export class Viewpoints extends Component implements BCFViewpoint { + static readonly uuid = "ee867824-a796-408d-8aa0-4e5962a83c66" as const; + enabled = true; + + readonly list = new Map(); -export class Viewpoint implements BCFViewpoint { guid = UUID.create(); + + // The transformation matrix used at the time of creation coordinationMatrix = new THREE.Matrix4(); + camera: ViewpointPerspectiveCamera | ViewpointOrthographicCamera = { aspect: 0, fov: 0, @@ -30,6 +38,8 @@ export class Viewpoint implements BCFViewpoint { openingsVisible = false; defaultVisibility = true; + create(world: World) {} + get cameraProjection(): CameraProjection { if ("fov" in this.camera) { return "Perspective"; @@ -63,10 +73,9 @@ export class Viewpoint implements BCFViewpoint { if (world) this.update(); } - private _components: Components; - constructor(components: Components) { - this._components = components; + super(components); + components.add(Viewpoints.uuid, this); } set(data: Partial) { @@ -115,7 +124,7 @@ export class Viewpoint implements BCFViewpoint { const controls = camera.controls; const position = this.cameraPosition; - const fragments = this._components.get(FragmentsManager); + const fragments = this.components.get(FragmentsManager); const baseModel = fragments.groups.get(fragments.baseCoordinationModel); if (baseModel) { position.applyMatrix4(baseModel.coordinationMatrix.clone().invert()); @@ -163,7 +172,7 @@ export class Viewpoint implements BCFViewpoint { // If the renderer exists but there is no HTMLElement, then aspect will be 0 / 0. In that case, use 1 as a fallback. if (Number.isNaN(aspect)) aspect = 1; - const fragments = this._components.get(FragmentsManager); + const fragments = this.components.get(FragmentsManager); const baseModel = fragments.groups.get(fragments.baseCoordinationModel); if (baseModel) position.applyMatrix4(baseModel.coordinationMatrix); @@ -187,7 +196,7 @@ export class Viewpoint implements BCFViewpoint { } async getXML() { - const fragmentManager = this._components.get(FragmentsManager); + const fragmentManager = this.components.get(FragmentsManager); let componentSelection = ""; for (const fragmentID in this.selectionComponents) { @@ -270,3 +279,5 @@ export class Viewpoint implements BCFViewpoint { `; } } + +export * from "./src"; diff --git a/packages/core/src/core/Viewpoints/src/index.ts b/packages/core/src/core/Viewpoints/src/index.ts new file mode 100644 index 000000000..eea524d65 --- /dev/null +++ b/packages/core/src/core/Viewpoints/src/index.ts @@ -0,0 +1 @@ +export * from "./types"; diff --git a/packages/core/src/core/Viewpoints/src/types.ts b/packages/core/src/core/Viewpoints/src/types.ts new file mode 100644 index 000000000..551c44492 --- /dev/null +++ b/packages/core/src/core/Viewpoints/src/types.ts @@ -0,0 +1,28 @@ +import * as FRAGS from "@thatopen/fragments"; + +export interface ViewpointPerspectiveCamera { + aspect: number; + fov: number; + direction: { x: number; y: number; z: number }; + position: { x: number; y: number; z: number }; +} + +export interface ViewpointOrthographicCamera { + aspect: number; + viewtoWorld: number; + direction: { x: number; y: number; z: number }; + position: { x: number; y: number; z: number }; +} + +export interface BCFViewpoint { + guid: string; + camera: ViewpointPerspectiveCamera | ViewpointOrthographicCamera; + selectionComponents: + | FRAGS.FragmentIdMap + | Record + | Record; + spacesVisible: boolean; + spaceBoundariesVisible: boolean; + openingsVisible: boolean; + defaultVisibility: boolean; +} diff --git a/packages/core/src/core/index.ts b/packages/core/src/core/index.ts index c100866d0..34fa8e907 100644 --- a/packages/core/src/core/index.ts +++ b/packages/core/src/core/index.ts @@ -6,5 +6,6 @@ export * from "./Worlds"; export * from "./Grids"; export * from "./Clipper"; export * from "./Cullers"; +export * from "./Viewpoints"; export * from "./MiniMap"; export * from "./OrthoPerspectiveCamera"; diff --git a/packages/core/src/openbim/BCFManager/src/Topic.ts b/packages/core/src/openbim/BCFManager/src/Topic.ts index bcaa9dd4c..0eff4902a 100644 --- a/packages/core/src/openbim/BCFManager/src/Topic.ts +++ b/packages/core/src/openbim/BCFManager/src/Topic.ts @@ -1,5 +1,6 @@ import { UUID } from "../../../utils"; -import { BCFTopic, BCFTopicComment, BCFViewpoint } from "./types"; +import { BCFTopic, BCFTopicComment } from "./types"; +import { BCFViewpoint } from "../../../core"; export class Topic implements BCFTopic { guid = UUID.create(); @@ -11,13 +12,13 @@ export class Topic implements BCFTopic { comments: BCFTopicComment[] = []; viewpoints: BCFViewpoint[] = []; customData: Record = {}; - description?: string | undefined; - serverAssignedId?: string | undefined; - priority?: string | undefined; - stage?: string | undefined; - labels?: string[] | undefined; - assignedTo?: string | undefined; - dueDate?: Date | undefined; - modifiedAuthor?: string | undefined; - modifiedDate?: Date | undefined; + description?: string; + serverAssignedId?: string; + priority?: string; + stage?: string; + labels?: string[]; + assignedTo?: string; + dueDate?: Date; + modifiedAuthor?: string; + modifiedDate?: Date; } diff --git a/packages/core/src/openbim/BCFManager/src/index.ts b/packages/core/src/openbim/BCFManager/src/index.ts index fe43e8414..f5be28811 100644 --- a/packages/core/src/openbim/BCFManager/src/index.ts +++ b/packages/core/src/openbim/BCFManager/src/index.ts @@ -1,3 +1,3 @@ export * from "./Topic"; -export * from "./Viewpoint"; +export * from "./Viewpoints"; export * from "./types"; diff --git a/packages/core/src/openbim/BCFManager/src/types.ts b/packages/core/src/openbim/BCFManager/src/types.ts index 60c5577f1..fc02cc482 100644 --- a/packages/core/src/openbim/BCFManager/src/types.ts +++ b/packages/core/src/openbim/BCFManager/src/types.ts @@ -1,31 +1,4 @@ -import * as FRAGS from "@thatopen/fragments"; - -export interface ViewpointPerspectiveCamera { - aspect: number; - fov: number; - direction: { x: number; y: number; z: number }; - position: { x: number; y: number; z: number }; -} - -export interface ViewpointOrthographicCamera { - aspect: number; - viewtoWorld: number; - direction: { x: number; y: number; z: number }; - position: { x: number; y: number; z: number }; -} - -export interface BCFViewpoint { - guid: string; - camera: ViewpointPerspectiveCamera | ViewpointOrthographicCamera; - selectionComponents: - | FRAGS.FragmentIdMap - | Record - | Record; - spacesVisible: boolean; - spaceBoundariesVisible: boolean; - openingsVisible: boolean; - defaultVisibility: boolean; -} +import { BCFViewpoint } from "../../../core/Viewpoints"; export interface BCFTopicComment extends Record { author: string; From 6560a638862ef285aa687ec1e74ecbd6d0cc4771 Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Thu, 18 Jul 2024 17:56:15 -0500 Subject: [PATCH 03/12] many changes to the bcf implementation --- package.json | 2 +- packages/core/package.json | 4 +- packages/core/src/core/Viewpoints/index.ts | 282 ++-------------- .../core/src/core/Viewpoints/src/Viewpoint.ts | 303 ++++++++++++++++++ .../core/src/core/Viewpoints/src/index.ts | 1 + .../core/src/core/Viewpoints/src/types.ts | 7 +- .../core/src/openbim/BCFManager/example.ts | 17 +- packages/core/src/openbim/BCFManager/index.ts | 203 +++++++----- .../src/openbim/BCFManager/src/Comment.ts | 87 +++++ .../src/openbim/BCFManager/src/Exporter.ts | 0 .../src/openbim/BCFManager/src/Importer.ts | 0 .../core/src/openbim/BCFManager/src/Topic.ts | 126 +++++++- .../core/src/openbim/BCFManager/src/index.ts | 1 - .../core/src/openbim/BCFManager/src/types.ts | 6 +- yarn.lock | 123 ++++++- 15 files changed, 790 insertions(+), 372 deletions(-) create mode 100644 packages/core/src/core/Viewpoints/src/Viewpoint.ts create mode 100644 packages/core/src/openbim/BCFManager/src/Comment.ts delete mode 100644 packages/core/src/openbim/BCFManager/src/Exporter.ts delete mode 100644 packages/core/src/openbim/BCFManager/src/Importer.ts diff --git a/package.json b/package.json index a46b8f379..ae93c18a5 100644 --- a/package.json +++ b/package.json @@ -44,4 +44,4 @@ "vite-plugin-dts": "3.7.3" }, "version": "2.1.0" -} \ No newline at end of file +} diff --git a/packages/core/package.json b/packages/core/package.json index e637eecc9..d3fde4a62 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -46,6 +46,8 @@ }, "dependencies": { "camera-controls": "2.7.3", + "fast-xml-parser": "latest", + "jszip": "latest", "three-mesh-bvh": "0.7.0" }, "peerDependencies": { @@ -53,4 +55,4 @@ "three": "^0.160.1", "web-ifc": "0.0.56" } -} \ No newline at end of file +} diff --git a/packages/core/src/core/Viewpoints/index.ts b/packages/core/src/core/Viewpoints/index.ts index 9aa0de2ce..5226d3ae3 100644 --- a/packages/core/src/core/Viewpoints/index.ts +++ b/packages/core/src/core/Viewpoints/index.ts @@ -1,76 +1,28 @@ -import * as FRAGS from "@thatopen/fragments"; -import * as THREE from "three"; -import { - BCFViewpoint, - ViewpointOrthographicCamera, - ViewpointPerspectiveCamera, -} from "./src"; -import { UUID } from "../../utils"; -import { World, Component } from "../Types"; +import { World, Component, Disposable, Event } from "../Types"; import { Components } from "../Components"; -import { FragmentsManager } from "../../fragments/FragmentsManager"; -import { - CameraProjection, - OrthoPerspectiveCamera, -} from "../OrthoPerspectiveCamera"; +import { Viewpoint } from "./src"; -export class Viewpoints extends Component implements BCFViewpoint { +export class Viewpoints extends Component implements Disposable { static readonly uuid = "ee867824-a796-408d-8aa0-4e5962a83c66" as const; enabled = true; - readonly list = new Map(); + readonly list = new Set(); - guid = UUID.create(); + readonly onViewpointCreated = new Event(); + readonly onViewpointDeleted = new Event(); - // The transformation matrix used at the time of creation - coordinationMatrix = new THREE.Matrix4(); - - camera: ViewpointPerspectiveCamera | ViewpointOrthographicCamera = { - aspect: 0, - fov: 0, - direction: { x: 0, y: 0, z: 0 }, - position: { x: 0, y: 0, z: 0 }, - }; - - selectionComponents: FRAGS.FragmentIdMap = {}; - spacesVisible = false; - spaceBoundariesVisible = false; - openingsVisible = false; - defaultVisibility = true; - - create(world: World) {} - - get cameraProjection(): CameraProjection { - if ("fov" in this.camera) { - return "Perspective"; - } - return "Orthographic"; - } - - get cameraPosition() { - const { position } = this.camera; - const { x, y, z } = position; - return new THREE.Vector3(x, y, z); + create(world: World) { + const viewpoint = new Viewpoint(this.components, world); + this.list.add(viewpoint); + this.onViewpointCreated.trigger(viewpoint); + return viewpoint; } - get cameraDirection() { - const { direction } = this.camera; - const { x, y, z } = direction; - return new THREE.Vector3(x, y, z); - } - - private _world: World | null = null; - - get world(): World { - if (!this._world) { - throw new Error("Viewport: no world has been set."); - } - return this._world; - } - - set world(world: World | null) { - this._world = world; - if (world) this.update(); + delete(viewpoint: Viewpoint) { + const { guid } = viewpoint; + const deleted = this.list.delete(viewpoint); + this.onViewpointDeleted.trigger(guid); + return deleted; } constructor(components: Components) { @@ -78,205 +30,11 @@ export class Viewpoints extends Component implements BCFViewpoint { components.add(Viewpoints.uuid, this); } - set(data: Partial) { - const { - guid, - camera, - selectionComponents, - spacesVisible, - spaceBoundariesVisible, - openingsVisible, - defaultVisibility, - } = data; - if (guid) { - this.guid = guid; - } - if (camera) { - this.camera = camera; - } - if (selectionComponents) { - this.selectionComponents = selectionComponents as FRAGS.FragmentIdMap; - } - if (spacesVisible) { - this.spacesVisible = spacesVisible; - } - if (spaceBoundariesVisible) { - this.spaceBoundariesVisible = spaceBoundariesVisible; - } - if (openingsVisible) { - this.openingsVisible = openingsVisible; - } - if (defaultVisibility) { - this.defaultVisibility = defaultVisibility; - } - } - - async go(transition = true) { - const { camera } = this.world; - if (!camera.hasCameraControls()) { - throw new Error("Viewpoint: camera controls are needed."); - } - - if (camera instanceof OrthoPerspectiveCamera) { - camera.projection.set(this.cameraProjection); - } - - const controls = camera.controls; - const position = this.cameraPosition; - - const fragments = this.components.get(FragmentsManager); - const baseModel = fragments.groups.get(fragments.baseCoordinationModel); - if (baseModel) { - position.applyMatrix4(baseModel.coordinationMatrix.clone().invert()); - } - - const target = { - x: position.x + this.cameraDirection.x, - y: position.y + this.cameraDirection.y, - z: position.z + this.cameraDirection.z, - }; - - await controls.setLookAt( - position.x, - position.y, - position.z, - target.x, - target.y, - target.z, - transition, - ); - } - - update() { - const { camera, renderer } = this.world; - if (!renderer) { - throw new Error("Viewpoint: the world needs to have a renderer!"); - } - - if (!camera.hasCameraControls()) { - throw new Error("Viewpoint: world's camera need camera controls!"); - } - - const position = new THREE.Vector3(); - camera.controls.getPosition(position); - - const threeCamera = camera.three; - - const direction = new THREE.Vector3(0, 0, -1).applyEuler( - threeCamera.rotation, - ); - - const { width, height } = renderer.getSize(); - let aspect = width / height; - - // If the renderer exists but there is no HTMLElement, then aspect will be 0 / 0. In that case, use 1 as a fallback. - if (Number.isNaN(aspect)) aspect = 1; - - const fragments = this.components.get(FragmentsManager); - const baseModel = fragments.groups.get(fragments.baseCoordinationModel); - if (baseModel) position.applyMatrix4(baseModel.coordinationMatrix); - - const partialCamera = { - aspect, - position: { x: position.x, y: position.y, z: position.z }, - direction: { x: direction.x, y: direction.y, z: direction.z }, - }; - - if (threeCamera instanceof THREE.PerspectiveCamera) { - this.camera = { - ...partialCamera, - fov: threeCamera.fov, - }; - } else if (threeCamera instanceof THREE.OrthographicCamera) { - this.camera = { - ...partialCamera, - viewtoWorld: threeCamera.top - threeCamera.bottom, - }; - } - } - - async getXML() { - const fragmentManager = this.components.get(FragmentsManager); - - let componentSelection = ""; - for (const fragmentID in this.selectionComponents) { - const fragment = fragmentManager.list.get(fragmentID); - if (!(fragment && fragment.group)) continue; - const model = fragment.group; - const expressIDs = this.selectionComponents[fragmentID]; - for (const expressID of expressIDs) { - const attrs = await model.getProperties(expressID); - if (!attrs) continue; - const globalID = attrs.GlobalId?.value as string | undefined; - const tag = attrs.Tag?.value as string | undefined; - if (!(globalID || tag)) continue; - const componentAttributes = `${globalID ? `IfcGuid="${globalID}"` : ""} ${tag ? `AuthoringToolId="${tag}"` : ""}`; - componentSelection += `\n`; - } - } - - const coordinationMatrix = new THREE.Matrix4(); - const baseModel = fragmentManager.groups.get( - fragmentManager.baseCoordinationModel, - ); - if (baseModel) { - coordinationMatrix.copy(baseModel.coordinationMatrix); - } - const position = this.cameraPosition - .clone() - .applyMatrix4(this.coordinationMatrix.clone().invert()); - position.applyMatrix4(coordinationMatrix); - - const cameraViewpointXML = ` - ${position.x} - ${-position.z} - ${position.y} - `; - - const cameraDirectionXML = ` - ${this.camera.direction.x} - ${-this.camera.direction.z} - ${this.camera.direction.y} - `; - - const cameraUpVectorXML = ` - 0 - 0 - 1 - `; - - const cameraRatioXML = `${this.camera.aspect}`; - - let cameraXML = ""; - if ("viewToWorld" in this.camera) { - cameraXML = ` - ${cameraViewpointXML} - ${cameraDirectionXML} - ${cameraUpVectorXML} - ${cameraRatioXML} - ${this.camera.viewToWorld} - `; - } else if ("fov" in this.camera) { - cameraXML = ` - ${cameraViewpointXML} - ${cameraDirectionXML} - ${cameraUpVectorXML} - ${cameraRatioXML} - ${this.camera.fov} - `; - } + readonly onDisposed = new Event(); - return ` - - - - - ${componentSelection} - - - - ${cameraXML} - `; + dispose() { + (this.list as any) = new Set(); + this.onDisposed.trigger(); } } diff --git a/packages/core/src/core/Viewpoints/src/Viewpoint.ts b/packages/core/src/core/Viewpoints/src/Viewpoint.ts new file mode 100644 index 000000000..96d5c66dd --- /dev/null +++ b/packages/core/src/core/Viewpoints/src/Viewpoint.ts @@ -0,0 +1,303 @@ +import * as THREE from "three"; +import * as FRAGS from "@thatopen/fragments"; +import { UUID } from "../../../utils"; +import { + BCFViewpoint, + ViewpointOrthographicCamera, + ViewpointPerspectiveCamera, +} from "./types"; +import { + CameraProjection, + OrthoPerspectiveCamera, +} from "../../OrthoPerspectiveCamera"; +import { Components } from "../../Components"; +import { World } from "../../Types"; +import { FragmentsManager } from "../../../fragments/FragmentsManager"; +import { BCFManager } from "../../../openbim/BCFManager"; + +export class Viewpoint { + guid = UUID.create(); + + // The transformation matrix used at the time of creation + coordinationMatrix = new THREE.Matrix4(); + + camera: ViewpointPerspectiveCamera | ViewpointOrthographicCamera = { + aspect: 0, + fov: 0, + direction: { x: 0, y: 0, z: 0 }, + position: { x: 0, y: 0, z: 0 }, + }; + + readonly selectionComponents = new Set(); + spacesVisible = false; + spaceBoundariesVisible = false; + openingsVisible = false; + defaultVisibility = true; + + private get _modelIdMap() { + const fragments = this._components.get(FragmentsManager); + const modelIdMap: { [modelID: string]: Set } = {}; + for (const [id, model] of fragments.groups) { + if (!(id in modelIdMap)) modelIdMap[id] = new Set(); + for (const globalId of this.selectionComponents) { + const expressID = model.globalToExpressIDs.get(globalId); + if (expressID) modelIdMap[id].add(expressID); + } + } + return modelIdMap; + } + + get selection() { + const fragments = this._components.get(FragmentsManager); + const fragmentIdMap = fragments.modelIdToFragmentIdMap(this._modelIdMap); + return fragmentIdMap; + } + + get projection(): CameraProjection { + if ("fov" in this.camera) { + return "Perspective"; + } + return "Orthographic"; + } + + get position() { + const { position } = this.camera; + const { x, y, z } = position; + return new THREE.Vector3(x, y, z); + } + + get direction() { + const { direction } = this.camera; + const { x, y, z } = direction; + return new THREE.Vector3(x, y, z); + } + + private _components: Components; + readonly world: World; + + private get _managerVersion() { + const manager = this._components.get(BCFManager); + return manager.config.version; + } + + constructor(components: Components, world: World) { + this._components = components; + this.world = world; + this.update(); + } + + async addComponentsFromMap(fragmentIdMap: FRAGS.FragmentIdMap) { + const fragments = this._components.get(FragmentsManager); + for (const fragmentID in fragmentIdMap) { + const fragment = fragments.list.get(fragmentID); + if (!(fragment && fragment.group)) continue; + const model = fragment.group; + const expressIDs = fragmentIdMap[fragmentID]; + for (const expressID of expressIDs) { + const attrs = await model.getProperties(expressID); + if (!attrs) continue; + const globalId = attrs.GlobalId?.value; + if (globalId) this.selectionComponents.add(globalId); + } + } + } + + set(data: Partial) { + const { + guid, + camera, + selectionComponents, + spacesVisible, + spaceBoundariesVisible, + openingsVisible, + defaultVisibility, + } = data; + if (guid) this.guid = guid; + if (camera) this.camera = camera; + if (selectionComponents) + (this.selectionComponents as any) = selectionComponents; + if (spacesVisible) this.spacesVisible = spacesVisible; + if (spaceBoundariesVisible) + this.spaceBoundariesVisible = spaceBoundariesVisible; + if (openingsVisible) this.openingsVisible = openingsVisible; + if (defaultVisibility) this.defaultVisibility = defaultVisibility; + } + + async go(transition = true) { + const { camera } = this.world; + if (!camera.hasCameraControls()) { + throw new Error( + "Viewpoint: the world's camera need controls to set the viewpoint.", + ); + } + + if (camera instanceof OrthoPerspectiveCamera) { + camera.projection.set(this.projection); + } + + const controls = camera.controls; + const position = this.position; + + const fragments = this._components.get(FragmentsManager); + fragments.applyBaseCoordinateSystem( + position.clone(), + this.coordinationMatrix, + ); + + const target = { + x: position.x + this.direction.x, + y: position.y + this.direction.y, + z: position.z + this.direction.z, + }; + + await controls.setLookAt( + position.x, + position.y, + position.z, + target.x, + target.y, + target.z, + transition, + ); + } + + update() { + const { camera, renderer } = this.world; + if (!renderer) { + throw new Error("Viewpoint: the world needs to have a renderer!"); + } + + if (!camera.hasCameraControls()) { + throw new Error("Viewpoint: world's camera need camera controls!"); + } + + const position = new THREE.Vector3(); + camera.controls.getPosition(position); + + const threeCamera = camera.three; + + const direction = new THREE.Vector3(0, 0, -1).applyEuler( + threeCamera.rotation, + ); + + const { width, height } = renderer.getSize(); + let aspect = width / height; + + // If the renderer exists but there is no HTMLElement, then aspect will be 0 / 0. In that case, use 1 as a fallback. + if (Number.isNaN(aspect)) aspect = 1; + + const fragments = this._components.get(FragmentsManager); + const baseModel = fragments.groups.get(fragments.baseCoordinationModel); + if (baseModel) position.applyMatrix4(baseModel.coordinationMatrix); + + const partialCamera = { + aspect, + position: { x: position.x, y: position.y, z: position.z }, + direction: { x: direction.x, y: direction.y, z: direction.z }, + }; + + if (threeCamera instanceof THREE.PerspectiveCamera) { + this.camera = { + ...partialCamera, + fov: threeCamera.fov, + }; + } else if (threeCamera instanceof THREE.OrthographicCamera) { + this.camera = { + ...partialCamera, + viewtoWorld: threeCamera.top - threeCamera.bottom, + }; + } + + this.coordinationMatrix = fragments.baseCoordinationMatrix; + } + + async serialize(version = this._managerVersion) { + const fragments = this._components.get(FragmentsManager); + const manager = this._components.get(BCFManager); + + let componentSelection = ""; + if (manager.config.includeSelectionTag) { + componentSelection = [...this.selectionComponents] + .map((globalId) => ``) + .join(`\n`); + } else { + const modelIdMap = this._modelIdMap; + for (const modelID in modelIdMap) { + const model = fragments.groups.get(modelID); + if (!model) continue; + const expressIDs = modelIdMap[modelID]; + for (const expressID of expressIDs) { + const attrs = await model.getProperties(expressID); + if (!attrs) continue; + const globalID = attrs.GlobalId?.value; + if (!globalID) continue; + const tag = attrs.Tag?.value; + let tagAttribute: string | null = null; + if (tag) tagAttribute = `AuthoringToolId="${tag}`; + componentSelection += `\n`; + } + } + } + + const coordinationMatrix = new THREE.Matrix4(); + const baseModel = fragments.groups.get(fragments.baseCoordinationModel); + if (baseModel) { + coordinationMatrix.copy(baseModel.coordinationMatrix); + } + const position = this.position + .clone() + .applyMatrix4(this.coordinationMatrix.clone().invert()); + position.applyMatrix4(coordinationMatrix); + + const cameraViewpointXML = ` + ${position.x} + ${-position.z} + ${position.y} + `; + + const cameraDirectionXML = ` + ${this.camera.direction.x} + ${-this.camera.direction.z} + ${this.camera.direction.y} + `; + + const cameraUpVectorXML = ` + 0 + 0 + 1 + `; + + const cameraRatioXML = `${this.camera.aspect}`; + + let cameraXML = ""; + if ("viewToWorld" in this.camera) { + cameraXML = ` + ${cameraViewpointXML} + ${cameraDirectionXML} + ${cameraUpVectorXML} + ${cameraRatioXML} + ${this.camera.viewToWorld} + `; + } else if ("fov" in this.camera) { + cameraXML = ` + ${cameraViewpointXML} + ${cameraDirectionXML} + ${cameraUpVectorXML} + ${cameraRatioXML} + ${this.camera.fov} + `; + } + + return ` + + + + + ${componentSelection} + + + + ${cameraXML} + `; + } +} diff --git a/packages/core/src/core/Viewpoints/src/index.ts b/packages/core/src/core/Viewpoints/src/index.ts index eea524d65..415d33534 100644 --- a/packages/core/src/core/Viewpoints/src/index.ts +++ b/packages/core/src/core/Viewpoints/src/index.ts @@ -1 +1,2 @@ export * from "./types"; +export * from "./Viewpoint"; diff --git a/packages/core/src/core/Viewpoints/src/types.ts b/packages/core/src/core/Viewpoints/src/types.ts index 551c44492..ccea351fd 100644 --- a/packages/core/src/core/Viewpoints/src/types.ts +++ b/packages/core/src/core/Viewpoints/src/types.ts @@ -1,5 +1,3 @@ -import * as FRAGS from "@thatopen/fragments"; - export interface ViewpointPerspectiveCamera { aspect: number; fov: number; @@ -17,10 +15,7 @@ export interface ViewpointOrthographicCamera { export interface BCFViewpoint { guid: string; camera: ViewpointPerspectiveCamera | ViewpointOrthographicCamera; - selectionComponents: - | FRAGS.FragmentIdMap - | Record - | Record; + selectionComponents: Iterable; spacesVisible: boolean; spaceBoundariesVisible: boolean; openingsVisible: boolean; diff --git a/packages/core/src/openbim/BCFManager/example.ts b/packages/core/src/openbim/BCFManager/example.ts index 925a35063..47ca64299 100644 --- a/packages/core/src/openbim/BCFManager/example.ts +++ b/packages/core/src/openbim/BCFManager/example.ts @@ -46,13 +46,20 @@ const file = await fetch( ); const data = await file.arrayBuffer(); const buffer = new Uint8Array(data); -await ifcLoader.load(buffer); +const model = await ifcLoader.load(buffer); +const selection = model.getFragmentMap([186]); -// BCF Manager -const viewpoint = new OBC.Viewpoint(components); -viewpoint.world = world; +// Viewpoints +const viewpoints = components.get(OBC.Viewpoints); +const viewpoint = viewpoints.create(world); +viewpoint.addComponentsFromMap(selection); +viewpoint.selectionComponents.add("2idC0G3ezCdhA9WVjWemcy"); +// viewpoint.selection gives the fragmentIdMap to select elements with the highlighter from @thatopen/components-front -console.log(viewpoint); +// BCF Manager +const bcfManager = components.get(OBC.BCFManager); +const topic = bcfManager.createTopic(); +topic.viewpoints.add(viewpoint); const panel = BUI.Component.create(() => { const onUpdateViewpoint = () => { diff --git a/packages/core/src/openbim/BCFManager/index.ts b/packages/core/src/openbim/BCFManager/index.ts index 6def39178..5c3f2663e 100644 --- a/packages/core/src/openbim/BCFManager/index.ts +++ b/packages/core/src/openbim/BCFManager/index.ts @@ -1,111 +1,140 @@ -import { Component, Disposable, Event } from "../../core/Types"; +import JSZip from "jszip"; +import { Component, Configurable, Disposable, Event } from "../../core/Types"; import { Topic } from "./src"; -export class BCFManager extends Component implements Disposable { +interface BCFManagerConfig { + version: "2.1" | "3.0"; + author: string; + types: Set; + statuses: Set; + priorities: Set; + labels: Set; + stages: Set; + users: Set; + includeSelectionTag: boolean; +} + +export class BCFManager + extends Component + implements Disposable, Configurable +{ static uuid = "de977976-e4f6-4e4f-a01a-204727839802" as const; + enabled = false; - static types: Set = new Set(); - static readonly onTypeAdded = new Event(); - static addType(...name: string[]) { - for (const type of name) { - BCFManager.types.add(type); - BCFManager.onTypeAdded.trigger(type); - } - } + config: Required = { + author: "jhon.doe@example.com", + version: "2.1", + types: new Set(), + statuses: new Set(), + priorities: new Set(), + labels: new Set(), + stages: new Set(), + users: new Set(), + includeSelectionTag: false, + }; - static statuses: Set = new Set(); - static readonly onStatusAdded = new Event(); - static addStatus(...name: string[]) { - for (const status of name) { - BCFManager.statuses.add(status); - BCFManager.onStatusAdded.trigger(status); - } - } + readonly list = new Set(); - static priorities: Set = new Set(); - static readonly onPriorityAdded = new Event(); - static addPriority(...name: string[]) { - for (const priority of name) { - BCFManager.priorities.add(priority); - BCFManager.onPriorityAdded.trigger(priority); - } - return BCFManager.priorities; + readonly onSetup = new Event(); + isSetup = false; + setup(config?: Partial) { + if (this.isSetup) return; + this.config = { ...this.config, ...config }; + this.isSetup = true; + this.enabled = true; + this.onSetup.trigger(); } - static labels: Set = new Set(); - static readonly onLabelAdded = new Event(); - static addLabel(...name: string[]) { - for (const label of name) { - BCFManager.labels.add(label); - BCFManager.onLabelAdded.trigger(label); - } + readonly onTopicCreated = new Event(); + readonly onTopicDeleted = new Event(); + + createTopic() { + const topic = new Topic(this.components); + this.list.add(topic); + this.onTopicCreated.trigger(topic); + return topic; } - static stages: Set = new Set(); - static readonly onStageAdded = new Event(); - static addStage(...name: string[]) { - for (const stage of name) { - BCFManager.stages.add(stage); - BCFManager.onStageAdded.trigger(stage); - } + deleteTopic(topic: Topic) { + const { guid } = topic; + const deleted = this.list.delete(topic); + this.onTopicDeleted.trigger(guid); + return deleted; } - static users: Set = new Set(); - static readonly onUserAdded = new Event(); - static addUser(...name: string[]) { - for (const user of name) { - BCFManager.users.add(user); - BCFManager.onUserAdded.trigger(user); - } - return BCFManager.users; + readonly onDisposed = new Event(); + dispose() { + (this.list as any) = []; + this.onDisposed.trigger(); + this.onDisposed.reset(); } - static getXMLExtensions() { - let types = ""; - for (const type of BCFManager.types) { - types += `${type}`; - } - let statuses = ""; - for (const type of BCFManager.statuses) { - statuses += `${type}`; - } - let priorities = ""; - for (const type of BCFManager.priorities) { - priorities += `${type}`; - } - let labels = ""; - for (const type of BCFManager.labels) { - labels += `${type}`; - } - let stages = ""; - for (const type of BCFManager.stages) { - stages += `${type}`; - } - let users = ""; - for (const type of BCFManager.users) { - users += `${type}`; - } + serializeExtensions() { + const types = [...this.config.types] + .map((type) => `${type}`) + .join("\n"); + + const statuses = [...this.config.statuses] + .map((status) => `${status}`) + .join("\n"); + + const priorities = [...this.config.priorities] + .map((priority) => `${priority}`) + .join("\n"); + + const labels = [...this.config.labels] + .map((label) => `${label}`) + .join("\n"); + + const stages = [...this.config.stages] + .map((stage) => `${stage}`) + .join("\n"); + + const users = [...this.config.users] + .map((user) => `${user}`) + .join("\n"); + return ` - ${types} - ${statuses} - ${priorities} - ${labels} - ${stages} - ${users} + ${types.length !== 0 ? `\n${types}\n` : ""} + ${statuses.length !== 0 ? `\n${statuses}\n` : ""} + ${priorities.length !== 0 ? `\n${priorities}\n` : ""} + ${labels.length !== 0 ? `\n${labels}\n` : ""} + ${stages.length !== 0 ? `\n${stages}\n` : ""} + ${users.length !== 0 ? `\n${users}\n` : ""} `; } - enabled = true; - readonly list: Topic[] = []; - - readonly onDisposed = new Event(); - dispose() { - (this.list as any) = []; - this.onDisposed.trigger(); - this.onDisposed.reset(); + async export(topics = this.list, fileName = "topics") { + const zip = new JSZip(); + zip.file( + "bcf.version", + ` + + `, + ); + zip.file("bcf.extensions", this.serializeExtensions()); + for (const topic of topics) { + const topicFolder = zip.folder(topic.guid) as JSZip; + topicFolder.file("markup.bcf", topic.serialize()); + for (const viewpoint of topic.viewpoints) { + const image = await fetch("/topic-viewpoint-snapshot.jpeg"); + topicFolder.file(`${viewpoint.guid}.jpeg`, image.blob(), { + binary: true, + }); + topicFolder.file(`${viewpoint.guid}.bcfv`, await viewpoint.serialize()); + } + } + const a = document.createElement("a"); + const content = await zip.generateAsync({ type: "blob" }); + a.href = URL.createObjectURL(content); + a.download = `${fileName}.bcf`; + a.click(); + URL.revokeObjectURL(a.href); + a.remove(); } } diff --git a/packages/core/src/openbim/BCFManager/src/Comment.ts b/packages/core/src/openbim/BCFManager/src/Comment.ts new file mode 100644 index 000000000..36044b3a1 --- /dev/null +++ b/packages/core/src/openbim/BCFManager/src/Comment.ts @@ -0,0 +1,87 @@ +import { BCFManager } from ".."; +import { Viewpoint } from "../../../core/Viewpoints"; +import { Components } from "../../../core/Components"; +import { UUID } from "../../../utils"; + +/** + * Represents a comment in a BCF Topic. + */ +export class Comment { + date = new Date(); + author: string; + guid = UUID.create(); + viewpoint?: Viewpoint; + modifiedAuthor?: string; + modifiedDate?: Date; + + private get _managerVersion() { + const manager = this._components.get(BCFManager); + return manager.config.version; + } + + private _components: Components; + private _comment: string = ""; + + /** + * Sets the comment text and updates the modified date and author. + * @param value - The new comment text. + */ + set comment(value: string) { + const manager = this._components.get(BCFManager); + this._comment = value; + this.modifiedDate = new Date(); + this.modifiedAuthor = manager.config.author; + } + + /** + * Gets the comment text. + * @returns The comment text. + */ + get comment() { + return this._comment; + } + + /** + * Constructs a new BCF Topic Comment instance. + * @param components - The Components instance. + * @param text - The initial comment text. + */ + constructor(components: Components, text: string) { + this._components = components; + this._comment = text; // Set the comment to the private property to prevent setting a modifiedDate and author + const manager = this._components.get(BCFManager); + this.author = manager.config.author; + } + + /** + * Serializes the Comment instance to an XML string. + * @returns The serialized XML string. + */ + serialize(version = this._managerVersion) { + let viewpointTag: string | null = null; + if (this.viewpoint) { + viewpointTag = ``; + } + + let modifiedDateTag: string | null = null; + if (this.modifiedDate) { + modifiedDateTag = `${this.modifiedDate.toISOString()}`; + } + + let modifiedAuthorTag: string | null = null; + if (this.modifiedAuthor) { + modifiedAuthorTag = `${this.modifiedAuthor}`; + } + + return ` + + ${this.date.toISOString()} + ${this.author} + ${this.comment} + ${viewpointTag ?? ""} + ${modifiedAuthorTag ?? ""} + ${modifiedDateTag ?? ""} + + `; + } +} diff --git a/packages/core/src/openbim/BCFManager/src/Exporter.ts b/packages/core/src/openbim/BCFManager/src/Exporter.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/core/src/openbim/BCFManager/src/Importer.ts b/packages/core/src/openbim/BCFManager/src/Importer.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/core/src/openbim/BCFManager/src/Topic.ts b/packages/core/src/openbim/BCFManager/src/Topic.ts index 0eff4902a..8fbbdf16c 100644 --- a/packages/core/src/openbim/BCFManager/src/Topic.ts +++ b/packages/core/src/openbim/BCFManager/src/Topic.ts @@ -1,24 +1,138 @@ import { UUID } from "../../../utils"; -import { BCFTopic, BCFTopicComment } from "./types"; -import { BCFViewpoint } from "../../../core"; +import { Components } from "../../../core/Components"; +import { Viewpoint } from "../../../core/Viewpoints"; +import { Comment } from "./Comment"; +import { BCFManager } from ".."; -export class Topic implements BCFTopic { +export class Topic { guid = UUID.create(); title = "BCF Topic"; type = "Issue"; creationAuthor = "jhon.doe@example.com"; creationDate = new Date(); status = "Active"; - comments: BCFTopicComment[] = []; - viewpoints: BCFViewpoint[] = []; + readonly comments = new Set(); + readonly viewpoints = new Set(); customData: Record = {}; description?: string; serverAssignedId?: string; priority?: string; stage?: string; - labels?: string[]; + labels = new Set(); assignedTo?: string; dueDate?: Date; modifiedAuthor?: string; modifiedDate?: Date; + index?: number; + + private _components: Components; + + private get _managerVersion() { + const manager = this._components.get(BCFManager); + return manager.config.version; + } + + constructor(components: Components) { + this._components = components; + } + + createComment(text: string) { + const comment = new Comment(this._components, text); + this.comments.add(comment); + return comment; + } + + serialize(version = this._managerVersion) { + let serverAssignedIdAttribute: string | null = null; + if (this.serverAssignedId) { + serverAssignedIdAttribute = `ServerAssignedId="${this.serverAssignedId}"`; + } + + let priorityTag: string | null = null; + if (this.priority) { + priorityTag = `${this.priority}`; + } + + let indexTag: string | null = null; + if (this.index) { + indexTag = `${this.index}`; + } + + let labelTags = [...this.labels] + .map((label) => `${label}`) + .join("\n"); + + for (const key in this.customData) { + const value = this.customData[key]; + if (typeof value !== "string") continue; + labelTags += `\n${value}`; + } + + let modifiedDateTag: string | null = null; + if (this.modifiedDate) { + modifiedDateTag = `${this.modifiedDate.toISOString()}`; + } + + let modifiedAuthorTag: string | null = null; + if (this.modifiedAuthor) { + modifiedAuthorTag = `${this.modifiedAuthor}`; + } + + let dueDateTag: string | null = null; + if (this.dueDate) { + dueDateTag = `${this.dueDate.toISOString()}`; + } + + let assignedToTag: string | null = null; + if (this.assignedTo) { + assignedToTag = `${this.assignedTo}`; + } + + let descriptionTag: string | null = null; + if (this.description) { + descriptionTag = `${this.description}`; + } + + let stageTag: string | null = null; + if (this.stage) { + stageTag = `${this.stage}`; + } + + const commentTags = [...this.comments] + .map((comment) => comment.serialize(version)) + .join("\n"); + + const viewpointTags = [...this.viewpoints] + .map( + (viewpoint) => + ` + ${viewpoint.guid}.bcfv + ${viewpoint.guid}.jpeg + + `, + ) + .join("\n"); + + return ` + + + + Codestin Search App + ${this.creationDate.toISOString()} + ${this.creationAuthor} + ${priorityTag ?? ""} + ${indexTag ?? ""} + ${modifiedDateTag ?? ""} + ${modifiedAuthorTag ?? ""} + ${dueDateTag ?? ""} + ${assignedToTag ?? ""} + ${descriptionTag ?? ""} + ${stageTag ?? ""} + ${labelTags} + + ${commentTags} + ${viewpointTags} + + `; + } } diff --git a/packages/core/src/openbim/BCFManager/src/index.ts b/packages/core/src/openbim/BCFManager/src/index.ts index f5be28811..ed9ac3845 100644 --- a/packages/core/src/openbim/BCFManager/src/index.ts +++ b/packages/core/src/openbim/BCFManager/src/index.ts @@ -1,3 +1,2 @@ export * from "./Topic"; -export * from "./Viewpoints"; export * from "./types"; diff --git a/packages/core/src/openbim/BCFManager/src/types.ts b/packages/core/src/openbim/BCFManager/src/types.ts index fc02cc482..4c656a87e 100644 --- a/packages/core/src/openbim/BCFManager/src/types.ts +++ b/packages/core/src/openbim/BCFManager/src/types.ts @@ -1,11 +1,11 @@ import { BCFViewpoint } from "../../../core/Viewpoints"; export interface BCFTopicComment extends Record { + date: Date; author: string; guid: string; comment: string; - date: Date; - viewpoint: string; + viewpoint?: string; modifiedAuthor?: string; modifiedDate?: Date; } @@ -30,3 +30,5 @@ export interface BCFTopic extends Record { modifiedAuthor?: string; modifiedDate?: Date; } + +export type BCFVersion = "2.1" | "3.0"; diff --git a/yarn.lock b/yarn.lock index 5bc4b34bf..35f5c1fd3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -647,6 +647,8 @@ __metadata: "@thatopen/ui": ~2.1.0 "@types/three": 0.160.0 camera-controls: 2.7.3 + fast-xml-parser: latest + jszip: latest stats.js: ^0.17.0 three: ^0.160.1 three-mesh-bvh: 0.7.0 @@ -1336,6 +1338,13 @@ __metadata: languageName: node linkType: hard +"core-util-is@npm:~1.0.0": + version: 1.0.3 + resolution: "core-util-is@npm:1.0.3" + checksum: 9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99 + languageName: node + linkType: hard + "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -1985,6 +1994,17 @@ __metadata: languageName: node linkType: hard +"fast-xml-parser@npm:latest": + version: 4.4.0 + resolution: "fast-xml-parser@npm:4.4.0" + dependencies: + strnum: ^1.0.5 + bin: + fxparser: src/cli/cli.js + checksum: ad33a4b5165a0ffcb6e17ae78825bd4619a8298844a8a8408f2ea141a0d2d9439d18865dc5254162f09fe54d510ff18e5d5c0a190869cab21fc745ee66be816b + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.17.1 resolution: "fastq@npm:1.17.1" @@ -2397,6 +2417,13 @@ __metadata: languageName: node linkType: hard +"immediate@npm:~3.0.5": + version: 3.0.6 + resolution: "immediate@npm:3.0.6" + checksum: f9b3486477555997657f70318cc8d3416159f208bec4cca3ff3442fd266bc23f50f0c9bd8547e1371a6b5e82b821ec9a7044a4f7b944798b25aa3cc6d5e63e62 + languageName: node + linkType: hard + "import-fresh@npm:^3.2.1": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" @@ -2438,7 +2465,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2": +"inherits@npm:2, inherits@npm:~2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 @@ -2651,6 +2678,13 @@ __metadata: languageName: node linkType: hard +"isarray@npm:~1.0.0": + version: 1.0.0 + resolution: "isarray@npm:1.0.0" + checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab + languageName: node + linkType: hard + "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -2747,6 +2781,18 @@ __metadata: languageName: node linkType: hard +"jszip@npm:latest": + version: 3.10.1 + resolution: "jszip@npm:3.10.1" + dependencies: + lie: ~3.3.0 + pako: ~1.0.2 + readable-stream: ~2.3.6 + setimmediate: ^1.0.5 + checksum: abc77bfbe33e691d4d1ac9c74c8851b5761fba6a6986630864f98d876f3fcc2d36817dfc183779f32c00157b5d53a016796677298272a714ae096dfe6b1c8b60 + languageName: node + linkType: hard + "keyv@npm:^4.5.3": version: 4.5.4 resolution: "keyv@npm:4.5.4" @@ -2773,6 +2819,15 @@ __metadata: languageName: node linkType: hard +"lie@npm:~3.3.0": + version: 3.3.0 + resolution: "lie@npm:3.3.0" + dependencies: + immediate: ~3.0.5 + checksum: 33102302cf19766f97919a6a98d481e01393288b17a6aa1f030a3542031df42736edde8dab29ffdbf90bebeffc48c761eb1d064dc77592ca3ba3556f9fe6d2a8 + languageName: node + linkType: hard + "lit-element@npm:^4.0.4": version: 4.0.5 resolution: "lit-element@npm:4.0.5" @@ -3234,6 +3289,13 @@ __metadata: languageName: node linkType: hard +"pako@npm:~1.0.2": + version: 1.0.11 + resolution: "pako@npm:1.0.11" + checksum: 1be2bfa1f807608c7538afa15d6f25baa523c30ec870a3228a89579e474a4d992f4293859524e46d5d87fd30fa17c5edf34dbef0671251d9749820b488660b16 + languageName: node + linkType: hard + "parent-module@npm:^1.0.0": version: 1.0.1 resolution: "parent-module@npm:1.0.1" @@ -3375,6 +3437,13 @@ __metadata: languageName: node linkType: hard +"process-nextick-args@npm:~2.0.0": + version: 2.0.1 + resolution: "process-nextick-args@npm:2.0.1" + checksum: 1d38588e520dab7cea67cbbe2efdd86a10cc7a074c09657635e34f035277b59fbb57d09d8638346bf7090f8e8ebc070c96fa5fd183b777fff4f5edff5e9466cf + languageName: node + linkType: hard + "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1" @@ -3399,6 +3468,21 @@ __metadata: languageName: node linkType: hard +"readable-stream@npm:~2.3.6": + version: 2.3.8 + resolution: "readable-stream@npm:2.3.8" + dependencies: + core-util-is: ~1.0.0 + inherits: ~2.0.3 + isarray: ~1.0.0 + process-nextick-args: ~2.0.0 + safe-buffer: ~5.1.1 + string_decoder: ~1.1.1 + util-deprecate: ~1.0.1 + checksum: 65645467038704f0c8aaf026a72fbb588a9e2ef7a75cd57a01702ee9db1c4a1e4b03aaad36861a6a0926546a74d174149c8c207527963e0c2d3eee2f37678a42 + languageName: node + linkType: hard + "regexp.prototype.flags@npm:^1.5.2": version: 1.5.2 resolution: "regexp.prototype.flags@npm:1.5.2" @@ -3592,6 +3676,13 @@ __metadata: languageName: node linkType: hard +"safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": + version: 5.1.2 + resolution: "safe-buffer@npm:5.1.2" + checksum: f2f1f7943ca44a594893a852894055cf619c1fbcb611237fc39e461ae751187e7baf4dc391a72125e0ac4fb2d8c5c0b3c71529622e6a58f46b960211e704903c + languageName: node + linkType: hard + "safe-regex-test@npm:^1.0.3": version: 1.0.3 resolution: "safe-regex-test@npm:1.0.3" @@ -3665,6 +3756,13 @@ __metadata: languageName: node linkType: hard +"setimmediate@npm:^1.0.5": + version: 1.0.5 + resolution: "setimmediate@npm:1.0.5" + checksum: c9a6f2c5b51a2dabdc0247db9c46460152ffc62ee139f3157440bd48e7c59425093f42719ac1d7931f054f153e2d26cf37dfeb8da17a794a58198a2705e527fd + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -3842,6 +3940,15 @@ __metadata: languageName: node linkType: hard +"string_decoder@npm:~1.1.1": + version: 1.1.1 + resolution: "string_decoder@npm:1.1.1" + dependencies: + safe-buffer: ~5.1.0 + checksum: 9ab7e56f9d60a28f2be697419917c50cac19f3e8e6c28ef26ed5f4852289fe0de5d6997d29becf59028556f2c62983790c1d9ba1e2a3cc401768ca12d5183a5b + languageName: node + linkType: hard + "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -3874,6 +3981,13 @@ __metadata: languageName: node linkType: hard +"strnum@npm:^1.0.5": + version: 1.0.5 + resolution: "strnum@npm:1.0.5" + checksum: 651b2031db5da1bf4a77fdd2f116a8ac8055157c5420f5569f64879133825915ad461513e7202a16d7fec63c54fd822410d0962f8ca12385c4334891b9ae6dd2 + languageName: node + linkType: hard + "supports-color@npm:^7.1.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" @@ -4135,6 +4249,13 @@ __metadata: languageName: node linkType: hard +"util-deprecate@npm:~1.0.1": + version: 1.0.2 + resolution: "util-deprecate@npm:1.0.2" + checksum: 474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2 + languageName: node + linkType: hard + "validator@npm:^13.7.0": version: 13.12.0 resolution: "validator@npm:13.12.0" From 079f4cb3b61d9ff00a522b3a8d1a1bb918f02d4a Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Fri, 19 Jul 2024 07:49:34 -0500 Subject: [PATCH 04/12] general fixes over the bcf implementation --- .../core/src/core/Viewpoints/src/Viewpoint.ts | 10 +- .../core/src/openbim/BCFManager/example.ts | 43 +++++-- packages/core/src/openbim/BCFManager/index.ts | 30 ++--- .../src/openbim/BCFManager/src/Comment.ts | 4 - .../core/src/openbim/BCFManager/src/Topic.ts | 117 ++++++++++++++++-- .../core/src/openbim/BCFManager/src/types.ts | 21 ++++ 6 files changed, 180 insertions(+), 45 deletions(-) diff --git a/packages/core/src/core/Viewpoints/src/Viewpoint.ts b/packages/core/src/core/Viewpoints/src/Viewpoint.ts index 96d5c66dd..8fde8aabb 100644 --- a/packages/core/src/core/Viewpoints/src/Viewpoint.ts +++ b/packages/core/src/core/Viewpoints/src/Viewpoint.ts @@ -217,10 +217,6 @@ export class Viewpoint { let componentSelection = ""; if (manager.config.includeSelectionTag) { - componentSelection = [...this.selectionComponents] - .map((globalId) => ``) - .join(`\n`); - } else { const modelIdMap = this._modelIdMap; for (const modelID in modelIdMap) { const model = fragments.groups.get(modelID); @@ -233,10 +229,14 @@ export class Viewpoint { if (!globalID) continue; const tag = attrs.Tag?.value; let tagAttribute: string | null = null; - if (tag) tagAttribute = `AuthoringToolId="${tag}`; + if (tag) tagAttribute = `AuthoringToolId="${tag}"`; componentSelection += `\n`; } } + } else { + componentSelection = [...this.selectionComponents] + .map((globalId) => ``) + .join(`\n`); } const coordinationMatrix = new THREE.Matrix4(); diff --git a/packages/core/src/openbim/BCFManager/example.ts b/packages/core/src/openbim/BCFManager/example.ts index 47ca64299..f52f65cfd 100644 --- a/packages/core/src/openbim/BCFManager/example.ts +++ b/packages/core/src/openbim/BCFManager/example.ts @@ -47,19 +47,35 @@ const file = await fetch( const data = await file.arrayBuffer(); const buffer = new Uint8Array(data); const model = await ifcLoader.load(buffer); -const selection = model.getFragmentMap([186]); -// Viewpoints +const bcfManager = components.get(OBC.BCFManager); +bcfManager.config.types = new Set([ + "Clash, Inquiry, Information, Coordination", +]); +bcfManager.config.statuses = new Set([ + "Active", + "In Progress", + "Completed", + "In Review", + "Closed", +]); +bcfManager.config.priorities = new Set(["Low", "Normal", "High", "Critical"]); +bcfManager.config.stages = new Set(["Planning", "Design", "Construction"]); + +const topic = bcfManager.createTopic(); +topic.description = "It seems these elements are badly defined."; +topic.type = "Information"; +topic.priority = "High"; +topic.stage = "Design"; + const viewpoints = components.get(OBC.Viewpoints); const viewpoint = viewpoints.create(world); -viewpoint.addComponentsFromMap(selection); -viewpoint.selectionComponents.add("2idC0G3ezCdhA9WVjWemcy"); +viewpoint.addComponentsFromMap(model.getFragmentMap([186])); // You can provide a FragmentIdMap to the viewpoint selection +viewpoint.selectionComponents.add("2idC0G3ezCdhA9WVjWemcy"); // You can provide a GlobalId to the viewpoint selection // viewpoint.selection gives the fragmentIdMap to select elements with the highlighter from @thatopen/components-front -// BCF Manager -const bcfManager = components.get(OBC.BCFManager); -const topic = bcfManager.createTopic(); topic.viewpoints.add(viewpoint); +topic.createComment("Hi there! I agree."); const panel = BUI.Component.create(() => { const onUpdateViewpoint = () => { @@ -68,12 +84,25 @@ const panel = BUI.Component.create(() => { console.log({ ...viewpoint.camera }); }; + const onBcfDownload = async () => { + const bcf = await bcfManager.export(new Set([topic])); + const bcfFile = new File([bcf], "topics.bcf"); + const a = document.createElement("a"); + a.href = URL.createObjectURL(bcfFile); + a.download = bcfFile.name; + a.click(); + URL.revokeObjectURL(a.href); + }; + return BUI.html` viewpoint.go()}> + + + `; }); diff --git a/packages/core/src/openbim/BCFManager/index.ts b/packages/core/src/openbim/BCFManager/index.ts index 5c3f2663e..0cfde682a 100644 --- a/packages/core/src/openbim/BCFManager/index.ts +++ b/packages/core/src/openbim/BCFManager/index.ts @@ -1,18 +1,6 @@ import JSZip from "jszip"; import { Component, Configurable, Disposable, Event } from "../../core/Types"; -import { Topic } from "./src"; - -interface BCFManagerConfig { - version: "2.1" | "3.0"; - author: string; - types: Set; - statuses: Set; - priorities: Set; - labels: Set; - stages: Set; - users: Set; - includeSelectionTag: boolean; -} +import { BCFManagerConfig, Topic } from "./src"; export class BCFManager extends Component @@ -24,13 +12,16 @@ export class BCFManager config: Required = { author: "jhon.doe@example.com", version: "2.1", - types: new Set(), - statuses: new Set(), + types: new Set(["Issue"]), + statuses: new Set(["Active"]), priorities: new Set(), labels: new Set(), stages: new Set(), users: new Set(), includeSelectionTag: false, + updateExtensionsOnImport: true, + strict: false, + includeMissingExtensionsOnExport: true, }; readonly list = new Set(); @@ -107,7 +98,7 @@ export class BCFManager `; } - async export(topics = this.list, fileName = "topics") { + async export(topics = this.list) { const zip = new JSZip(); zip.file( "bcf.version", @@ -128,13 +119,8 @@ export class BCFManager topicFolder.file(`${viewpoint.guid}.bcfv`, await viewpoint.serialize()); } } - const a = document.createElement("a"); const content = await zip.generateAsync({ type: "blob" }); - a.href = URL.createObjectURL(content); - a.download = `${fileName}.bcf`; - a.click(); - URL.revokeObjectURL(a.href); - a.remove(); + return content; } } diff --git a/packages/core/src/openbim/BCFManager/src/Comment.ts b/packages/core/src/openbim/BCFManager/src/Comment.ts index 36044b3a1..447318f47 100644 --- a/packages/core/src/openbim/BCFManager/src/Comment.ts +++ b/packages/core/src/openbim/BCFManager/src/Comment.ts @@ -53,10 +53,6 @@ export class Comment { this.author = manager.config.author; } - /** - * Serializes the Comment instance to an XML string. - * @returns The serialized XML string. - */ serialize(version = this._managerVersion) { let viewpointTag: string | null = null; if (this.viewpoint) { diff --git a/packages/core/src/openbim/BCFManager/src/Topic.ts b/packages/core/src/openbim/BCFManager/src/Topic.ts index 8fbbdf16c..fb79461dd 100644 --- a/packages/core/src/openbim/BCFManager/src/Topic.ts +++ b/packages/core/src/openbim/BCFManager/src/Topic.ts @@ -7,24 +7,127 @@ import { BCFManager } from ".."; export class Topic { guid = UUID.create(); title = "BCF Topic"; - type = "Issue"; - creationAuthor = "jhon.doe@example.com"; creationDate = new Date(); - status = "Active"; readonly comments = new Set(); readonly viewpoints = new Set(); customData: Record = {}; description?: string; serverAssignedId?: string; - priority?: string; - stage?: string; - labels = new Set(); - assignedTo?: string; dueDate?: Date; modifiedAuthor?: string; modifiedDate?: Date; index?: number; + get creationAuthor() { + const manager = this._components.get(BCFManager); + const author = manager.config.author; + return author; + } + + private _type = "Issue"; + + set type(value: string) { + const manager = this._components.get(BCFManager); + const { strict, types } = manager.config; + const valid = strict ? types.has(value) : true; + if (!valid) return; + this._type = value; + } + + get type() { + return this._type; + } + + private _status = "Active"; + + set status(value: string) { + const manager = this._components.get(BCFManager); + const { strict, statuses } = manager.config; + const valid = strict ? statuses.has(value) : true; + if (!valid) return; + this._status = value; + } + + get status() { + return this._status; + } + + private _priority?: string; + + set priority(value: string | undefined) { + const manager = this._components.get(BCFManager); + if (value) { + const { strict, priorities } = manager.config; + const valid = strict ? priorities.has(value) : true; + if (!valid) return; + this._priority = value; + } else { + this._priority = value; + } + } + + get priority() { + return this._priority; + } + + private _stage?: string; + + set stage(value: string | undefined) { + const manager = this._components.get(BCFManager); + if (value) { + const { strict, stages } = manager.config; + const valid = strict ? stages.has(value) : true; + if (!valid) return; + this._stage = value; + } else { + this._stage = value; + } + } + + get stage() { + return this._stage; + } + + private _assignedTo?: string; + + set assignedTo(value: string | undefined) { + const manager = this._components.get(BCFManager); + if (value) { + const { strict, users } = manager.config; + const valid = strict ? users.has(value) : true; + if (!valid) return; + this._assignedTo = value; + } else { + this._assignedTo = value; + } + } + + get assignedTo() { + return this._assignedTo; + } + + private _labels = new Set(); + + set labels(value: Set) { + const manager = this._components.get(BCFManager); + const { strict, labels } = manager.config; + if (strict) { + const _value = new Set(); + for (const label of value) { + const valid = strict ? labels.has(label) : true; + if (!valid) continue; + _value.add(label); + } + this._labels = _value; + } else { + this._labels = value; + } + } + + get labels() { + return this._labels; + } + private _components: Components; private get _managerVersion() { diff --git a/packages/core/src/openbim/BCFManager/src/types.ts b/packages/core/src/openbim/BCFManager/src/types.ts index 4c656a87e..88b927874 100644 --- a/packages/core/src/openbim/BCFManager/src/types.ts +++ b/packages/core/src/openbim/BCFManager/src/types.ts @@ -32,3 +32,24 @@ export interface BCFTopic extends Record { } export type BCFVersion = "2.1" | "3.0"; + +export interface BCFManagerConfig { + // The BCF version used during export + version: "2.1" | "3.0"; + // The user (usually an email) creating topics using this manager + author: string; + types: Set; + statuses: Set; + priorities: Set; + labels: Set; + stages: Set; + users: Set; + // Wether or not to include the AuthoringSoftwareId in the viewpoint components during export + includeSelectionTag: boolean; + // Updates the types, statuses, users, etc., after importing an external BCF + updateExtensionsOnImport: boolean; + // Only allow to use the extensions (types, statuses, etc.) defined in the manager + strict: boolean; + // If true, updates the extensions (types, status, etc.) based on Topics data + includeMissingExtensionsOnExport: boolean; +} From 2022255c7f78fc59d484bdc9096780c2e5ca58b5 Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Mon, 22 Jul 2024 16:18:13 -0500 Subject: [PATCH 05/12] chages BCFManager to BCFTopics --- .../core/src/core/Viewpoints/src/Viewpoint.ts | 6 +++--- .../{BCFManager => BCFTopics}/example.html | 0 .../{BCFManager => BCFTopics}/example.ts | 16 +++++++--------- .../openbim/{BCFManager => BCFTopics}/index.ts | 10 +++++----- .../{BCFManager => BCFTopics}/src/Comment.ts | 8 ++++---- .../{BCFManager => BCFTopics}/src/Topic.ts | 18 +++++++++--------- .../{BCFManager => BCFTopics}/src/index.ts | 0 .../{BCFManager => BCFTopics}/src/types.ts | 2 +- packages/core/src/openbim/index.ts | 2 +- 9 files changed, 30 insertions(+), 32 deletions(-) rename packages/core/src/openbim/{BCFManager => BCFTopics}/example.html (100%) rename packages/core/src/openbim/{BCFManager => BCFTopics}/example.ts (88%) rename packages/core/src/openbim/{BCFManager => BCFTopics}/index.ts (94%) rename packages/core/src/openbim/{BCFManager => BCFTopics}/src/Comment.ts (91%) rename packages/core/src/openbim/{BCFManager => BCFTopics}/src/Topic.ts (92%) rename packages/core/src/openbim/{BCFManager => BCFTopics}/src/index.ts (100%) rename packages/core/src/openbim/{BCFManager => BCFTopics}/src/types.ts (97%) diff --git a/packages/core/src/core/Viewpoints/src/Viewpoint.ts b/packages/core/src/core/Viewpoints/src/Viewpoint.ts index 8fde8aabb..90e9b8d74 100644 --- a/packages/core/src/core/Viewpoints/src/Viewpoint.ts +++ b/packages/core/src/core/Viewpoints/src/Viewpoint.ts @@ -13,7 +13,7 @@ import { import { Components } from "../../Components"; import { World } from "../../Types"; import { FragmentsManager } from "../../../fragments/FragmentsManager"; -import { BCFManager } from "../../../openbim/BCFManager"; +import { BCFTopics } from "../../../openbim/BCFTopics"; export class Viewpoint { guid = UUID.create(); @@ -76,7 +76,7 @@ export class Viewpoint { readonly world: World; private get _managerVersion() { - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); return manager.config.version; } @@ -213,7 +213,7 @@ export class Viewpoint { async serialize(version = this._managerVersion) { const fragments = this._components.get(FragmentsManager); - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); let componentSelection = ""; if (manager.config.includeSelectionTag) { diff --git a/packages/core/src/openbim/BCFManager/example.html b/packages/core/src/openbim/BCFTopics/example.html similarity index 100% rename from packages/core/src/openbim/BCFManager/example.html rename to packages/core/src/openbim/BCFTopics/example.html diff --git a/packages/core/src/openbim/BCFManager/example.ts b/packages/core/src/openbim/BCFTopics/example.ts similarity index 88% rename from packages/core/src/openbim/BCFManager/example.ts rename to packages/core/src/openbim/BCFTopics/example.ts index f52f65cfd..7d46e1a61 100644 --- a/packages/core/src/openbim/BCFManager/example.ts +++ b/packages/core/src/openbim/BCFTopics/example.ts @@ -48,21 +48,19 @@ const data = await file.arrayBuffer(); const buffer = new Uint8Array(data); const model = await ifcLoader.load(buffer); -const bcfManager = components.get(OBC.BCFManager); -bcfManager.config.types = new Set([ - "Clash, Inquiry, Information, Coordination", -]); -bcfManager.config.statuses = new Set([ +const bcfTopics = components.get(OBC.BCFTopics); +bcfTopics.config.types = new Set(["Clash, Inquiry, Information, Coordination"]); +bcfTopics.config.statuses = new Set([ "Active", "In Progress", "Completed", "In Review", "Closed", ]); -bcfManager.config.priorities = new Set(["Low", "Normal", "High", "Critical"]); -bcfManager.config.stages = new Set(["Planning", "Design", "Construction"]); +bcfTopics.config.priorities = new Set(["Low", "Normal", "High", "Critical"]); +bcfTopics.config.stages = new Set(["Planning", "Design", "Construction"]); -const topic = bcfManager.createTopic(); +const topic = bcfTopics.createTopic(); topic.description = "It seems these elements are badly defined."; topic.type = "Information"; topic.priority = "High"; @@ -85,7 +83,7 @@ const panel = BUI.Component.create(() => { }; const onBcfDownload = async () => { - const bcf = await bcfManager.export(new Set([topic])); + const bcf = await bcfTopics.export(new Set([topic])); const bcfFile = new File([bcf], "topics.bcf"); const a = document.createElement("a"); a.href = URL.createObjectURL(bcfFile); diff --git a/packages/core/src/openbim/BCFManager/index.ts b/packages/core/src/openbim/BCFTopics/index.ts similarity index 94% rename from packages/core/src/openbim/BCFManager/index.ts rename to packages/core/src/openbim/BCFTopics/index.ts index 0cfde682a..f03faf6d3 100644 --- a/packages/core/src/openbim/BCFManager/index.ts +++ b/packages/core/src/openbim/BCFTopics/index.ts @@ -1,15 +1,15 @@ import JSZip from "jszip"; import { Component, Configurable, Disposable, Event } from "../../core/Types"; -import { BCFManagerConfig, Topic } from "./src"; +import { BCFTopicsConfig, Topic } from "./src"; -export class BCFManager +export class BCFTopics extends Component - implements Disposable, Configurable + implements Disposable, Configurable { static uuid = "de977976-e4f6-4e4f-a01a-204727839802" as const; enabled = false; - config: Required = { + config: Required = { author: "jhon.doe@example.com", version: "2.1", types: new Set(["Issue"]), @@ -28,7 +28,7 @@ export class BCFManager readonly onSetup = new Event(); isSetup = false; - setup(config?: Partial) { + setup(config?: Partial) { if (this.isSetup) return; this.config = { ...this.config, ...config }; this.isSetup = true; diff --git a/packages/core/src/openbim/BCFManager/src/Comment.ts b/packages/core/src/openbim/BCFTopics/src/Comment.ts similarity index 91% rename from packages/core/src/openbim/BCFManager/src/Comment.ts rename to packages/core/src/openbim/BCFTopics/src/Comment.ts index 447318f47..bb87c99ab 100644 --- a/packages/core/src/openbim/BCFManager/src/Comment.ts +++ b/packages/core/src/openbim/BCFTopics/src/Comment.ts @@ -1,4 +1,4 @@ -import { BCFManager } from ".."; +import { BCFTopics } from ".."; import { Viewpoint } from "../../../core/Viewpoints"; import { Components } from "../../../core/Components"; import { UUID } from "../../../utils"; @@ -15,7 +15,7 @@ export class Comment { modifiedDate?: Date; private get _managerVersion() { - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); return manager.config.version; } @@ -27,7 +27,7 @@ export class Comment { * @param value - The new comment text. */ set comment(value: string) { - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); this._comment = value; this.modifiedDate = new Date(); this.modifiedAuthor = manager.config.author; @@ -49,7 +49,7 @@ export class Comment { constructor(components: Components, text: string) { this._components = components; this._comment = text; // Set the comment to the private property to prevent setting a modifiedDate and author - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); this.author = manager.config.author; } diff --git a/packages/core/src/openbim/BCFManager/src/Topic.ts b/packages/core/src/openbim/BCFTopics/src/Topic.ts similarity index 92% rename from packages/core/src/openbim/BCFManager/src/Topic.ts rename to packages/core/src/openbim/BCFTopics/src/Topic.ts index fb79461dd..863119be2 100644 --- a/packages/core/src/openbim/BCFManager/src/Topic.ts +++ b/packages/core/src/openbim/BCFTopics/src/Topic.ts @@ -2,7 +2,7 @@ import { UUID } from "../../../utils"; import { Components } from "../../../core/Components"; import { Viewpoint } from "../../../core/Viewpoints"; import { Comment } from "./Comment"; -import { BCFManager } from ".."; +import { BCFTopics } from ".."; export class Topic { guid = UUID.create(); @@ -19,7 +19,7 @@ export class Topic { index?: number; get creationAuthor() { - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); const author = manager.config.author; return author; } @@ -27,7 +27,7 @@ export class Topic { private _type = "Issue"; set type(value: string) { - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); const { strict, types } = manager.config; const valid = strict ? types.has(value) : true; if (!valid) return; @@ -41,7 +41,7 @@ export class Topic { private _status = "Active"; set status(value: string) { - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); const { strict, statuses } = manager.config; const valid = strict ? statuses.has(value) : true; if (!valid) return; @@ -55,7 +55,7 @@ export class Topic { private _priority?: string; set priority(value: string | undefined) { - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); if (value) { const { strict, priorities } = manager.config; const valid = strict ? priorities.has(value) : true; @@ -73,7 +73,7 @@ export class Topic { private _stage?: string; set stage(value: string | undefined) { - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); if (value) { const { strict, stages } = manager.config; const valid = strict ? stages.has(value) : true; @@ -91,7 +91,7 @@ export class Topic { private _assignedTo?: string; set assignedTo(value: string | undefined) { - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); if (value) { const { strict, users } = manager.config; const valid = strict ? users.has(value) : true; @@ -109,7 +109,7 @@ export class Topic { private _labels = new Set(); set labels(value: Set) { - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); const { strict, labels } = manager.config; if (strict) { const _value = new Set(); @@ -131,7 +131,7 @@ export class Topic { private _components: Components; private get _managerVersion() { - const manager = this._components.get(BCFManager); + const manager = this._components.get(BCFTopics); return manager.config.version; } diff --git a/packages/core/src/openbim/BCFManager/src/index.ts b/packages/core/src/openbim/BCFTopics/src/index.ts similarity index 100% rename from packages/core/src/openbim/BCFManager/src/index.ts rename to packages/core/src/openbim/BCFTopics/src/index.ts diff --git a/packages/core/src/openbim/BCFManager/src/types.ts b/packages/core/src/openbim/BCFTopics/src/types.ts similarity index 97% rename from packages/core/src/openbim/BCFManager/src/types.ts rename to packages/core/src/openbim/BCFTopics/src/types.ts index 88b927874..9dd51f5dc 100644 --- a/packages/core/src/openbim/BCFManager/src/types.ts +++ b/packages/core/src/openbim/BCFTopics/src/types.ts @@ -33,7 +33,7 @@ export interface BCFTopic extends Record { export type BCFVersion = "2.1" | "3.0"; -export interface BCFManagerConfig { +export interface BCFTopicsConfig { // The BCF version used during export version: "2.1" | "3.0"; // The user (usually an email) creating topics using this manager diff --git a/packages/core/src/openbim/index.ts b/packages/core/src/openbim/index.ts index 47fb5d8b2..2c7d2bbf5 100644 --- a/packages/core/src/openbim/index.ts +++ b/packages/core/src/openbim/index.ts @@ -1 +1 @@ -export * from "./BCFManager"; +export * from "./BCFTopics"; From ed5c501fca7753f1afd07635105d74930c4525fe Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Mon, 22 Jul 2024 22:17:33 -0500 Subject: [PATCH 06/12] more progress on bcf integration --- packages/core/src/core/Viewpoints/index.ts | 6 +- .../core/src/core/Viewpoints/src/Viewpoint.ts | 34 ++- .../core/src/openbim/BCFTopics/example.html | 2 +- .../core/src/openbim/BCFTopics/example.ts | 8 +- packages/core/src/openbim/BCFTopics/index.ts | 276 +++++++++++++++++- .../core/src/openbim/BCFTopics/src/Topic.ts | 9 +- .../core/src/openbim/BCFTopics/src/index.ts | 1 + .../core/src/openbim/BCFTopics/src/types.ts | 56 +--- resources/topics.bcf | Bin 0 -> 6335 bytes 9 files changed, 329 insertions(+), 63 deletions(-) create mode 100644 resources/topics.bcf diff --git a/packages/core/src/core/Viewpoints/index.ts b/packages/core/src/core/Viewpoints/index.ts index 5226d3ae3..61ea44bf2 100644 --- a/packages/core/src/core/Viewpoints/index.ts +++ b/packages/core/src/core/Viewpoints/index.ts @@ -6,21 +6,21 @@ export class Viewpoints extends Component implements Disposable { static readonly uuid = "ee867824-a796-408d-8aa0-4e5962a83c66" as const; enabled = true; - readonly list = new Set(); + readonly list = new Map(); readonly onViewpointCreated = new Event(); readonly onViewpointDeleted = new Event(); create(world: World) { const viewpoint = new Viewpoint(this.components, world); - this.list.add(viewpoint); + this.list.set(viewpoint.guid, viewpoint); this.onViewpointCreated.trigger(viewpoint); return viewpoint; } delete(viewpoint: Viewpoint) { const { guid } = viewpoint; - const deleted = this.list.delete(viewpoint); + const deleted = this.list.delete(guid); this.onViewpointDeleted.trigger(guid); return deleted; } diff --git a/packages/core/src/core/Viewpoints/src/Viewpoint.ts b/packages/core/src/core/Viewpoints/src/Viewpoint.ts index 90e9b8d74..86054470f 100644 --- a/packages/core/src/core/Viewpoints/src/Viewpoint.ts +++ b/packages/core/src/core/Viewpoints/src/Viewpoint.ts @@ -14,6 +14,8 @@ import { Components } from "../../Components"; import { World } from "../../Types"; import { FragmentsManager } from "../../../fragments/FragmentsManager"; import { BCFTopics } from "../../../openbim/BCFTopics"; +import { Raycasters } from "../../Raycasters"; +import { BoundingBoxer } from "../../../fragments/BoundingBoxer"; export class Viewpoint { guid = UUID.create(); @@ -80,6 +82,13 @@ export class Viewpoint { return manager.config.version; } + get topics() { + const manager = this._components.get(BCFTopics); + const topicsList = [...manager.list.values()]; + const topics = topicsList.filter((topic) => topic.viewpoints.has(this)); + return topics; + } + constructor(components: Components, world: World) { this._components = components; this.world = world; @@ -144,12 +153,29 @@ export class Viewpoint { this.coordinationMatrix, ); - const target = { - x: position.x + this.direction.x, - y: position.y + this.direction.y, - z: position.z + this.direction.z, + // Default target based on the viewpoint information + let target = { + x: position.x + this.direction.x * 80, + y: position.y + this.direction.y * 80, + z: position.z + this.direction.z * 80, }; + const selection = this.selection; + if (Object.keys(selection).length !== 0) { + // In case there are selection components, use their center as the target + const bb = this._components.get(BoundingBoxer); + bb.reset(); + bb.addFragmentIdMap(this.selection); + target = bb.getSphere().center; + bb.reset(); + } else { + // In case there are not selection components, use the raycaster to calculate one + const raycasters = this._components.get(Raycasters); + const raycaster = raycasters.get(this.world); + const result = raycaster.castRayFromVector(position, this.direction); + if (result) target = result.point; + } + await controls.setLookAt( position.x, position.y, diff --git a/packages/core/src/openbim/BCFTopics/example.html b/packages/core/src/openbim/BCFTopics/example.html index d037adc26..4d7bebdb7 100644 --- a/packages/core/src/openbim/BCFTopics/example.html +++ b/packages/core/src/openbim/BCFTopics/example.html @@ -4,7 +4,7 @@ - Codestin Search App + Codestin Search App