diff --git a/packages/core/package.json b/packages/core/package.json index aa81f5797..e8fbed636 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": { diff --git a/packages/core/src/core/Viewpoints/index.ts b/packages/core/src/core/Viewpoints/index.ts new file mode 100644 index 000000000..04dd613e2 --- /dev/null +++ b/packages/core/src/core/Viewpoints/index.ts @@ -0,0 +1,35 @@ +import { World, Component, Disposable, Event, DataMap } from "../Types"; +import { Components } from "../Components"; +import { BCFViewpoint, Viewpoint } from "./src"; + +export class Viewpoints extends Component implements Disposable { + static readonly uuid = "ee867824-a796-408d-8aa0-4e5962a83c66" as const; + enabled = true; + + readonly list = new DataMap(); + + create(world: World, data?: Partial) { + const viewpoint = new Viewpoint(this.components, world); + if (data) { + viewpoint.guid = data.guid ?? viewpoint.guid; + viewpoint.set(data); + } + this.list.set(viewpoint.guid, viewpoint); + return viewpoint; + } + + constructor(components: Components) { + super(components); + components.add(Viewpoints.uuid, this); + } + + readonly onDisposed = new Event(); + + dispose() { + this.list.dispose(); + this.onDisposed.trigger(); + this.onDisposed.reset(); + } +} + +export * from "./src"; 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..6053698ab --- /dev/null +++ b/packages/core/src/core/Viewpoints/src/Viewpoint.ts @@ -0,0 +1,360 @@ +import * as THREE from "three"; +import * as FRAGS from "@thatopen/fragments"; +import { UUID } from "../../../utils"; +import { + BCFViewpoint, + ViewpointCamera, + ViewpointOrthographicCamera, + ViewpointPerspectiveCamera, +} from "./types"; +import { + CameraProjection, + OrthoPerspectiveCamera, +} from "../../OrthoPerspectiveCamera"; +import { Components } from "../../Components"; +import { DataSet, World } from "../../Types"; +import { FragmentsManager } from "../../../fragments/FragmentsManager"; +import { BCFTopics } from "../../../openbim/BCFTopics"; +import { Raycasters } from "../../Raycasters"; +import { BoundingBoxer } from "../../../fragments/BoundingBoxer"; +import { Hider } from "../../../fragments"; +import { SimplePlane } from "../../Clipper"; + +export class Viewpoint implements BCFViewpoint { + name = "Viewpoint"; + guid = UUID.create(); + + clippingPlanes = new DataSet(); + + camera: ViewpointPerspectiveCamera | ViewpointOrthographicCamera = { + aspectRatio: 0, + fov: 0, + direction: { x: 0, y: 0, z: 0 }, + position: { x: 0, y: 0, z: 0 }, + }; + + readonly exceptionComponents = new DataSet(); + readonly selectionComponents = new DataSet(); + spacesVisible = false; + spaceBoundariesVisible = false; + openingsVisible = false; + defaultVisibility = true; + + private get _selectionModelIdMap() { + 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; + } + + private get _exceptionModelIdMap() { + 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.exceptionComponents) { + 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._selectionModelIdMap, + ); + return fragmentIdMap; + } + + get exception() { + const fragments = this._components.get(FragmentsManager); + const fragmentIdMap = fragments.modelIdToFragmentIdMap( + this._exceptionModelIdMap, + ); + return fragmentIdMap; + } + + get projection(): CameraProjection { + if ("fov" in this.camera) return "Perspective"; + return "Orthographic"; + } + + get position() { + const fragments = this._components.get(FragmentsManager); + const { position } = this.camera; + const { x, y, z } = position; + const vector = new THREE.Vector3(x, y, z); + fragments.applyBaseCoordinateSystem(vector, new THREE.Matrix4()); + return vector; + } + + get direction() { + const { direction } = this.camera; + const { x, y, z } = direction; + const vector = new THREE.Vector3(x, y, z); + return vector; + } + + private _components: Components; + readonly world: World; + + private get _managerVersion() { + const manager = this._components.get(BCFTopics); + 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; + 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 _data = data as any; + const _this = this as any; + for (const key in data) { + if (key === "guid") continue; + const value = _data[key]; + if (key in this) _this[key] = value; + } + } + + 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 position = this.position; + const direction = this.direction; + + // Default target based on the viewpoint information + const target = { + x: position.x + direction.x * 80, + y: position.y + direction.y * 80, + z: position.z + 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(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; + // } + + // Sets the viewpoint components visibility + const hider = this._components.get(Hider); + hider.set(this.defaultVisibility); + hider.set(!this.defaultVisibility, this.exception); + hider.set(true, selection); // Always make sure the selection is visible + + await camera.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 aspectRatio = 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(aspectRatio)) aspectRatio = 1; + + const fragments = this._components.get(FragmentsManager); + position.applyMatrix4(fragments.baseCoordinationMatrix.clone().invert()); + // fragments.applyBaseCoordinateSystem(position, new THREE.Matrix4()); + + const partialCamera: ViewpointCamera = { + aspectRatio, + 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, + viewToWorldScale: threeCamera.top - threeCamera.bottom, + }; + } + } + + private async createComponentTags(from: "selection" | "exception") { + const fragments = this._components.get(FragmentsManager); + const manager = this._components.get(BCFTopics); + let tags = ""; + if (manager.config.includeSelectionTag) { + const modelIdMap = + from === "selection" + ? this._selectionModelIdMap + : this._exceptionModelIdMap; + 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}"`; + tags += `\n`; + } + } + } else { + tags = [...this.selectionComponents] + .map((globalId) => ``) + .join(`\n`); + } + return tags; + } + + async serialize(version = this._managerVersion) { + const fragments = this._components.get(FragmentsManager); + + // Set the position back to the original transformation for exporting purposes + const position = this.position; + fragments.applyBaseCoordinateSystem(position, new THREE.Matrix4()); + + // Set the direction back to the original transformation for exporting purposes + const direction = this.direction; + direction.normalize(); + + const rotationMatrix = new THREE.Matrix4().makeRotationX(Math.PI / 2); + const upVector = direction.clone().applyMatrix4(rotationMatrix); + upVector.normalize(); + + const cameraViewpointXML = ` + ${-position.x} + ${position.z} + ${-position.y} + `; + + const cameraDirectionXML = ` + ${direction.x} + ${-direction.z} + ${direction.y} + `; + + const cameraUpVectorXML = ` + ${upVector.x} + ${-upVector.z} + ${upVector.y} + `; + + const cameraRatioXML = `${this.camera.aspectRatio}`; + + 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} + `; + } + + const viewSetupHints = ``; + + const selectionTags = (await this.createComponentTags("selection")).trim(); + const exceptionTags = (await this.createComponentTags("exception")).trim(); + + return ` + + + ${version === "2.1" ? viewSetupHints : ""} + ${selectionTags.length !== 0 ? `${selectionTags}` : ""} + + ${version === "3" ? viewSetupHints : ""} + ${exceptionTags.length !== 0 ? `${exceptionTags}` : ""} + + + ${cameraXML} + `; + } +} 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..415d33534 --- /dev/null +++ b/packages/core/src/core/Viewpoints/src/index.ts @@ -0,0 +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 new file mode 100644 index 000000000..662f94329 --- /dev/null +++ b/packages/core/src/core/Viewpoints/src/types.ts @@ -0,0 +1,29 @@ +import { SimplePlane } from "../../Clipper"; +import { DataSet } from "../../Types"; + +export interface ViewpointCamera { + direction: { x: number; y: number; z: number }; + position: { x: number; y: number; z: number }; + aspectRatio: number; +} + +export interface ViewpointPerspectiveCamera extends ViewpointCamera { + fov: number; +} + +export interface ViewpointOrthographicCamera extends ViewpointCamera { + viewToWorldScale: number; +} + +export interface BCFViewpoint { + name: string; + guid: string; + camera: ViewpointPerspectiveCamera | ViewpointOrthographicCamera; + selectionComponents: Iterable; + exceptionComponents: Iterable; + clippingPlanes: DataSet; + 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/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/BCFTopics/example.html b/packages/core/src/openbim/BCFTopics/example.html new file mode 100644 index 000000000..4d7bebdb7 --- /dev/null +++ b/packages/core/src/openbim/BCFTopics/example.html @@ -0,0 +1,32 @@ + + + + + + + Codestin Search App + + + + + + + + + \ No newline at end of file diff --git a/packages/core/src/openbim/BCFTopics/example.ts b/packages/core/src/openbim/BCFTopics/example.ts new file mode 100644 index 000000000..1e9829fbd --- /dev/null +++ b/packages/core/src/openbim/BCFTopics/example.ts @@ -0,0 +1,239 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import * as BUI from "@thatopen/ui"; +import * as THREE from "three"; +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; + +world.scene.three.add(new THREE.AxesHelper(10)); + +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 fragments = components.get(OBC.FragmentsManager); +fragments.onFragmentsLoaded.add(async (model) => { + world.scene.three.add(model); + if (model.hasProperties) await indexer.process(model); + for (const fragment of model.items) world.meshes.add(fragment.mesh); +}); + +const loadModels = async (urls: string[]) => { + for (const url of urls) { + const file = await fetch(url); + const data = await file.arrayBuffer(); + const buffer = new Uint8Array(data); + await ifcLoader.load(buffer); + } +}; + +// await loadModels([ +// "https://thatopen.github.io/engine_components/resources/small.ifc", +// ]); + +await loadModels([ + "/resources/NAV-IPI-ET1_E03-ZZZ-M3D-EST.ifc", + "/resources/NAV-IPI-ET1_E07-ZZZ-M3D-EST.ifc", +]); + +const bcfTopics = components.get(OBC.BCFTopics); +bcfTopics.setup({ + types: new Set(["Clash", "Inquiry", "Information", "Coordination"]), + statuses: new Set([ + "Active", + "In Progress", + "Completed", + "In Review", + "Closed", + ]), + priorities: new Set(["Low", "Normal", "High", "Critical"]), +}); + +const viewpoints = components.get(OBC.Viewpoints); +viewpoints.list.onItemSet.add(({ value: viewpoint }) => console.log(viewpoint)); + +// Importing an external BCF (topics and viewpoints are going to be created) +const bcfFile = await fetch("/resources/topics.bcf"); +const bcfData = await bcfFile.arrayBuffer(); +await bcfTopics.import(world, new Uint8Array(bcfData)); + +// Creating a custom Topic +const topicsTable = document.createElement("bim-table"); +topicsTable.hiddenColumns = ["Guid"]; +topicsTable.columns = ["Title"]; +topicsTable.dataTransform = { + Actions: (_, rowData) => { + const { Guid } = rowData; + if (!(Guid && typeof Guid === "string")) return Guid; + const viewpoints = components.get(OBC.Viewpoints); + const viewpoint = viewpoints.list.get(Guid); + if (!viewpoint) return Guid; + return BUI.html` + viewpoint.go()} icon="ph:eye-fill"> + console.log(viewpoint.selection)} icon="ph:cursor-fill"> + viewpoint.update()} icon="jam:refresh"> + viewpoints.list.delete(viewpoint.guid)} icon="tabler:trash-filled"> + `; + }, +}; + +const [topicsList, updatetopicsList] = BUI.Component.create( + (state: { components: OBC.Components }) => { + const { components } = state; + const topics = components.get(OBC.BCFTopics); + topicsTable.data = [...topics.list.values()].map((topic) => { + return { + data: { + Guid: topic.guid, + Title: topic.title, + Description: topic.description ?? "", + Author: topic.creationAuthor, + Date: topic.creationDate.toDateString(), + Type: topic.type, + Status: topic.status, + Priority: topic.priority ?? "", + Labels: [...topic.labels].join(", "), + }, + }; + }); + return BUI.html`${topicsTable}`; + }, + { components }, +); + +bcfTopics.list.onItemSet.add(() => updatetopicsList()); +bcfTopics.list.onCleared.add(() => updatetopicsList()); +bcfTopics.list.onItemDeleted.add(() => updatetopicsList()); + +const topic = bcfTopics.create({ + description: "It seems these elements are badly defined.", + type: "Information", + priority: "High", + stage: "Design", + labels: new Set(["Architecture", "Cost Estimation"]), +}); + +// Creating a custom viewpoint +const viewpointsTable = document.createElement("bim-table"); +viewpointsTable.headersHidden = true; +viewpointsTable.hiddenColumns = ["Guid"]; +viewpointsTable.columns = ["Name", { name: "Actions", width: "auto" }]; +viewpointsTable.dataTransform = { + Actions: (_, rowData) => { + const { Guid } = rowData; + if (!(Guid && typeof Guid === "string")) return Guid; + const viewpoints = components.get(OBC.Viewpoints); + const viewpoint = viewpoints.list.get(Guid); + if (!viewpoint) return Guid; + return BUI.html` + viewpoint.go()} icon="ph:eye-fill"> + console.log(viewpoint.selection)} icon="ph:cursor-fill"> + viewpoint.update()} icon="jam:refresh"> + viewpoints.list.delete(viewpoint.guid)} icon="tabler:trash-filled"> + `; + }, +}; + +const [viewpointsList, updateViewpointsList] = BUI.Component.create( + (state: { components: OBC.Components }) => { + const { components } = state; + const viewpoints = components.get(OBC.Viewpoints); + viewpointsTable.data = [...viewpoints.list.values()].map((viewpoint) => { + return { + data: { + Guid: viewpoint.guid, + Name: viewpoint.name, + Actions: "", + }, + }; + }); + return BUI.html`${viewpointsTable}`; + }, + { + components, + }, +); + +viewpoints.list.onItemSet.add(() => updateViewpointsList()); +viewpoints.list.onItemDeleted.add(() => updateViewpointsList()); +viewpoints.list.onCleared.add(() => updateViewpointsList()); + +const viewpoint = viewpoints.create(world, { name: "Custom Viewpoint" }); +// 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 + +topic.viewpoints.add(viewpoint); +topic.createComment("Hi there! I agree."); + +const leftPanel = BUI.Component.create(() => { + return BUI.html` + + + ${viewpointsList} + + + `; +}); + +const bottomPanel = BUI.Component.create(() => { + const onBcfDownload = async () => { + const bcf = await bcfTopics.export(); + // 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` + + + + ${topicsList} + + + `; +}); + +const app = document.getElementById("app") as BUI.Grid; +app.layouts = { + main: { + template: ` + "leftPanel viewport" 2fr + "leftPanel bottomPanel" 1fr + / 25rem 1fr + `, + elements: { leftPanel, viewport, bottomPanel }, + }, +}; + +app.layout = "main"; diff --git a/packages/core/src/openbim/BCFTopics/index.ts b/packages/core/src/openbim/BCFTopics/index.ts new file mode 100644 index 000000000..b1f7b18f5 --- /dev/null +++ b/packages/core/src/openbim/BCFTopics/index.ts @@ -0,0 +1,532 @@ +import JSZip from "jszip"; +import * as THREE from "three"; +import { XMLParser } from "fast-xml-parser"; +import { + Component, + Configurable, + Disposable, + Event, + World, + DataMap, +} from "../../core/Types"; +import { BCFTopic, BCFTopicsConfig, BCFVersion, Topic } from "./src"; +import { + BCFViewpoint, + ViewpointCamera, + Viewpoints, +} from "../../core/Viewpoints"; +import { Comment } from "./src/Comment"; +import { Clipper } from "../../core/Clipper"; + +export class BCFTopics + extends Component + implements Disposable, Configurable +{ + static uuid = "de977976-e4f6-4e4f-a01a-204727839802" as const; + enabled = false; + + private _xmlParser = new XMLParser({ + allowBooleanAttributes: true, + attributeNamePrefix: "", + ignoreAttributes: false, + ignoreDeclaration: true, + ignorePiTags: true, + numberParseOptions: { leadingZeros: true, hex: true }, + parseAttributeValue: true, + preserveOrder: false, + processEntities: false, + removeNSPrefix: true, + trimValues: true, + }); + + config: Required = { + author: "jhon.doe@example.com", + version: "2.1", + types: new Set([ + "Clash", + "Failure", + "Fault", + "Inquiry", + "Issue", + "Remark", + "Request", + ]), + statuses: new Set(["Active", "Done", "Closed"]), + priorities: new Set(["Critical", "Major", "Normal", "Minor", "On hold"]), + labels: new Set(), + stages: new Set(), + users: new Set(), + includeSelectionTag: false, + updateExtensionsOnImport: true, + strict: false, + includeAllExtensionsOnExport: true, + fallbackVersionOnImport: "2.1", + ignoreIncompleteTopicsOnImport: false, + }; + + readonly list = new DataMap(); + + 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(); + } + + readonly onBCFImported = new Event(); + + create(data?: Partial) { + const topic = new Topic(this.components); + if (data) { + topic.guid = data.guid ?? topic.guid; + topic.set(data); + } + this.list.set(topic.guid, topic); + return topic; + } + + readonly onDisposed = new Event(); + dispose() { + this.list.dispose(); + this.onDisposed.trigger(); + this.onDisposed.reset(); + } + + /** + * Retrieves the unique set of topic types used across all topics. + * + * @returns A Set containing the unique topic types. + */ + get usedTypes() { + const types = [...this.list].map(([_, topic]) => topic.type); + return new Set(types); + } + + /** + * Retrieves the unique set of topic statuses used across all topics. + * + * @returns A Set containing the unique topic statuses. + */ + get usedStatuses() { + const statuses = [...this.list].map(([_, topic]) => topic.status); + return new Set(statuses); + } + + /** + * Retrieves the unique set of topic priorities used across all topics. + * + * @returns A Set containing the unique topic priorities. + * Note: This method filters out any null or undefined priorities. + */ + get usedPriorities() { + const priorities = [...this.list] + .map(([_, topic]) => topic.priority) + .filter((priority) => priority); + return new Set(priorities); + } + + /** + * Retrieves the unique set of topic stages used across all topics. + * + * @returns A Set containing the unique topic stages. + * Note: This method filters out any null or undefined stages. + */ + get usedStages() { + const stages = [...this.list] + .map(([_, topic]) => topic.stage) + .filter((stage) => stage); + return new Set(stages); + } + + /** + * Retrieves the unique set of users associated with topics. + * + * @returns A Set containing the unique users. + * Note: This method collects users from the creation author, assigned to, modified author, and comment authors. + */ + get usedUsers() { + const users: string[] = []; + for (const [_, topic] of this.list) { + users.push(topic.creationAuthor); + if (topic.assignedTo) users.push(topic.assignedTo); + if (topic.modifiedAuthor) users.push(topic.modifiedAuthor); + for (const comment of topic.comments) { + users.push(comment.author); + if (comment.modifiedAuthor) users.push(comment.modifiedAuthor); + } + } + return new Set(users); + } + + /** + * Retrieves the unique set of labels used across all topics. + * + * @returns A Set containing the unique labels. + */ + get usedLabels() { + const labels: string[] = []; + for (const [_, topic] of this.list) labels.push(...topic.labels); + return new Set(labels); + } + + /** + * Updates the set of extensions (types, statuses, priorities, labels, stages, users) based on the current topics. + * This method iterates through each topic in the list and adds its properties to the corresponding sets in the config. + */ + updateExtensions() { + for (const [_, topic] of this.list) { + for (const label of topic.labels) this.config.labels.add(label); + this.config.types.add(topic.type); + if (topic.priority) this.config.priorities.add(topic.priority); + if (topic.stage) this.config.stages.add(topic.stage); + this.config.statuses.add(topic.status); + this.config.users.add(topic.creationAuthor); + if (topic.assignedTo) this.config.users.add(topic.assignedTo); + if (topic.modifiedAuthor) this.config.users.add(topic.modifiedAuthor); + for (const comment of topic.comments) { + this.config.users.add(comment.author); + if (comment.modifiedAuthor) + this.config.users.add(comment.modifiedAuthor); + } + } + } + + private 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.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` : ""} + + `; + } + + async export(topics = this.list) { + const zip = new JSZip(); + zip.file( + "bcf.version", + ` + + `, + ); + zip.file("bcf.extensions", this.serializeExtensions()); + const image = await fetch( + "https://thatopen.github.io/engine_components/resources/favicon.ico", + ); + const imgBlob = await image.blob(); + 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) { + topicFolder.file(`${viewpoint.guid}.jpeg`, imgBlob, { + binary: true, + }); + topicFolder.file(`${viewpoint.guid}.bcfv`, await viewpoint.serialize()); + } + } + const content = await zip.generateAsync({ type: "blob" }); + return content; + } + + private processMarkupComment(markupComment: any) { + const { + Guid, + Date: CommentDate, + Author, + Comment: CommentText, + Viewpoint, + } = markupComment; + if (!(Guid && CommentDate && Author && (Comment || Viewpoint))) return null; + const viewpoints = this.components.get(Viewpoints); + const comment = new Comment(this.components, CommentText ?? "") as Comment; + comment.guid = Guid; + comment.date = new Date(CommentDate); + comment.author = Author; + comment.viewpoint = viewpoints.list.get(Viewpoint.Guid); + comment.modifiedAuthor = markupComment.ModifiedAuthor; + comment.modifiedDate = markupComment.ModifiedDate + ? new Date(markupComment.ModifiedDate) + : undefined; + return comment; + } + + private getMarkupComments(markup: any, version: BCFVersion) { + let data: any; + if (version === "2.1") data = markup.Comment; + if (version === "3") data = markup.Topic.Comments?.Comment; + if (!data) return []; + data = Array.isArray(data) ? data : [data]; + const comments = data + .map((comment: any) => this.processMarkupComment(comment)) + .filter((comment: any) => comment) as Comment[]; + const array = Array.isArray(comments) ? comments : [comments]; + return array; + } + + private getLabelsFromXML(markup: any, version: BCFVersion) { + let data: any; + if (version === "2.1") data = markup.Topic.Labels; + if (version === "3") data = markup.Topic.Labels?.Label; + if (!data) return []; + const labels: string[] = Array.isArray(data) ? data : [data]; + return labels; + } + + // world: the default world where the viewpoints are going to be created + async import(world: World, data: Uint8Array) { + const { fallbackVersionOnImport, ignoreIncompleteTopicsOnImport } = + this.config; + const zip = new JSZip(); + await zip.loadAsync(data); + + const files = Object.values(zip.files); + + // Get BCF Version from incomming data + let version = fallbackVersionOnImport; + const versionFile = files.find((file) => file.name.endsWith(".version")); + if (versionFile) { + const versionXML = await versionFile.async("string"); + const bcfVersion = this._xmlParser.parse(versionXML).Version.VersionId; + version = String(bcfVersion) as BCFVersion; + } + + // Viewpoints must be processed first as they don't care about the topic, but the topic and comments care about them + const viewpoints = this.components.get(Viewpoints); + const viewpointFiles = files.filter((file) => file.name.endsWith(".bcfv")); + for (const viewpointFile of viewpointFiles) { + const xml = await viewpointFile.async("string"); + const visualizationInfo = this._xmlParser.parse(xml).VisualizationInfo; + if (!visualizationInfo) { + console.warn("Missing VisualizationInfo in Viewpoint"); + continue; + } + + const bcfViewpoint: Partial = {}; + + const { + Guid, + ClippingPlanes, + Components, + OrthogonalCamera, + PerspectiveCamera, + } = visualizationInfo; + + if (Guid) bcfViewpoint.guid = Guid; + + if (Components) { + const { Selection, Visibility } = Components; + if (Selection && Selection.Component) { + const components = Array.isArray(Selection.Component) + ? Selection.Component + : [Selection.Component]; + bcfViewpoint.selectionComponents = components + .map((component: any) => component.IfcGuid) + .filter((guid: any) => guid); + } + if (Visibility && "DefaultVisibility" in Visibility) { + bcfViewpoint.defaultVisibility = Visibility.DefaultVisibility; + } + if ( + Visibility && + Visibility.Exceptions && + "Component" in Visibility.Exceptions + ) { + const { Component } = Visibility.Exceptions; + const components = Array.isArray(Component) ? Component : [Component]; + bcfViewpoint.exceptionComponents = components + .map((component: any) => component.IfcGuid) + .filter((guid: any) => guid); + } + let ViewSetupHints; + if (version === "2.1") { + ViewSetupHints = Components.ViewSetupHints; + } + if (version === "3") { + ViewSetupHints = Components.Visibility?.ViewSetupHints; + } + if (ViewSetupHints) { + if ("OpeningsVisible" in ViewSetupHints) { + bcfViewpoint.openingsVisible = ViewSetupHints.OpeningsVisible; + } + if ("SpacesVisible" in ViewSetupHints) { + bcfViewpoint.spacesVisible = ViewSetupHints.SpacesVisible; + } + if ("SpaceBoundariesVisible" in ViewSetupHints) { + bcfViewpoint.spaceBoundariesVisible = + ViewSetupHints.SpaceBoundariesVisible; + } + } + } + + if (OrthogonalCamera || PerspectiveCamera) { + const camera = + visualizationInfo.PerspectiveCamera ?? + visualizationInfo.OrthogonalCamera; + const { CameraViewPoint, CameraDirection } = camera; + + const position = new THREE.Vector3( + Number(CameraViewPoint.X), + Number(CameraViewPoint.Z), + Number(-CameraViewPoint.Y), + ); + + const direction = new THREE.Vector3( + Number(CameraDirection.X), + Number(CameraDirection.Z), + Number(-CameraDirection.Y), + ); + + const viewpointCamera: ViewpointCamera = { + position: { x: position.x, y: position.y, z: position.z }, + direction: { x: direction.x, y: direction.y, z: direction.z }, + aspectRatio: "AspectRatio" in camera ? camera.AspectRatio : 1, // Temporal simplification + }; + + if ("ViewToWorldScale" in camera) { + bcfViewpoint.camera = { + ...viewpointCamera, + viewToWorldScale: camera.ViewToWorldScale, + }; + } + + if ("FieldOfView" in camera) { + bcfViewpoint.camera = { + ...viewpointCamera, + fov: camera.FieldOfView, + }; + } + } + + const viewpoint = viewpoints.create(world, bcfViewpoint); + + if (ClippingPlanes) { + const clipper = this.components.get(Clipper); + const planes = Array.isArray(ClippingPlanes.ClippingPlane) + ? ClippingPlanes.ClippingPlane + : [ClippingPlanes.ClippingPlane]; + for (const plane of planes) { + const { Location, Direction } = plane; + if (!(Location && Direction)) continue; + const location = new THREE.Vector3( + Location.X, + Location.Z, + -Location.Y, + ); + const direction = new THREE.Vector3( + Direction.X, + -Direction.Z, + Direction.Y, + ); + const clippingPlane = clipper.createFromNormalAndCoplanarPoint( + world, + direction, + location, + ); + clippingPlane.visible = false; + clippingPlane.enabled = false; + viewpoint.clippingPlanes.add(clippingPlane); + } + } + } + + // Process markup files + const topics: Topic[] = []; + const markupFiles = files.filter((file) => file.name.endsWith(".bcf")); + for (const markupFile of markupFiles) { + const xml = await markupFile.async("string"); + const markup = this._xmlParser.parse(xml).Markup; + const markupTopic = markup.Topic; + const { Guid, Type, Status, Title, CreationDate, CreationAuthor } = + markupTopic; + + // Required Data + if (ignoreIncompleteTopicsOnImport) { + if ( + !(Guid && Type && Status && Title && CreationDate && CreationAuthor) + ) + continue; + } + const topic = new Topic(this.components); + topic.guid = Guid ?? topic.guid; + topic.type = Type ?? topic.type; + topic.status = Status ?? topic.status; + topic.title = Title ?? topic.title; + topic.creationDate = CreationDate + ? new Date(CreationDate) + : topic.creationDate; + topic.creationAuthor = CreationAuthor ?? topic.creationAuthor; + + // Optional Data + topic.serverAssignedId = markupTopic.ServerAssignedId; + topic.priority = markupTopic.Priority; + topic.index = markupTopic.Index; + topic.modifiedDate = markupTopic.ModifiedDate + ? new Date(markupTopic.ModifiedDate) + : undefined; + topic.modifiedAuthor = markupTopic.ModifiedAuthor; + topic.dueDate = markupTopic.DueDate + ? new Date(markupTopic.DueDate) + : undefined; + topic.assignedTo = markupTopic.AssignedTo; + topic.description = markupTopic.Description; + topic.stage = markupTopic.Stage; + const labels = this.getLabelsFromXML(markup, version); + for (const label of labels) topic.labels.add(label); + + // Comments + const comments = this.getMarkupComments(markup, version); + for (const comment of comments) topic.comments.add(comment); + + // Viewpoints + const markupViewpoints = Array.isArray(markup.Viewpoints) + ? markup.Viewpoints + : [markup.Viewpoints]; + for (const markupViewpoint of markupViewpoints) { + if (!(markupViewpoint && markupViewpoint.Guid)) continue; + const viewpoint = viewpoints.list.get(markupViewpoint.Guid); + if (viewpoint) { + viewpoint.name = topic.title; + topic.viewpoints.add(viewpoint); + } + } + + this.list.set(topic.guid, topic); + topics.push(topic); + } + this.onBCFImported.trigger(topics); + } +} + +export * from "./src"; diff --git a/packages/core/src/openbim/BCFTopics/src/Comment.ts b/packages/core/src/openbim/BCFTopics/src/Comment.ts new file mode 100644 index 000000000..bb87c99ab --- /dev/null +++ b/packages/core/src/openbim/BCFTopics/src/Comment.ts @@ -0,0 +1,83 @@ +import { BCFTopics } 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(BCFTopics); + 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(BCFTopics); + 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(BCFTopics); + this.author = manager.config.author; + } + + 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/BCFTopics/src/Topic.ts b/packages/core/src/openbim/BCFTopics/src/Topic.ts new file mode 100644 index 000000000..0343cce7c --- /dev/null +++ b/packages/core/src/openbim/BCFTopics/src/Topic.ts @@ -0,0 +1,251 @@ +import { UUID } from "../../../utils"; +import { Components } from "../../../core/Components"; +import { Viewpoint } from "../../../core/Viewpoints"; +import { Comment } from "./Comment"; +import { BCFTopics } from ".."; +import { BCFTopic } from "./types"; +import { DataSet } from "../../../core/Types"; + +export class Topic implements BCFTopic { + // By no means a Topic guid must be changed after it has been created + guid = UUID.create(); + title = "BCF Topic"; + creationDate = new Date(); + creationAuthor = ""; + readonly comments = new DataSet(); + readonly viewpoints = new DataSet(); + customData: Record = {}; + description?: string; + serverAssignedId?: string; + dueDate?: Date; + modifiedAuthor?: string; + modifiedDate?: Date; + index?: number; + + private _type = "Issue"; + + set type(value: string) { + const manager = this._components.get(BCFTopics); + 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(BCFTopics); + 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(BCFTopics); + 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(BCFTopics); + 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(BCFTopics); + 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(BCFTopics); + 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() { + const manager = this._components.get(BCFTopics); + return manager.config.version; + } + + constructor(components: Components) { + this._components = components; + const manager = components.get(BCFTopics); + this.creationAuthor = manager.config.author; + } + + set(data: Partial) { + const _data = data as any; + const _this = this as any; + for (const key in data) { + if (key === "guid") continue; + const value = _data[key]; + if (key in this) _this[key] = value; + } + } + + 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/BCFTopics/src/index.ts b/packages/core/src/openbim/BCFTopics/src/index.ts new file mode 100644 index 000000000..177ea0a1b --- /dev/null +++ b/packages/core/src/openbim/BCFTopics/src/index.ts @@ -0,0 +1,3 @@ +export * from "./Topic"; +export * from "./types"; +export * from "./Comment"; diff --git a/packages/core/src/openbim/BCFTopics/src/types.ts b/packages/core/src/openbim/BCFTopics/src/types.ts new file mode 100644 index 000000000..51447d3d1 --- /dev/null +++ b/packages/core/src/openbim/BCFTopics/src/types.ts @@ -0,0 +1,59 @@ +export type BCFVersion = "2.1" | "3"; + +export interface BCFTopic { + guid: string; + serverAssignedId?: string; + type: string; + status: string; + title: string; + priority?: string; + index?: number; + labels: Set; + creationDate: Date; + creationAuthor: string; + modifiedDate?: Date; + modifiedAuthor?: string; + dueDate?: Date; + assignedTo?: string; + description?: string; + stage?: string; +} + +export interface BCFTopicsConfig { + // The BCF version used during export + version: BCFVersion; + + // The user (usually an email) creating topics using this component + author: string; + + // The set of allowed topic types. This is exported inside the [bcf.extensions](https://github.com/buildingSMART/BCF-XML/tree/release_3_0/Documentation#bcf-file-structure). The default values are the ones from the most widely used BCF plugin in BIM apps: BCF Manager from BIM Collab. + types: Set; + + // The set of allowed topic statuses. This is exported inside the [bcf.extensions](https://github.com/buildingSMART/BCF-XML/tree/release_3_0/Documentation#bcf-file-structure). + statuses: Set; + + // The set of allowed topic priorities. This is exported inside the [bcf.extensions](https://github.com/buildingSMART/BCF-XML/tree/release_3_0/Documentation#bcf-file-structure). The default values are the ones from the most widely used BCF plugin in BIM apps: BCF Manager from BIM Collab. + 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 config when setting the corresponding data in a topic. + strict: boolean; + + // If true, export the extensions (types, status, etc.) based on topics data. This doesn't update the extensions in the config. If false, only export the extensions defined in each collection of possibilities set in the config. In all cases, all the values from each collection of extensions defined in the config are going to be exported. + includeAllExtensionsOnExport: boolean; + + // Version to be used when importing if no bcf.version file is present in the incomming data + fallbackVersionOnImport: BCFVersion; + + // If true, do not import a topic with missing information (guid, type, status, title, creationDate or creationAuthor). If false, use default values for missing data. + ignoreIncompleteTopicsOnImport: boolean; +} diff --git a/packages/core/src/openbim/index.ts b/packages/core/src/openbim/index.ts new file mode 100644 index 000000000..2c7d2bbf5 --- /dev/null +++ b/packages/core/src/openbim/index.ts @@ -0,0 +1 @@ +export * from "./BCFTopics"; diff --git a/resources/topics.bcf b/resources/topics.bcf new file mode 100644 index 000000000..4e6d559f1 Binary files /dev/null and b/resources/topics.bcf differ diff --git a/yarn.lock b/yarn.lock index 7e982d6d8..5674fbd02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -245,11 +245,11 @@ __metadata: linkType: hard "@floating-ui/core@npm:^1.0.0": - version: 1.6.5 - resolution: "@floating-ui/core@npm:1.6.5" + version: 1.6.6 + resolution: "@floating-ui/core@npm:1.6.6" dependencies: - "@floating-ui/utils": ^0.2.5 - checksum: 8e6c62a6e9223fba9afbcaca8afe408788a2bc8ab1b2f5734a26d5b02d4017a2baffc7176a938a610fd243e6a983ada605f259b35c88813e2230dd29906a78fd + "@floating-ui/utils": ^0.2.6 + checksum: 3992e93f40655798bff7a721258cd9f2ad4d98eca9e29ecbf3dc79154a191f47fbab3d86777fecc08e83fdc64a9f390a5256f3cf10543db87f177863770ddeeb languageName: node linkType: hard @@ -263,10 +263,10 @@ __metadata: languageName: node linkType: hard -"@floating-ui/utils@npm:^0.2.0, @floating-ui/utils@npm:^0.2.5": - version: 0.2.5 - resolution: "@floating-ui/utils@npm:0.2.5" - checksum: 32834fe0fec5ee89187f8defd0b10813d725dab7dc6ed1545ded6655630bac5d438f0c991d019d675585e118846f12391236fc2886a5c73a57576e7de3eca3f9 +"@floating-ui/utils@npm:^0.2.0, @floating-ui/utils@npm:^0.2.6": + version: 0.2.6 + resolution: "@floating-ui/utils@npm:0.2.6" + checksum: 4350edad705f69f944f853d95b2832fbf3d6ff2347276526198040ddec03a7e4afae0b5863d50373275dd319ae648c8070fd528405a1a10a1620277534846792 languageName: node linkType: hard @@ -317,9 +317,9 @@ __metadata: linkType: hard "@lit-labs/ssr-dom-shim@npm:^1.2.0": - version: 1.2.0 - resolution: "@lit-labs/ssr-dom-shim@npm:1.2.0" - checksum: 704621c28df8d651e54a1b93f6ede8103db2dd3e7a1f02463fe5492bd28aa22de813314c7833260204fed5c8491a6bbd763f6051abc25690df537d812a508c35 + version: 1.2.1 + resolution: "@lit-labs/ssr-dom-shim@npm:1.2.1" + checksum: 5667c44f58e16edaa257fc3ae7f752250d5250d4eb1d071b65df0f1fce0b90b42e8528787cc2673998d76d993440143a2a20c3358ce125c62df4cd193784de8d languageName: node linkType: hard @@ -463,114 +463,114 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.19.1" +"@rollup/rollup-android-arm-eabi@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.20.0" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-android-arm64@npm:4.19.1" +"@rollup/rollup-android-arm64@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-android-arm64@npm:4.20.0" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-darwin-arm64@npm:4.19.1" +"@rollup/rollup-darwin-arm64@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-darwin-arm64@npm:4.20.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-darwin-x64@npm:4.19.1" +"@rollup/rollup-darwin-x64@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-darwin-x64@npm:4.20.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.19.1" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.20.0" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.19.1" +"@rollup/rollup-linux-arm-musleabihf@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.20.0" conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.19.1" +"@rollup/rollup-linux-arm64-gnu@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.20.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.19.1" +"@rollup/rollup-linux-arm64-musl@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.20.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-powerpc64le-gnu@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.19.1" +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.20.0" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.19.1" +"@rollup/rollup-linux-riscv64-gnu@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.20.0" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.19.1" +"@rollup/rollup-linux-s390x-gnu@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.20.0" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.19.1" +"@rollup/rollup-linux-x64-gnu@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.20.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.19.1" +"@rollup/rollup-linux-x64-musl@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.20.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.19.1" +"@rollup/rollup-win32-arm64-msvc@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.20.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.19.1" +"@rollup/rollup-win32-ia32-msvc@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.20.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.19.1": - version: 4.19.1 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.19.1" +"@rollup/rollup-win32-x64-msvc@npm:4.20.0": + version: 4.20.0 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.20.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -649,6 +649,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 @@ -673,8 +675,8 @@ __metadata: linkType: hard "@thatopen/ui-obc@npm:~2.1.0": - version: 2.1.5 - resolution: "@thatopen/ui-obc@npm:2.1.5" + version: 2.1.6 + resolution: "@thatopen/ui-obc@npm:2.1.6" dependencies: "@thatopen/ui": ~2.1.0 lit: 3.1.2 @@ -683,7 +685,7 @@ __metadata: "@thatopen/components-front": ~2.1.0 three: 0.160.1 web-ifc: 0.0.56 - checksum: 526e37f56bab1a0664656edf765bded2c05fb7305f00ee93cbb1600fe13572d04e183358a6f1bba707dfa86fb9dd7f96d864889dde19b42833567d5eb06309f5 + checksum: fc4b9cacfa65052231261e293687fdc2022d65810c11e43dcd14a2d02b3c2190568d5e7f21a0dede4e592ba98e9a19daed4fb84916213c0eba1ca16cc761f492 languageName: node linkType: hard @@ -931,26 +933,26 @@ __metadata: languageName: node linkType: hard -"@vue/compiler-core@npm:3.4.35": - version: 3.4.35 - resolution: "@vue/compiler-core@npm:3.4.35" +"@vue/compiler-core@npm:3.4.36": + version: 3.4.36 + resolution: "@vue/compiler-core@npm:3.4.36" dependencies: "@babel/parser": ^7.24.7 - "@vue/shared": 3.4.35 - entities: ^4.5.0 + "@vue/shared": 3.4.36 + entities: ^5.0.0 estree-walker: ^2.0.2 source-map-js: ^1.2.0 - checksum: 9f050edee2efefd214dcff65efea7f9faab63bab34ce31fb6ea08a7a12bad5a792b2b9f31e2f72a3d034474bd217707c769a5341e9006c8d442024647895cdd5 + checksum: a6c4c1b0c64cf3f3f139ce4518bb0e298484fa194f7e4fd3b6181b8f4b1a00b63a61fe68f53e6b1a49e26e7fc14fcc79d661db0a966dd5fbbb39170e7a9dbfed languageName: node linkType: hard "@vue/compiler-dom@npm:^3.3.0": - version: 3.4.35 - resolution: "@vue/compiler-dom@npm:3.4.35" + version: 3.4.36 + resolution: "@vue/compiler-dom@npm:3.4.36" dependencies: - "@vue/compiler-core": 3.4.35 - "@vue/shared": 3.4.35 - checksum: 3944753daadef563249778e5672dae80c66e687ed48509de682ffc9f620e13b373492925f2658a2c938dabcfb3914b39582b77e92e9190913be528d3e91434fc + "@vue/compiler-core": 3.4.36 + "@vue/shared": 3.4.36 + checksum: ba9520bacfbf02baedfebfbd8a9013a972bf9c70c7f193948333aee2e8b157aa56d081f3cb7c644f9601753d917df1b4cc519afca4b7f108eb48ed279c82df1c languageName: node linkType: hard @@ -976,10 +978,10 @@ __metadata: languageName: node linkType: hard -"@vue/shared@npm:3.4.35, @vue/shared@npm:^3.3.0": - version: 3.4.35 - resolution: "@vue/shared@npm:3.4.35" - checksum: 6397a102ff46e7c5e3d2507b6eb5390f4c07e56898cfdadf7be52e2bf124d1adce41c0c27b81c684ee3e211b480e67e11015fc4e3d14dcd2768ba9045a69d874 +"@vue/shared@npm:3.4.36, @vue/shared@npm:^3.3.0": + version: 3.4.36 + resolution: "@vue/shared@npm:3.4.36" + checksum: 540fbdb8495a805771155ec72afaa29ca1cacdff54bf1ffe8a921704e6a7687b49803ae0ec0eb7d96db8acbb97cb0f30de74f63168b5c52b5b50586f535a0a9a languageName: node linkType: hard @@ -1338,6 +1340,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" @@ -1510,10 +1519,10 @@ __metadata: languageName: node linkType: hard -"entities@npm:^4.5.0": - version: 4.5.0 - resolution: "entities@npm:4.5.0" - checksum: 853f8ebd5b425d350bffa97dd6958143179a5938352ccae092c62d1267c4e392a039be1bae7d51b6e4ffad25f51f9617531fedf5237f15df302ccfb452cbf2d7 +"entities@npm:^5.0.0": + version: 5.0.0 + resolution: "entities@npm:5.0.0" + checksum: d641555e641ef648ebf92f02d763156ffa35a0136ecd45f26050bd6af299ed7e2a53e5063654b662f72a91a8432f03326f245d4b373824e282afafbe0b4ac320 languageName: node linkType: hard @@ -1987,6 +1996,17 @@ __metadata: languageName: node linkType: hard +"fast-xml-parser@npm:latest": + version: 4.4.1 + resolution: "fast-xml-parser@npm:4.4.1" + dependencies: + strnum: ^1.0.5 + bin: + fxparser: src/cli/cli.js + checksum: f440c01cd141b98789ae777503bcb6727393296094cc82924ae9f88a5b971baa4eec7e65306c7e07746534caa661fc83694ff437d9012dc84dee39dfbfaab947 + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.17.1 resolution: "fastq@npm:1.17.1" @@ -2416,6 +2436,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" @@ -2457,7 +2484,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 @@ -2670,6 +2697,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" @@ -2779,6 +2813,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" @@ -2805,23 +2851,32 @@ __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.6 - resolution: "lit-element@npm:4.0.6" + version: 4.1.0 + resolution: "lit-element@npm:4.1.0" dependencies: "@lit-labs/ssr-dom-shim": ^1.2.0 "@lit/reactive-element": ^2.0.4 - lit-html: ^3.1.2 - checksum: 4f73cd1c0d8dc9643a92a5a6cb93b5a4f1d25675ecd38c2e39583e0ed03f0b2725c0b08330f0f1a70b372e7e4c693839c94d95f59c7c65bbca596f4faf91bfee + lit-html: ^3.2.0 + checksum: 16cc7e343fc7f872a0f6a468bb9d7f3697cd9c3c020fd66e1f29f81e15300dc8d091559a1fd2d4cb6f2eb99b76e3fbeea1991f74dd5ca77bfaadb5b6af3d85b3 languageName: node linkType: hard -"lit-html@npm:^3.1.2": - version: 3.1.4 - resolution: "lit-html@npm:3.1.4" +"lit-html@npm:^3.1.2, lit-html@npm:^3.2.0": + version: 3.2.0 + resolution: "lit-html@npm:3.2.0" dependencies: "@types/trusted-types": ^2.0.2 - checksum: 428593679cd295ef56db7220d1349a2eeba67aa513f99ddb7653d265e914017a17060c293f5fc899d3b6f36329fbac2d9c3392be26648091e3404da90c76bbf4 + checksum: fa566878efab2492f2dc359216bc5ccd5164466f6760984b9f9b7122c4932be19891ddf10a611bc88718e59c49f83f18e9b9e32fe193dcdc37df28f9fe05630c languageName: node linkType: hard @@ -3289,6 +3344,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" @@ -3382,13 +3444,13 @@ __metadata: linkType: hard "postcss@npm:^8.4.35": - version: 8.4.40 - resolution: "postcss@npm:8.4.40" + version: 8.4.41 + resolution: "postcss@npm:8.4.41" dependencies: nanoid: ^3.3.7 picocolors: ^1.0.1 source-map-js: ^1.2.0 - checksum: afd0cc49d2169dcd96c0f17e155c5d75de048956306a3017f1cfa6a7d66b941592245bed20f7796ceeccb2d8967749b623be2c7b010a74f67ea10fb5bdb8ba28 + checksum: f865894929eb0f7fc2263811cc853c13b1c75103028b3f4f26df777e27b201f1abe21cb4aa4c2e901c80a04f6fb325ee22979688fe55a70e2ea82b0a517d3b6f languageName: node linkType: hard @@ -3433,6 +3495,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" @@ -3457,6 +3526,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" @@ -3548,25 +3632,25 @@ __metadata: linkType: hard "rollup@npm:^4.2.0": - version: 4.19.1 - resolution: "rollup@npm:4.19.1" - dependencies: - "@rollup/rollup-android-arm-eabi": 4.19.1 - "@rollup/rollup-android-arm64": 4.19.1 - "@rollup/rollup-darwin-arm64": 4.19.1 - "@rollup/rollup-darwin-x64": 4.19.1 - "@rollup/rollup-linux-arm-gnueabihf": 4.19.1 - "@rollup/rollup-linux-arm-musleabihf": 4.19.1 - "@rollup/rollup-linux-arm64-gnu": 4.19.1 - "@rollup/rollup-linux-arm64-musl": 4.19.1 - "@rollup/rollup-linux-powerpc64le-gnu": 4.19.1 - "@rollup/rollup-linux-riscv64-gnu": 4.19.1 - "@rollup/rollup-linux-s390x-gnu": 4.19.1 - "@rollup/rollup-linux-x64-gnu": 4.19.1 - "@rollup/rollup-linux-x64-musl": 4.19.1 - "@rollup/rollup-win32-arm64-msvc": 4.19.1 - "@rollup/rollup-win32-ia32-msvc": 4.19.1 - "@rollup/rollup-win32-x64-msvc": 4.19.1 + version: 4.20.0 + resolution: "rollup@npm:4.20.0" + dependencies: + "@rollup/rollup-android-arm-eabi": 4.20.0 + "@rollup/rollup-android-arm64": 4.20.0 + "@rollup/rollup-darwin-arm64": 4.20.0 + "@rollup/rollup-darwin-x64": 4.20.0 + "@rollup/rollup-linux-arm-gnueabihf": 4.20.0 + "@rollup/rollup-linux-arm-musleabihf": 4.20.0 + "@rollup/rollup-linux-arm64-gnu": 4.20.0 + "@rollup/rollup-linux-arm64-musl": 4.20.0 + "@rollup/rollup-linux-powerpc64le-gnu": 4.20.0 + "@rollup/rollup-linux-riscv64-gnu": 4.20.0 + "@rollup/rollup-linux-s390x-gnu": 4.20.0 + "@rollup/rollup-linux-x64-gnu": 4.20.0 + "@rollup/rollup-linux-x64-musl": 4.20.0 + "@rollup/rollup-win32-arm64-msvc": 4.20.0 + "@rollup/rollup-win32-ia32-msvc": 4.20.0 + "@rollup/rollup-win32-x64-msvc": 4.20.0 "@types/estree": 1.0.5 fsevents: ~2.3.2 dependenciesMeta: @@ -3606,7 +3690,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: b431700a5248563c0e1d03521b8262aad9adc46007159b2792569ff56e5b11cbaa555809b90765f291484dad726f2250270112943bcbd8a067608804d96fa3d5 + checksum: 92c6c68a93d7726345df2627fd5b0a88d1481fbe76e6c8ad84a8eae6835c03fc36ed4cb3271350b5290397b26eb97a97297496ca972289b2299a24e81649bca0 languageName: node linkType: hard @@ -3650,6 +3734,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" @@ -3723,6 +3814,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" @@ -3900,6 +3998,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" @@ -3932,6 +4039,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" @@ -4193,6 +4307,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"