├── .nojekyll ├── packages ├── core │ ├── .npmignore │ ├── src │ │ ├── typeless-libraries-handler.ts │ │ ├── core │ │ │ ├── Types │ │ │ │ ├── index.ts │ │ │ │ └── src │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── component.ts │ │ │ │ │ ├── base-camera.ts │ │ │ │ │ ├── event-manager.ts │ │ │ │ │ ├── base-world-item.ts │ │ │ │ │ ├── world.ts │ │ │ │ │ ├── base.ts │ │ │ │ │ ├── event.ts │ │ │ │ │ ├── async-event.ts │ │ │ │ │ └── base-scene.ts │ │ │ ├── Clipper │ │ │ │ ├── src │ │ │ │ │ └── index.ts │ │ │ │ └── example.html │ │ │ ├── ConfigManager │ │ │ │ ├── src │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── ShadowedScene │ │ │ │ ├── src │ │ │ │ │ ├── index.ts │ │ │ │ │ └── pixel-reader.ts │ │ │ │ └── example.html │ │ │ ├── Views │ │ │ │ ├── src │ │ │ │ │ ├── index.ts │ │ │ │ │ └── types.ts │ │ │ │ └── example.html │ │ │ ├── Viewpoints │ │ │ │ ├── src │ │ │ │ │ ├── index.ts │ │ │ │ │ └── viewpoints-config.ts │ │ │ │ └── example.html │ │ │ ├── Grids │ │ │ │ ├── src │ │ │ │ │ └── index.ts │ │ │ │ └── example.html │ │ │ ├── Raycasters │ │ │ │ ├── src │ │ │ │ │ └── index.ts │ │ │ │ └── example.html │ │ │ ├── OrthoPerspectiveCamera │ │ │ │ ├── src │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ ├── orbit-mode.ts │ │ │ │ │ ├── first-person-mode.ts │ │ │ │ │ └── plan-mode.ts │ │ │ │ └── example.html │ │ │ ├── Worlds │ │ │ │ ├── src │ │ │ │ │ └── index.ts │ │ │ │ └── example.html │ │ │ └── index.ts │ │ ├── measurement │ │ │ ├── index.ts │ │ │ └── MeasurementUtils │ │ │ │ └── test.html │ │ ├── fragments │ │ │ ├── Classifier │ │ │ │ ├── src │ │ │ │ │ └── index.ts │ │ │ │ └── example.html │ │ │ ├── FragmentsManager │ │ │ │ ├── src │ │ │ │ │ ├── index.ts │ │ │ │ │ └── types.ts │ │ │ │ └── example.html │ │ │ ├── ItemsFinder │ │ │ │ ├── src │ │ │ │ │ └── index.ts │ │ │ │ └── example.html │ │ │ ├── index.ts │ │ │ ├── IfcLoader │ │ │ │ ├── src │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ifc-fragment-settings.ts │ │ │ │ └── example.html │ │ │ ├── Hider │ │ │ │ └── example.html │ │ │ └── BoundingBoxer │ │ │ │ └── example.html │ │ ├── openbim │ │ │ ├── BCFTopics │ │ │ │ ├── src │ │ │ │ │ ├── importers │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── extensions.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── Comment.ts │ │ │ │ └── example.html │ │ │ ├── index.ts │ │ │ └── IDSSpecifications │ │ │ │ ├── src │ │ │ │ ├── index.ts │ │ │ │ ├── importers │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── entity.ts │ │ │ │ │ ├── attribute.ts │ │ │ │ │ ├── material.ts │ │ │ │ │ ├── part-of.ts │ │ │ │ │ ├── property.ts │ │ │ │ │ └── classification.ts │ │ │ │ └── facets │ │ │ │ │ ├── index.ts │ │ │ │ │ └── PartOf.ts │ │ │ │ └── example.html │ │ ├── utils │ │ │ ├── index.ts │ │ │ └── xml.ts │ │ └── index.ts │ ├── tsconfig-build.json │ ├── tsconfig.node.json │ ├── resources │ │ └── updateComponentsVersion.mjs │ ├── tsconfig.json │ └── package.json └── front │ ├── .npmignore │ ├── src │ ├── typeless-libraries-handler.ts │ ├── fragments │ │ ├── Highlighter │ │ │ ├── src │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ └── example.html │ │ ├── index.ts │ │ ├── Hoverer │ │ │ └── example.html │ │ ├── Outliner │ │ │ └── example.html │ │ └── Mesher │ │ │ └── sample.html │ ├── measurement │ │ ├── utils │ │ │ ├── index.ts │ │ │ └── dimension-mark.ts │ │ ├── AreaMeasurement │ │ │ ├── src │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ └── example.html │ │ ├── LengthMeasurement │ │ │ ├── src │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ └── example.html │ │ ├── VolumeMeasurement │ │ │ ├── src │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ └── example.html │ │ ├── Measurement │ │ │ └── src │ │ │ │ ├── index.ts │ │ │ │ ├── default-units.ts │ │ │ │ └── types.ts │ │ └── index.ts │ ├── core │ │ ├── ClipStyler │ │ │ ├── src │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ └── example.html │ │ ├── Marker │ │ │ ├── src │ │ │ │ ├── index.ts │ │ │ │ └── renderer-with-2d.ts │ │ │ └── example.html │ │ ├── index.ts │ │ ├── PlatformComponents │ │ │ ├── test.ts │ │ │ ├── test.html │ │ │ └── index.ts │ │ └── PostproductionRenderer │ │ │ └── example.html │ ├── index.ts │ ├── civil │ │ ├── index.ts │ │ ├── Types │ │ │ └── index.ts │ │ └── CivilNavigators │ │ │ └── example.html │ └── utils │ │ ├── index.ts │ │ ├── line.ts │ │ └── volume.ts │ ├── tsconfig-build.json │ ├── tsconfig.node.json │ ├── tsconfig.json │ ├── vite.config.ts │ └── package.json ├── .DS_Store ├── .yarnrc.yml ├── resources ├── .DS_Store ├── cover.png ├── topics.bcf ├── favicon.ico ├── frags │ ├── school_arq.frag │ ├── school_str.frag │ └── small_road.frag ├── readme-copier.mjs ├── mock-cloud-component-2.js ├── mock-cloud-component.js └── specs.ids ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ └── feature_request.yml ├── workflows │ ├── publish-npm.yml │ └── release-please.yml └── PULL_REQUEST_TEMPLATE.md ├── jest.config.ts ├── LICENSE.md ├── examples ├── paths.json ├── Views │ └── index.html ├── BCFTopics │ └── index.html ├── IDSSpecifications │ └── index.html ├── Viewpoints │ └── index.html ├── ItemsFinder │ └── index.html ├── Hider │ └── index.html ├── Worlds │ └── index.html ├── Clipper │ └── index.html ├── Raycasters │ └── index.html ├── BoundingBoxer │ └── index.html ├── Highlighter │ └── index.html ├── IfcLoader │ └── index.html ├── Classifier │ └── index.html ├── ClipStyler │ └── index.html ├── Marker │ └── index.html ├── Hoverer │ └── index.html ├── FragmentsManager │ └── index.html ├── assets │ ├── index-BOl1U6wY.js │ ├── hoverer.js │ └── worlds.js ├── OrthoPerspectiveCamera │ └── index.html ├── AreaMeasurement │ └── index.html ├── LengthMeasurement │ └── index.html ├── Outliner │ └── index.html ├── Grids │ └── index.html ├── VolumeMeasurement │ └── index.html ├── ShadowedScene │ └── index.html └── PostproductionRenderer │ └── index.html ├── .eslintrc.cjs ├── vite.config.ts ├── package.json └── vite.config-examples.ts /.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/core/.npmignore: -------------------------------------------------------------------------------- 1 | /resources/ 2 | /src/ -------------------------------------------------------------------------------- /packages/core/src/typeless-libraries-handler.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/front/.npmignore: -------------------------------------------------------------------------------- 1 | /resources/ 2 | /src/ -------------------------------------------------------------------------------- /packages/core/src/core/Types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./src"; 2 | -------------------------------------------------------------------------------- /packages/core/src/measurement/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./MeasurementUtils"; 2 | -------------------------------------------------------------------------------- /packages/front/src/typeless-libraries-handler.ts: -------------------------------------------------------------------------------- 1 | declare module "n8ao"; 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThatOpen/engine_components/HEAD/.DS_Store -------------------------------------------------------------------------------- /packages/core/src/core/Clipper/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./simple-plane"; 2 | -------------------------------------------------------------------------------- /packages/core/src/fragments/Classifier/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | -------------------------------------------------------------------------------- /packages/front/src/fragments/Highlighter/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | -------------------------------------------------------------------------------- /packages/front/src/measurement/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./dimension-mark"; 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | yarnPath: .yarn/releases/yarn-3.2.1.cjs 2 | nodeLinker: node-modules 3 | -------------------------------------------------------------------------------- /packages/core/src/core/ConfigManager/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./configurator"; 2 | -------------------------------------------------------------------------------- /packages/core/src/fragments/FragmentsManager/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | -------------------------------------------------------------------------------- /packages/front/src/measurement/AreaMeasurement/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | -------------------------------------------------------------------------------- /packages/core/src/core/ShadowedScene/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./distance-renderer"; 2 | -------------------------------------------------------------------------------- /packages/core/src/openbim/BCFTopics/src/importers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./extensions"; 2 | -------------------------------------------------------------------------------- /packages/front/src/measurement/LengthMeasurement/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | -------------------------------------------------------------------------------- /packages/front/src/measurement/VolumeMeasurement/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | -------------------------------------------------------------------------------- /packages/core/src/core/Views/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./view"; 2 | export * from "./types"; 3 | -------------------------------------------------------------------------------- /resources/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThatOpen/engine_components/HEAD/resources/.DS_Store -------------------------------------------------------------------------------- /resources/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThatOpen/engine_components/HEAD/resources/cover.png -------------------------------------------------------------------------------- /resources/topics.bcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThatOpen/engine_components/HEAD/resources/topics.bcf -------------------------------------------------------------------------------- /packages/core/src/core/Viewpoints/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | export * from "./viewpoint"; 3 | -------------------------------------------------------------------------------- /packages/core/src/openbim/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./BCFTopics"; 2 | export * from "./IDSSpecifications"; 3 | -------------------------------------------------------------------------------- /resources/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThatOpen/engine_components/HEAD/resources/favicon.ico -------------------------------------------------------------------------------- /packages/front/src/core/ClipStyler/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | export * from "./clip-edges"; 3 | -------------------------------------------------------------------------------- /packages/front/src/core/Marker/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./mark"; 2 | export * from "./renderer-with-2d"; 3 | -------------------------------------------------------------------------------- /packages/core/src/core/Grids/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./simple-grid"; 2 | export * from "./simple-grid-config"; 3 | -------------------------------------------------------------------------------- /packages/core/src/core/Raycasters/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./mouse"; 2 | export * from "./simple-raycaster"; 3 | -------------------------------------------------------------------------------- /packages/core/src/fragments/ItemsFinder/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./finder-query"; 2 | export * from "./types"; 3 | -------------------------------------------------------------------------------- /packages/front/src/measurement/Measurement/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | export * from "./default-units"; 3 | -------------------------------------------------------------------------------- /resources/frags/school_arq.frag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThatOpen/engine_components/HEAD/resources/frags/school_arq.frag -------------------------------------------------------------------------------- /resources/frags/school_str.frag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThatOpen/engine_components/HEAD/resources/frags/school_str.frag -------------------------------------------------------------------------------- /resources/frags/small_road.frag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThatOpen/engine_components/HEAD/resources/frags/small_road.frag -------------------------------------------------------------------------------- /packages/front/tsconfig-build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"], 4 | "exclude": ["src/**/example.ts"] 5 | } -------------------------------------------------------------------------------- /packages/core/tsconfig-build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["./src"], 4 | "exclude": ["./src/**/example.ts"] 5 | } -------------------------------------------------------------------------------- /packages/core/src/openbim/IDSSpecifications/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | export * from "./facets"; 3 | export * from "./Specification"; 4 | -------------------------------------------------------------------------------- /packages/core/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./uuid"; 2 | export * from "./xml"; 3 | export * from "./vertex-picker"; 4 | export * from "./model-id-map"; 5 | -------------------------------------------------------------------------------- /packages/front/src/fragments/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Highlighter"; 2 | export * from "./Outliner"; 3 | export * from "./Hoverer"; 4 | export * from "./Mesher"; 5 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./core"; 2 | export * from "./utils"; 3 | export * from "./fragments"; 4 | export * from "./openbim"; 5 | export * from "./measurement"; 6 | -------------------------------------------------------------------------------- /packages/front/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./core"; 2 | export * from "./fragments"; 3 | export * from "./civil"; 4 | export * from "./measurement"; 5 | export * from "./utils"; 6 | -------------------------------------------------------------------------------- /packages/front/src/core/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./PlatformComponents"; 2 | export * from "./Marker"; 3 | export * from "./PostproductionRenderer"; 4 | export * from "./ClipStyler"; 5 | -------------------------------------------------------------------------------- /packages/front/src/measurement/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./AreaMeasurement"; 2 | export * from "./LengthMeasurement"; 3 | export * from "./Measurement"; 4 | export * from "./VolumeMeasurement"; 5 | -------------------------------------------------------------------------------- /packages/core/src/openbim/BCFTopics/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Topic"; 2 | export * from "./types"; 3 | export * from "./Comment"; 4 | export * from "./importers"; 5 | export * from "./bcf-topics-config"; 6 | -------------------------------------------------------------------------------- /packages/core/src/core/OrthoPerspectiveCamera/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./first-person-mode"; 2 | export * from "./orbit-mode"; 3 | export * from "./plan-mode"; 4 | export * from "./projections"; 5 | export * from "./types"; 6 | -------------------------------------------------------------------------------- /packages/core/src/core/Worlds/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./simple-world"; 2 | export * from "./simple-scene"; 3 | export * from "./simple-renderer"; 4 | export * from "./simple-camera"; 5 | export * from "./simple-scene-config"; 6 | -------------------------------------------------------------------------------- /packages/front/src/civil/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./CivilNavigators"; 2 | export * from "./Types"; 3 | export * from "./CivilRaycaster"; 4 | export * from "./Utils/civil-utils"; 5 | export * from "./CivilCrossSectionNavigator"; 6 | -------------------------------------------------------------------------------- /packages/core/src/fragments/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./FragmentsManager"; 2 | export * from "./IfcLoader"; 3 | export * from "./Hider"; 4 | export * from "./BoundingBoxer"; 5 | export * from "./ItemsFinder"; 6 | export * from "./Classifier"; 7 | -------------------------------------------------------------------------------- /packages/core/src/openbim/IDSSpecifications/src/importers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./entity"; 2 | export * from "./attribute"; 3 | export * from "./material"; 4 | export * from "./property"; 5 | export * from "./classification"; 6 | export * from "./part-of"; 7 | -------------------------------------------------------------------------------- /packages/core/src/fragments/IfcLoader/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./ifc-fragment-settings"; 2 | // export * from "./spatial-structure"; 3 | // export * from "./civil-reader"; 4 | // export * from "./ifc-metadata-reader"; 5 | // export * from "./spatial-ids-finder"; 6 | -------------------------------------------------------------------------------- /packages/front/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./graphic-vertex-picker"; 2 | export * from "./dimension-line"; 3 | export * from "./area"; 4 | export * from "./line"; 5 | export * from "./volume"; 6 | export * from "./measure-fill"; 7 | export * from "./measure-mark"; 8 | -------------------------------------------------------------------------------- /packages/core/src/openbim/IDSSpecifications/src/facets/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Attribute"; 2 | export * from "./Classification"; 3 | export * from "./Entity"; 4 | export * from "./Property"; 5 | export * from "./Facet"; 6 | export * from "./Material"; 7 | export * from "./PartOf"; 8 | -------------------------------------------------------------------------------- /packages/core/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["./vite.config.ts", "./package.json"] 10 | } -------------------------------------------------------------------------------- /packages/front/src/measurement/Measurement/src/default-units.ts: -------------------------------------------------------------------------------- 1 | import { MeasureToUnitMap } from "./types"; 2 | 3 | export const defaultUnits: Record< 4 | keyof MeasureToUnitMap, 5 | MeasureToUnitMap[keyof MeasureToUnitMap] 6 | > = { 7 | length: "m", 8 | area: "m2", 9 | volume: "m3", 10 | }; 11 | -------------------------------------------------------------------------------- /packages/front/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config-library.ts", "package.json"] 10 | } -------------------------------------------------------------------------------- /packages/front/src/civil/Types/index.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | 3 | export enum CivilMarkerType { 4 | SELECT = "select", 5 | HOVER = "hover", 6 | } 7 | 8 | export interface CivilPoint { 9 | point: THREE.Vector3; 10 | normal: THREE.Vector3; 11 | curve: THREE.Line; 12 | alignment: THREE.Group; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/fragments/FragmentsManager/src/types.ts: -------------------------------------------------------------------------------- 1 | import * as FRAGS from "@thatopen/fragments"; 2 | 3 | /** 4 | * Mapping of model identifiers to a collection of numbers representing localIds. 5 | */ 6 | export type ModelIdMap = Record>; 7 | 8 | export type ModelIdDataMap = FRAGS.DataMap>; 9 | -------------------------------------------------------------------------------- /packages/front/src/measurement/VolumeMeasurement/src/types.ts: -------------------------------------------------------------------------------- 1 | import { MeasureVolume } from "../../../utils/measure-volume"; 2 | 3 | export interface VolumeMeasurerTempData { 4 | preview?: MeasureVolume; 5 | } 6 | 7 | /** 8 | * Represents the modes available for the volume measurement tool. 9 | */ 10 | export type VolumeMeasurerModes = ["free"]; 11 | -------------------------------------------------------------------------------- /packages/front/src/measurement/Measurement/src/types.ts: -------------------------------------------------------------------------------- 1 | export type MeasureToUnitMap = { 2 | length: "mm" | "cm" | "m" | "km"; 3 | area: "mm2" | "cm2" | "m2" | "km2"; 4 | volume: "mm3" | "cm3" | "m3" | "km3"; 5 | }; 6 | 7 | export type MeasurementStateChange = 8 | | "mode" 9 | | "color" 10 | | "units" 11 | | "rounding" 12 | | "visibility" 13 | | "enabled"; 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: Want our help? 🤝 3 | url: https://thatopen.com/accelerator 4 | about: Are you developing a project with our technology and would like our help? Apply now to join That Open Accelerator Program! 5 | - name: Community 6 | url: https://people.thatopen.com/ 7 | about: Join the community and discuss with other BIM software developers in real time. 8 | -------------------------------------------------------------------------------- /packages/core/src/core/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./ConfigManager"; 2 | export * from "./Disposer"; 3 | export * from "./Grids"; 4 | export * from "./Types"; 5 | export * from "./Worlds"; 6 | export * from "./Components"; 7 | export * from "./Raycasters"; 8 | export * from "./OrthoPerspectiveCamera"; 9 | export * from "./Viewpoints"; 10 | export * from "./Clipper"; 11 | export * from "./Views"; 12 | export * from "./ShadowedScene"; 13 | -------------------------------------------------------------------------------- /packages/core/src/core/Types/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./event"; 2 | export * from "./async-event"; 3 | export * from "./interfaces"; 4 | export * from "./component"; 5 | export * from "./base"; 6 | export * from "./base-world-item"; 7 | export * from "./base-camera"; 8 | export * from "./base-renderer"; 9 | export * from "./base-scene"; 10 | export * from "./world"; 11 | export * from "./data-set"; 12 | export * from "./data-map"; 13 | export * from "./config-types"; 14 | export * from "./event-manager"; 15 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { InitialOptionsTsJest } from "ts-jest/dist/types"; 2 | 3 | const config: InitialOptionsTsJest = { 4 | preset: "ts-jest", 5 | testEnvironment: "jsdom", 6 | transform: { 7 | "^.+\\.(t|j)s$": "ts-jest", 8 | }, 9 | globals: { 10 | "ts-jest": { 11 | tsconfig: "./tsconfig.jest.json", 12 | }, 13 | }, 14 | // These node_modules need to be transpiled 15 | // https://stackoverflow.com/a/63390125/3466729 16 | transformIgnorePatterns: [ 17 | "node_modules/(?!(web-ifc-three|web-ifc|three|@popperjs/core/dist/esm))", 18 | ], 19 | }; 20 | export default config; 21 | -------------------------------------------------------------------------------- /packages/front/src/measurement/AreaMeasurement/src/types.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import { DataSet } from "@thatopen/fragments"; 3 | import { Area, DimensionLine } from "../../../utils"; 4 | 5 | export interface AreaMeasurerTempData { 6 | isDragging: boolean; 7 | area: Area; 8 | lines: DataSet; 9 | point: THREE.Vector3; 10 | } 11 | 12 | // Potential new modes: 13 | // face 14 | /** 15 | * Represents the modes available for measuring areas. `free`: Allows freeform area measurement without constraints. `square`: Restricts area measurement to square shapes. 16 | */ 17 | export type AreaMeasurerModes = ["free", "square", "face"]; 18 | -------------------------------------------------------------------------------- /packages/core/src/utils/xml.ts: -------------------------------------------------------------------------------- 1 | import { XMLBuilder, XMLParser } from "fast-xml-parser"; 2 | 3 | export class XML { 4 | static parser = new XMLParser({ 5 | allowBooleanAttributes: true, 6 | attributeNamePrefix: "", 7 | ignoreAttributes: false, 8 | ignoreDeclaration: true, 9 | ignorePiTags: true, 10 | numberParseOptions: { leadingZeros: true, hex: true }, 11 | parseAttributeValue: true, 12 | preserveOrder: false, 13 | processEntities: false, 14 | removeNSPrefix: true, 15 | trimValues: true, 16 | }); 17 | 18 | static builder = new XMLBuilder({ 19 | attributeNamePrefix: "$", 20 | ignoreAttributes: false, 21 | suppressBooleanAttributes: false, 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /packages/core/resources/updateComponentsVersion.mjs: -------------------------------------------------------------------------------- 1 | // import * as fs from "fs"; 2 | 3 | // const packageJsonPath = "./package.json"; 4 | 5 | // const targetFilePath = "./src/core/Components/index.ts"; 6 | 7 | // const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); 8 | // const newVersion = packageJson.version; 9 | 10 | // const fileContent = fs.readFileSync(targetFilePath, "utf8"); 11 | 12 | // const versionRegex = /static readonly release = "\d+\.\d+\.\d+";/; 13 | 14 | // const newContent = fileContent.replace( 15 | // versionRegex, 16 | // `static readonly release = "${newVersion}";`, 17 | // ); 18 | 19 | // fs.writeFileSync(targetFilePath, newContent, "utf8"); 20 | 21 | // console.log(`Version updated to ${newVersion} in ${targetFilePath}`); 22 | -------------------------------------------------------------------------------- /.github/workflows/publish-npm.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/publish-npm.yml 2 | # This workflow pushes new published releases to https://www.npmjs.com using 3 | # the Yarn Package Manager. 4 | name: Publish package to npmjs (using Yarn) 5 | on: 6 | release: 7 | types: [published] 8 | workflow_call: 9 | secrets: 10 | NPM_TOKEN: 11 | required: true 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: actions/setup-node@v3 18 | with: 19 | node-version: '18.16.x' # Use LTS 20 | registry-url: 'https://registry.npmjs.org' 21 | scope: '@octocat' 22 | - run: yarn 23 | - run: yarn publish-repo 24 | env: 25 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 26 | -------------------------------------------------------------------------------- /packages/front/src/measurement/LengthMeasurement/src/types.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import { DimensionLine, Line } from "../../../utils"; 3 | 4 | export interface LengthMeasurerTempData { 5 | isDragging: boolean; 6 | line: Line; 7 | startNormal?: THREE.Vector3; 8 | endNormal?: THREE.Vector3; 9 | dimension?: DimensionLine; 10 | } 11 | 12 | // Potential new modes: 13 | // parallelEdges: select two edges and take the measurement between them 14 | // sequential: when the user makes the second click, a new measure will start 15 | /** 16 | * Represents the modes available for the length measurement tool. `free`: Allows free-form measurement without constraints. `edge`: Enables measurement constrained to item edges. 17 | */ 18 | export type LengthMeasurerModes = ["free", "edge"]; 19 | -------------------------------------------------------------------------------- /packages/core/src/openbim/IDSSpecifications/src/importers/entity.ts: -------------------------------------------------------------------------------- 1 | import { Components } from "../../../../core/Components"; 2 | import { IDSEntity, IDSFacet } from "../facets"; 3 | import { getParameterValue } from "./parameter"; 4 | 5 | export const createEntityFacets = (components: Components, elements: any) => { 6 | const facets: IDSFacet[] = []; 7 | for (const element of elements) { 8 | const nameParameter = element.name; 9 | const name = getParameterValue(nameParameter); 10 | if (!name) continue; 11 | const facet = new IDSEntity(components, name); 12 | if (element.cardinality) facet.cardinality = element.cardinality; 13 | facet.predefinedType = getParameterValue(element.predefinedType); 14 | facet.instructions = element.instructions; 15 | facets.push(facet); 16 | } 17 | return facets; 18 | }; 19 | -------------------------------------------------------------------------------- /resources/readme-copier.mjs: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import { fileURLToPath } from "url"; 4 | 5 | const targetPackages = ["core", "front"]; 6 | 7 | const __filename = fileURLToPath(import.meta.url); 8 | const __dirname = path.dirname(__filename); 9 | 10 | for (const dir of targetPackages) { 11 | // Get paths relative to this script 12 | const rootDir = path.resolve(__dirname, ".."); 13 | const sourceReadme = path.join(rootDir, "README.md"); 14 | const targetReadme = path.join(rootDir, "packages", dir, "README.md"); 15 | 16 | // Copy the README file 17 | try { 18 | fs.copyFileSync(sourceReadme, targetReadme); 19 | console.log(`README.md successfully copied to packages/${dir}/`); 20 | } catch (err) { 21 | console.error("Error copying README.md:", err); 22 | process.exit(1); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/src/openbim/IDSSpecifications/src/importers/attribute.ts: -------------------------------------------------------------------------------- 1 | import { Components } from "../../../../core/Components"; 2 | import { IDSAttribute, IDSFacet } from "../facets"; 3 | import { getParameterValue } from "./parameter"; 4 | 5 | export const createAttributeFacets = ( 6 | components: Components, 7 | elements: any, 8 | ) => { 9 | const facets: IDSFacet[] = []; 10 | for (const element of elements) { 11 | const nameParameter = element.name; 12 | const name = getParameterValue(nameParameter); 13 | if (!name) continue; 14 | const facet = new IDSAttribute(components, name); 15 | if (element.cardinality) facet.cardinality = element.cardinality; 16 | facet.value = getParameterValue(element.value); 17 | facet.instructions = element.instructions; 18 | facets.push(facet); 19 | } 20 | return facets; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/front/src/utils/line.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import * as OBC from "@thatopen/components"; 3 | 4 | export class Line extends THREE.Line3 { 5 | id = OBC.UUID.create(); 6 | 7 | private _units: "m" | "cm" | "mm" | "km" = "m"; 8 | 9 | set units(value: "m" | "cm" | "mm" | "km") { 10 | this._units = value; 11 | } 12 | 13 | get units() { 14 | return this._units; 15 | } 16 | 17 | private _rounding = 2; 18 | 19 | set rounding(value: number) { 20 | this._rounding = value; 21 | } 22 | 23 | get rounding() { 24 | return this._rounding; 25 | } 26 | 27 | get value() { 28 | const length = this.distance(); 29 | const convertedValue = OBC.MeasurementUtils.convertUnits( 30 | length, 31 | "m", 32 | this.units, 33 | this.rounding, 34 | ); 35 | return convertedValue; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/core/src/openbim/IDSSpecifications/src/importers/material.ts: -------------------------------------------------------------------------------- 1 | import { Components } from "../../../../core/Components"; 2 | import { IDSFacet, IDSMaterial } from "../facets"; 3 | import { getParameterValue } from "./parameter"; 4 | 5 | export const createMaterialFacets = (components: Components, elements: any) => { 6 | const facets: IDSFacet[] = []; 7 | 8 | for (const element of elements) { 9 | const facet = new IDSMaterial(components); 10 | if (element.cardinality) facet.cardinality = element.cardinality; 11 | 12 | const value = getParameterValue(element.value); 13 | if (value?.type === "enumeration" && Array.isArray(value.parameter)) { 14 | value.parameter = value.parameter.map(String); 15 | } 16 | 17 | facet.value = value; 18 | facet.uri = element.uri; 19 | facet.instructions = element.instructions; 20 | facets.push(facet); 21 | } 22 | 23 | return facets; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/core/src/core/Types/src/component.ts: -------------------------------------------------------------------------------- 1 | import { Components } from "../../Components"; 2 | import { Base } from "./base"; 3 | 4 | /** 5 | * Components are the building blocks of this library. Components are singleton elements that contain specific functionality. For instance, the Clipper Component can create, delete and handle 3D clipping planes. Components must be unique (they can't be instanced more than once per Components instance), and have a static UUID that identifies them uniquely. The can be accessed globally using the {@link Components} instance. 6 | */ 7 | export abstract class Component extends Base { 8 | /** 9 | * Whether this component is active or not. The behaviour can vary depending 10 | * on the type of component. E.g. a disabled dimension tool will stop creating 11 | * dimensions, while a disabled camera will stop moving. A disabled component 12 | * will not be updated automatically each frame. 13 | */ 14 | abstract enabled: boolean; 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/src/openbim/IDSSpecifications/src/importers/part-of.ts: -------------------------------------------------------------------------------- 1 | import { Components } from "../../../../core/Components"; 2 | import { IDSFacet, IDSPartOf } from "../facets"; 3 | import { getParameterValue } from "./parameter"; 4 | 5 | export const createPartOfFacets = (components: Components, elements: any) => { 6 | const facets: IDSFacet[] = []; 7 | for (const element of elements) { 8 | const entityName = getParameterValue(element.entity.name); 9 | if (!entityName) continue; 10 | 11 | const entityPredefinedType = getParameterValue( 12 | element.entity.predefinedType, 13 | ); 14 | 15 | const facet = new IDSPartOf(components, { 16 | name: entityName, 17 | predefinedType: entityPredefinedType, 18 | }); 19 | 20 | facet.relation = element.relation; 21 | if (element.cardinality) facet.cardinality = element.cardinality; 22 | facet.instructions = element.instructions; 23 | facets.push(facet); 24 | } 25 | return facets; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/core/src/core/ConfigManager/index.ts: -------------------------------------------------------------------------------- 1 | import { Component, DataMap } from "../Types"; 2 | import { Components } from "../Components"; 3 | import { Configurator } from "./src"; 4 | 5 | export * from "./src"; 6 | 7 | /** 8 | * A tool to manage all the configuration from the app centrally. 📘 [API](https://docs.thatopen.com/api/@thatopen/components/classes/ConfigManager). 9 | */ 10 | export class ConfigManager extends Component { 11 | /** 12 | * The list of all configurations of this app. 13 | */ 14 | list = new DataMap>(); 15 | 16 | /** {@link Component.enabled} */ 17 | enabled = true; 18 | 19 | /** 20 | * A unique identifier for the component. 21 | * This UUID is used to register the component within the Components system. 22 | */ 23 | static readonly uuid = "b8c764e0-6b24-4e77-9a32-35fa728ee5b4" as const; 24 | 25 | constructor(components: Components) { 26 | super(components); 27 | components.add(ConfigManager.uuid, this); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/src/core/OrthoPerspectiveCamera/src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The projection system of the camera. 3 | */ 4 | export type CameraProjection = "Perspective" | "Orthographic"; 5 | 6 | /** 7 | * The extensible list of supported navigation modes. 8 | */ 9 | export type NavModeID = "Orbit" | "FirstPerson" | "Plan"; 10 | 11 | /** 12 | * An object that determines the behavior of the camera controls and the user input (e.g. 2D floor plan mode, first person mode, etc). 13 | */ 14 | export interface NavigationMode { 15 | /** The unique ID of this navigation mode. */ 16 | id: NavModeID; 17 | 18 | /** 19 | * Enable or disable this navigation mode. 20 | * When a new navigation mode is enabled, the previous navigation mode 21 | * must be disabled. 22 | * 23 | * @param active - whether to enable or disable this mode. 24 | * @param options - any additional data required to enable or disable it. 25 | * */ 26 | set: (active: boolean, options?: any) => void; 27 | 28 | /** Whether this navigation mode is active or not. */ 29 | enabled: boolean; 30 | } 31 | -------------------------------------------------------------------------------- /packages/core/src/openbim/IDSSpecifications/src/importers/property.ts: -------------------------------------------------------------------------------- 1 | import { Components } from "../../../../core/Components"; 2 | import { IDSFacet, IDSProperty } from "../facets"; 3 | import { getParameterValue } from "./parameter"; 4 | 5 | export const createPropertyFacets = (components: Components, elements: any) => { 6 | const facets: IDSFacet[] = []; 7 | for (const element of elements) { 8 | const psetParameter = element.propertySet; 9 | const baseNameParameter = element.baseName; 10 | const pset = getParameterValue(psetParameter); 11 | const baseName = getParameterValue(baseNameParameter); 12 | if (!(baseName && pset)) continue; 13 | const facet = new IDSProperty(components, pset, baseName); 14 | if (element.cardinality) facet.cardinality = element.cardinality; 15 | const value = getParameterValue(element.value); 16 | facet.value = value; 17 | facet.dataType = element.dataType; 18 | facet.uri = element.uri; 19 | facet.instructions = element.instructions; 20 | facets.push(facet); 21 | } 22 | return facets; 23 | }; 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining 2 | a copy of this software and associated documentation files (the 3 | "Software"), to deal in the Software without restriction, including 4 | without limitation the rights to use, copy, modify, merge, publish, 5 | distribute, sublicense, and/or sell copies of the Software, and to 6 | permit persons to whom the Software is furnished to do so, subject to 7 | the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be 10 | included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 13 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 14 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 16 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 17 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 18 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "Bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true, 21 | 22 | "allowJs": false, 23 | "preserveSymlinks": true, 24 | "forceConsistentCasingInFileNames": true 25 | }, 26 | "references": [{ "path": "./tsconfig.node.json" }], 27 | "typedocOptions": { 28 | "entryPoints": ["./src/index.ts"], 29 | "readme": "README.md", 30 | "includeVersion": true, 31 | "excludeExternals": true, 32 | "excludeNotDocumented": true, 33 | "excludePrivate": true, 34 | "excludeProtected": true, 35 | "excludeReferences": true 36 | }, 37 | } -------------------------------------------------------------------------------- /packages/front/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "Bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true, 21 | 22 | "allowJs": false, 23 | "preserveSymlinks": true, 24 | "forceConsistentCasingInFileNames": true 25 | }, 26 | "references": [{ "path": "./tsconfig.node.json" }], 27 | "typedocOptions": { 28 | "entryPoints": ["./src/index.ts"], 29 | "readme": "README.md", 30 | "includeVersion": true, 31 | "excludeExternals": true, 32 | "excludeNotDocumented": true, 33 | "excludePrivate": true, 34 | "excludeProtected": true, 35 | "excludeReferences": true 36 | }, 37 | } 38 | 39 | -------------------------------------------------------------------------------- /packages/front/src/measurement/utils/dimension-mark.ts: -------------------------------------------------------------------------------- 1 | export function newDimensionMark() { 2 | const mark = document.createElement("div"); 3 | mark.style.backgroundColor = "blue"; 4 | mark.style.color = "white"; 5 | mark.style.padding = "6px"; 6 | mark.style.borderRadius = "6px"; 7 | mark.style.boxShadow = "0px 4px 6px rgba(0, 0, 0, 0.6)"; // Add box shadow effect 8 | mark.style.zIndex = "-10"; 9 | 10 | return mark; 11 | } 12 | 13 | export function newEndPoint( 14 | option: { 15 | color?: string; 16 | size?: string; 17 | border?: string; 18 | background?: string; 19 | } = {}, 20 | ): HTMLDivElement { 21 | const { 22 | color = "white", 23 | size = "4px", 24 | border = "2px solid blue", 25 | background = "white", 26 | } = option; 27 | 28 | const mark = document.createElement("div"); 29 | mark.style.backgroundColor = background; 30 | mark.style.color = color; 31 | mark.style.height = size; 32 | mark.style.width = size; 33 | mark.style.borderRadius = "50%"; 34 | mark.style.border = border; 35 | mark.style.zIndex = "-20"; 36 | 37 | return mark; 38 | } 39 | -------------------------------------------------------------------------------- /packages/core/src/core/Types/src/base-camera.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import CameraControls from "camera-controls"; 3 | import { BaseWorldItem } from "./base-world-item"; 4 | import { CameraControllable } from "./interfaces"; 5 | 6 | /** 7 | * Abstract class representing a camera in a 3D world. All cameras should use this class as a base. 8 | */ 9 | export abstract class BaseCamera extends BaseWorldItem { 10 | /** 11 | * Whether the camera is enabled or not. 12 | */ 13 | abstract enabled: boolean; 14 | 15 | /** 16 | * The Three.js camera instance. 17 | */ 18 | abstract three: THREE.Camera; 19 | 20 | /** 21 | * Optional CameraControls instance for controlling the camera. 22 | * This property is only available if the camera is controllable. 23 | */ 24 | abstract controls?: CameraControls; 25 | 26 | /** 27 | * Checks whether the instance is {@link CameraControllable}. 28 | * 29 | * @returns True if the instance is controllable, false otherwise. 30 | */ 31 | hasCameraControls = (): this is CameraControllable => { 32 | return "controls" in this; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/release-please.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/release-please.yml 2 | # This workflow handles the release-please system that manages releasing new versions. 3 | # It also calls `publish-npm.yml`, which then handles publishing to npmjs. 4 | # See: https://github.com/googleapis/release-please 5 | name: release-please 6 | on: 7 | push: 8 | branches: 9 | - main 10 | permissions: 11 | contents: write 12 | pull-requests: write 13 | jobs: 14 | release-please: 15 | runs-on: ubuntu-latest 16 | outputs: 17 | release_created: ${{ steps.release.outputs.release_created }} 18 | steps: 19 | - uses: google-github-actions/release-please-action@v3 # Handle local releases 20 | id: release 21 | with: 22 | release-type: node 23 | package-name: openbim-components 24 | publish-npm: 25 | needs: release-please 26 | if: ${{ needs.release-please.outputs.release_created }} 27 | uses: ./.github/workflows/publish-npm.yml # Publish to npmjs 28 | secrets: 29 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 30 | # Publish only if release-please creates a published release 31 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Description 4 | 5 | 6 | 7 | ### Additional context 8 | 9 | 10 | 11 | --- 12 | 13 | ### What is the purpose of this pull request? 14 | 15 | - [ ] Bug fix 16 | - [ ] New Feature 17 | - [ ] Documentation update 18 | - [ ] Other 19 | 20 | ### Before submitting the PR, please make sure you do the following: 21 | 22 | - [ ] Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate. 23 | - [ ] Follow the [Conventional Commits v1.0.0](https://www.conventionalcommits.org/en/v1.0.0/) standard for PR naming (e.g. `feat(examples): add hello-world example`). 24 | - [ ] Provide a description in this PR that addresses **what** the PR is solving, or reference the issue that it solves (e.g. `fixes #123`). 25 | - [ ] Ideally, include relevant tests that fail without this PR but pass with it. 26 | -------------------------------------------------------------------------------- /resources/mock-cloud-component-2.js: -------------------------------------------------------------------------------- 1 | class PlatformComponent extends OBC.ComponentWithUI { 2 | static uuid = "cfac4902-0e43-4897-acf2-e3fdc518cac3"; 3 | 4 | onDisposed = new OBC.Event(); 5 | 6 | enabled = true; 7 | 8 | name = "Mock Personal Component"; 9 | 10 | _uiElements = new Set(); 11 | 12 | constructor(components) { 13 | super(components); 14 | this.components.add(PlatformComponent.uuid, this); 15 | } 16 | 17 | getUI() { 18 | console.log("Hello from personal component!"); 19 | return [ 20 | { 21 | name: "Panel", 22 | id: "panel", 23 | icon: "", 24 | componentID: PlatformComponent.uuid, 25 | get: () => { 26 | const panel = BUI.Component.create(() => { 27 | return BUI.html` 28 | 29 | `; 30 | }); 31 | this._uiElements.add(panel); 32 | return { element: panel }; 33 | } 34 | } 35 | ]; 36 | } 37 | 38 | dispose() { 39 | for (const element of this._uiElements) { 40 | element.remove(); 41 | } 42 | this._uiElements.clear(); 43 | } 44 | } 45 | 46 | const main = PlatformComponent; -------------------------------------------------------------------------------- /resources/mock-cloud-component.js: -------------------------------------------------------------------------------- 1 | class PlatformComponent extends OBC.ComponentWithUI { 2 | static uuid = "392b7198-0193-4364-8b89-dfad1b386c50"; 3 | 4 | onDisposed = new OBC.Event(); 5 | 6 | enabled = true; 7 | 8 | name = "Mock Personal Component"; 9 | 10 | _uiElements = new Set(); 11 | 12 | constructor(components) { 13 | super(components); 14 | this.components.add(PlatformComponent.uuid, this); 15 | } 16 | 17 | getUI() { 18 | console.log("Hello from personal component!"); 19 | return [ 20 | { 21 | name: "Panel", 22 | id: "panel", 23 | icon: "", 24 | componentID: PlatformComponent.uuid, 25 | get: () => { 26 | const panel = BUI.Component.create(() => { 27 | return BUI.html` 28 | 29 | `; 30 | }); 31 | this._uiElements.add(panel); 32 | return { element: panel }; 33 | } 34 | } 35 | ]; 36 | } 37 | 38 | dispose() { 39 | for (const element of this._uiElements) { 40 | element.remove(); 41 | } 42 | this._uiElements.clear(); 43 | } 44 | } 45 | 46 | const main = PlatformComponent; -------------------------------------------------------------------------------- /packages/core/src/core/Viewpoints/src/viewpoints-config.ts: -------------------------------------------------------------------------------- 1 | import { BooleanSettingsControl } from "../../Types"; 2 | import { Viewpoints } from "../index"; 3 | import { Configurator } from "../../ConfigManager"; 4 | 5 | /** 6 | * Configuration interface for the Viewpoints general behavior. 7 | */ 8 | export interface ViewpointsConfig { 9 | /** 10 | * Indicates whether to overwrite the fragments colors when applying viewpoints. 11 | * @remarks BCF Viewpoints comes with information to indicate the colors to be applied to components, if any. 12 | * @default false 13 | */ 14 | overwriteColors: boolean; 15 | } 16 | 17 | type ViewpointsConfigType = { 18 | overwriteColors: BooleanSettingsControl; 19 | }; 20 | 21 | export class ViewpointsConfigManager extends Configurator< 22 | Viewpoints, 23 | ViewpointsConfigType 24 | > { 25 | protected _config: ViewpointsConfigType = { 26 | overwriteColors: { 27 | value: false, 28 | type: "Boolean" as const, 29 | }, 30 | }; 31 | 32 | get overwriteColors() { 33 | return this._config.overwriteColors.value; 34 | } 35 | 36 | set overwriteColors(value: boolean) { 37 | this._config.overwriteColors.value = value; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/core/src/openbim/IDSSpecifications/src/importers/classification.ts: -------------------------------------------------------------------------------- 1 | import { Components } from "../../../../core/Components"; 2 | import { IDSClassification, IDSFacet } from "../facets"; 3 | import { getParameterValue } from "./parameter"; 4 | 5 | export const createClassificationFacets = ( 6 | components: Components, 7 | elements: any, 8 | ) => { 9 | const facets: IDSFacet[] = []; 10 | for (const element of elements) { 11 | const systemParameter = element.system; 12 | const system = getParameterValue(systemParameter); 13 | if (!system) continue; 14 | 15 | const facet = new IDSClassification(components, system); 16 | if (element.cardinality) facet.cardinality = element.cardinality; 17 | 18 | const value = getParameterValue(element.value); 19 | if (value?.type === "simple") { 20 | value.parameter = String(value.parameter); 21 | } 22 | 23 | if (value?.type === "enumeration" && Array.isArray(value.parameter)) { 24 | value.parameter = value.parameter.map(String); 25 | } 26 | 27 | facet.value = value; 28 | facet.uri = element.uri; 29 | facet.instructions = element.instructions; 30 | facets.push(facet); 31 | } 32 | return facets; 33 | }; 34 | -------------------------------------------------------------------------------- /examples/paths.json: -------------------------------------------------------------------------------- 1 | ["packages/front/src/measurement/VolumeMeasurement/example.ts","packages/front/src/measurement/LengthMeasurement/example.ts","packages/front/src/measurement/AreaMeasurement/example.ts","packages/front/src/fragments/Outliner/example.ts","packages/front/src/fragments/Hoverer/example.ts","packages/front/src/fragments/Highlighter/example.ts","packages/front/src/core/PostproductionRenderer/example.ts","packages/front/src/core/Marker/example.ts","packages/front/src/core/ClipStyler/example.ts","packages/front/src/civil/CivilNavigators/example.ts","packages/core/src/openbim/IDSSpecifications/example.ts","packages/core/src/openbim/BCFTopics/example.ts","packages/core/src/fragments/ItemsFinder/example.ts","packages/core/src/fragments/IfcLoader/example.ts","packages/core/src/fragments/Hider/example.ts","packages/core/src/fragments/FragmentsManager/example.ts","packages/core/src/fragments/Classifier/example.ts","packages/core/src/fragments/BoundingBoxer/example.ts","packages/core/src/core/Worlds/example.ts","packages/core/src/core/Views/example.ts","packages/core/src/core/Viewpoints/example.ts","packages/core/src/core/ShadowedScene/example.ts","packages/core/src/core/Raycasters/example.ts","packages/core/src/core/OrthoPerspectiveCamera/example.ts","packages/core/src/core/Grids/example.ts","packages/core/src/core/Clipper/example.ts"] -------------------------------------------------------------------------------- /packages/front/vite.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import dts from "vite-plugin-dts"; 3 | import { defineConfig } from "vite"; 4 | import * as path from "path"; 5 | import pluginTerser from "@rollup/plugin-terser"; 6 | import * as packageJson from "./package.json"; 7 | 8 | export default defineConfig({ 9 | build: { 10 | lib: { 11 | entry: path.resolve(__dirname, "./src/index.ts"), 12 | }, 13 | rollupOptions: { 14 | external: Object.keys(packageJson.peerDependencies), 15 | output: [ 16 | { 17 | entryFileNames: `index.js`, 18 | format: "es", 19 | globals: { 20 | three: "THREE", 21 | "@thatopen/fragments": "FRAGS", 22 | "@thatopen/components": "OBC", 23 | "web-ifc": "WEB-IFC", 24 | }, 25 | }, 26 | { 27 | entryFileNames: `index.min.js`, 28 | plugins: [pluginTerser()], 29 | format: "es", 30 | globals: { 31 | three: "THREE", 32 | "@thatopen/fragments": "FRAGS", 33 | "@thatopen/components": "OBC", 34 | "web-ifc": "WEB-IFC", 35 | }, 36 | }, 37 | ], 38 | }, 39 | }, 40 | plugins: [ 41 | dts({ 42 | rollupTypes: true, 43 | }), 44 | ], 45 | }); 46 | -------------------------------------------------------------------------------- /packages/core/src/core/Types/src/event-manager.ts: -------------------------------------------------------------------------------- 1 | import { Event } from "./event"; 2 | import { AsyncEvent } from "./async-event"; 3 | 4 | /** 5 | * Simple class to easily toggle and reset event lists. 6 | */ 7 | export class EventManager { 8 | /** 9 | * The list of events managed by this instance. 10 | */ 11 | list = new Set | AsyncEvent>(); 12 | 13 | /** 14 | * Adds events to this manager. 15 | * @param events the events to add. 16 | */ 17 | add(events: Iterable | AsyncEvent>) { 18 | for (const event of events) { 19 | this.list.add(event); 20 | } 21 | } 22 | /** 23 | * Removes events from this manager. 24 | * @param events the events to remove. 25 | */ 26 | remove(events: Iterable | AsyncEvent>) { 27 | for (const event of events) { 28 | this.list.delete(event); 29 | } 30 | } 31 | 32 | /** 33 | * Sets all the events managed by this instance as enabled or disabled. 34 | * @param active whether to turn on or off the events. 35 | */ 36 | set(active: boolean) { 37 | for (const event of this.list) { 38 | event.enabled = active; 39 | } 40 | } 41 | 42 | /** 43 | * Resets all the events managed by this instance. 44 | */ 45 | reset() { 46 | for (const event of this.list) { 47 | event.reset(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/core/src/core/Types/src/base-world-item.ts: -------------------------------------------------------------------------------- 1 | import { DataMap } from "@thatopen/fragments"; 2 | import { Base } from "./base"; 3 | import { World } from "./world"; 4 | import { Event } from "./event"; 5 | import { Components } from "../../Components"; 6 | 7 | /** 8 | * One of the elements that make a world. It can be either a scene, a camera or a renderer. 9 | */ 10 | export abstract class BaseWorldItem extends Base { 11 | readonly worlds = new DataMap(); 12 | 13 | /** 14 | * Event that is triggered when a world is added or removed from the `worlds` map. 15 | * The event payload contains the world instance and the action ("added" or "removed"). 16 | */ 17 | readonly onWorldChanged = new Event<{ 18 | world: World; 19 | action: "added" | "removed"; 20 | }>(); 21 | 22 | protected _currentWorld: World | null = null; 23 | 24 | /** 25 | * The current world this item is associated with. It can be null if no world is currently active. 26 | */ 27 | set currentWorld(value: World | null) { 28 | this._currentWorld = value; 29 | } 30 | 31 | get currentWorld() { 32 | return this._currentWorld; 33 | } 34 | 35 | protected constructor(components: Components) { 36 | super(components); 37 | 38 | this.onWorldChanged.add(({ world, action }) => { 39 | if (action === "removed") { 40 | this.worlds.delete(world.uuid); 41 | } 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/core/src/core/OrthoPerspectiveCamera/src/orbit-mode.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import { NavigationMode } from "./types"; 3 | import { OrthoPerspectiveCamera } from "../index"; 4 | 5 | /** 6 | * A {@link NavigationMode} that allows 3D navigation and panning like in many 3D and CAD softwares. 7 | */ 8 | export class OrbitMode implements NavigationMode { 9 | /** {@link NavigationMode.enabled} */ 10 | enabled = true; 11 | 12 | /** {@link NavigationMode.id} */ 13 | readonly id = "Orbit"; 14 | 15 | constructor(public camera: OrthoPerspectiveCamera) { 16 | this.activateOrbitControls(); 17 | } 18 | 19 | /** {@link NavigationMode.set} */ 20 | set(active: boolean) { 21 | this.enabled = active; 22 | if (active) { 23 | this.activateOrbitControls(); 24 | } 25 | } 26 | 27 | private activateOrbitControls() { 28 | const controls = this.camera.controls; 29 | controls.minDistance = 1; 30 | controls.maxDistance = 300; 31 | const position = new THREE.Vector3(); 32 | controls.getPosition(position); 33 | const distance = position.length(); 34 | controls.distance = distance; 35 | controls.truckSpeed = 2; 36 | const { rotation } = this.camera.three; 37 | const direction = new THREE.Vector3(0, 0, -1).applyEuler(rotation); 38 | const target = position.addScaledVector(direction, distance); 39 | controls.moveTo(target.x, target.y, target.z); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/front/src/core/ClipStyler/src/types.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import * as OBC from "@thatopen/components"; 3 | import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js"; 4 | 5 | /** 6 | * Represents the style configuration for clipping edges, including materials for lines and fills. 7 | */ 8 | export interface ClipStyle { 9 | linesMaterial?: LineMaterial; 10 | fillsMaterial?: THREE.Material; 11 | } 12 | 13 | /** 14 | * Represents the style configuration for clip edges items. 15 | */ 16 | export interface ClipEdgesItemStyle { 17 | /** 18 | * The name of the style from the ClipStyler to apply. 19 | */ 20 | style: string; 21 | 22 | /** 23 | * Optional classifier intersection input. If not set, all items cut will be styled. 24 | */ 25 | data?: OBC.ClassifierIntersectionInput; 26 | } 27 | 28 | /** 29 | * Configuration for creating ClipEdges. 30 | */ 31 | export interface ClipEdgesCreationConfig { 32 | /** 33 | * If true, updates the ClipEdges based on the plane update. 34 | */ 35 | link?: boolean; 36 | 37 | /** 38 | * The unique name of the ClipEdges. 39 | */ 40 | id?: string; 41 | 42 | /** 43 | * The world in which the ClipEdges are going to be added. 44 | */ 45 | world?: OBC.World; 46 | 47 | /** 48 | * A record of groups from the classifier to style based on the style name set. 49 | */ 50 | items?: Record; 51 | } 52 | -------------------------------------------------------------------------------- /packages/core/src/core/Types/src/world.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import { BaseScene } from "./base-scene"; 3 | import { BaseCamera } from "./base-camera"; 4 | import { BaseRenderer } from "./base-renderer"; 5 | import { Updateable, Disposable } from "./interfaces"; 6 | import { Event } from "./event"; 7 | 8 | /** 9 | * Represents a 3D world with meshes, scene, camera, renderer, and other properties. 10 | */ 11 | export interface World extends Disposable, Updateable { 12 | /** 13 | * A set of meshes present in the world. This is taken into account for operations like raycasting. 14 | */ 15 | meshes: Set; 16 | 17 | /** 18 | * The base scene of the world. 19 | */ 20 | scene: BaseScene; 21 | 22 | /** 23 | * The default camera of the world. 24 | */ 25 | defaultCamera: BaseCamera; 26 | 27 | /** 28 | * The base camera of the world. 29 | */ 30 | camera: BaseCamera; 31 | 32 | onCameraChanged: Event; 33 | 34 | useDefaultCamera: () => void; 35 | 36 | /** 37 | * The base renderer of the world. Can be null if this world doesn't use a renderer (e.g. in a backend environment). 38 | */ 39 | renderer: BaseRenderer | null; 40 | 41 | /** 42 | * A unique identifier for the world. 43 | */ 44 | uuid: string; 45 | 46 | /** 47 | * Indicates whether the world is currently disposing. This is useful for cancelling logic that access the elements of a world (which are also disposed). 48 | */ 49 | isDisposing: boolean; 50 | } 51 | -------------------------------------------------------------------------------- /packages/core/src/openbim/BCFTopics/src/importers/extensions.ts: -------------------------------------------------------------------------------- 1 | import { BCFTopics } from "../.."; 2 | 3 | export const extensionsImporter = ( 4 | manager: BCFTopics, 5 | extensionsXML: string, 6 | ) => { 7 | if (extensionsXML.trim() === "") return; 8 | const extensions = BCFTopics.xmlParser.parse(extensionsXML).Extensions; 9 | if (!extensions) return; 10 | const { Priorities, TopicStatuses, TopicTypes, Users } = extensions; 11 | if (Priorities && Priorities.Priority) { 12 | const priorities = Array.isArray(Priorities.Priority) 13 | ? Priorities.Priority 14 | : [Priorities.Priority]; 15 | for (const priority of priorities) { 16 | manager.config.priorities.add(priority); 17 | } 18 | } 19 | if (TopicStatuses && TopicStatuses.TopicStatus) { 20 | const statuses = Array.isArray(TopicStatuses.TopicStatus) 21 | ? TopicStatuses.TopicStatus 22 | : [TopicStatuses.TopicStatus]; 23 | for (const status of statuses) { 24 | manager.config.statuses.add(status); 25 | } 26 | } 27 | if (TopicTypes && TopicTypes.TopicType) { 28 | const types = Array.isArray(TopicTypes.TopicType) 29 | ? TopicTypes.TopicType 30 | : [TopicTypes.TopicType]; 31 | for (const type of types) { 32 | manager.config.types.add(type); 33 | } 34 | } 35 | if (Users && Users.User) { 36 | const users = Array.isArray(Users.User) ? Users.User : [Users.User]; 37 | for (const user of users) { 38 | manager.config.users.add(user); 39 | } 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /packages/core/src/core/Types/src/base.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Disposable, 3 | Hideable, 4 | Resizeable, 5 | Updateable, 6 | Configurable, 7 | Serializable, 8 | } from "./interfaces"; 9 | import { Components } from "../../Components"; 10 | 11 | /** 12 | * Base class of the library. Useful for finding out the interfaces something implements. 13 | */ 14 | export abstract class Base { 15 | constructor(public components: Components) {} 16 | 17 | /** Whether is component is {@link Disposable}. */ 18 | isDisposeable = (): this is Disposable => { 19 | return "dispose" in this && "onDisposed" in this; 20 | }; 21 | 22 | /** Whether is component is {@link Resizeable}. */ 23 | isResizeable = (): this is Resizeable => { 24 | return "resize" in this && "getSize" in this; 25 | }; 26 | 27 | /** Whether is component is {@link Updateable}. */ 28 | isUpdateable = (): this is Updateable => { 29 | return ( 30 | "onAfterUpdate" in this && "onBeforeUpdate" in this && "update" in this 31 | ); 32 | }; 33 | 34 | /** Whether is component is {@link Hideable}. */ 35 | isHideable = (): this is Hideable => { 36 | return "visible" in this; 37 | }; 38 | 39 | /** Whether is component is {@link Configurable}. */ 40 | isConfigurable = (): this is Configurable => { 41 | return "setup" in this && "config" in this && "onSetup" in this; 42 | }; 43 | 44 | /** Whether is component is {@link Serializable}. */ 45 | isSerializable = (): this is Serializable => { 46 | return "import" in this && "export" in this 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/core/src/fragments/IfcLoader/src/ifc-fragment-settings.ts: -------------------------------------------------------------------------------- 1 | import * as WEBIFC from "web-ifc"; 2 | 3 | /** Configuration of the IFC-fragment conversion. */ 4 | export class IfcFragmentSettings { 5 | /** Path of the WASM for [web-ifc](https://github.com/ThatOpen/engine_web-ifc). */ 6 | wasm: { 7 | path: string; 8 | absolute: boolean; 9 | logLevel?: WEBIFC.LogLevel; 10 | } = { 11 | path: "", 12 | absolute: false, 13 | logLevel: WEBIFC.LogLevel.LOG_LEVEL_OFF, 14 | }; 15 | 16 | /** Loader settings for [web-ifc](https://github.com/ThatOpen/engine_web-ifc). */ 17 | webIfc: WEBIFC.LoaderSettings = { 18 | COORDINATE_TO_ORIGIN: true, 19 | // OPTIMIZE_PROFILES: true, 20 | }; 21 | 22 | /** 23 | * Whether to automatically set the path to the WASM file for [web-ifc](https://github.com/ThatOpen/engine_web-ifc). 24 | * If set to true, the path will be set to the default path of the WASM file. 25 | * If set to false, the path must be provided manually in the `wasm.path` property. 26 | * Default value is true. 27 | */ 28 | autoSetWasm = true; 29 | 30 | /** 31 | * Custom function to handle the file location for [web-ifc](https://github.com/ThatOpen/engine_web-ifc). 32 | * This function will be called when [web-ifc](https://github.com/ThatOpen/engine_web-ifc) needs to locate a file. 33 | * If set to null, the default file location handler will be used. 34 | * 35 | * @param url - The URL of the file to locate. 36 | * @returns The absolute path of the file. 37 | */ 38 | customLocateFileHandler: WEBIFC.LocateFileHandlerFn | null = null; 39 | } 40 | -------------------------------------------------------------------------------- /packages/core/src/core/Types/src/event.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple event handler by [Jason Kleban](https://gist.github.com/JasonKleban/50cee44960c225ac1993c922563aa540). Keep in mind that if you want to remove it later, you might want to declare the callback as an object. If you want to maintain the reference to `this`, you will need to declare the callback as an arrow function. 3 | */ 4 | export class Event { 5 | /** 6 | * Whether this event is active or not. If not, it won't trigger. 7 | */ 8 | enabled = true; 9 | 10 | /** 11 | * Add a callback to this event instance. 12 | * @param handler - the callback to be added to this event. 13 | */ 14 | add(handler: T extends void ? { (): void } : { (data: T): void }): void { 15 | this.handlers.push(handler); 16 | } 17 | 18 | /** 19 | * Removes a callback from this event instance. 20 | * @param handler - the callback to be removed from this event. 21 | */ 22 | remove(handler: T extends void ? { (): void } : { (data: T): void }): void { 23 | this.handlers = this.handlers.filter((h) => h !== handler); 24 | } 25 | 26 | /** Triggers all the callbacks assigned to this event. */ 27 | trigger = (data?: T) => { 28 | if (!this.enabled) { 29 | return; 30 | } 31 | const handlers = this.handlers.slice(0); 32 | for (const handler of handlers) { 33 | handler(data as any); 34 | } 35 | }; 36 | 37 | /** Gets rid of all the suscribed events. */ 38 | reset() { 39 | this.handlers.length = 0; 40 | } 41 | 42 | private handlers: (T extends void ? { (): void } : { (data: T): void })[] = 43 | []; 44 | } 45 | -------------------------------------------------------------------------------- /packages/front/src/fragments/Highlighter/src/types.ts: -------------------------------------------------------------------------------- 1 | import * as OBC from "@thatopen/components"; 2 | import * as THREE from "three"; 3 | import * as FRAGS from "@thatopen/fragments"; 4 | 5 | /** 6 | * Interface defining the events that the Highlighter class can trigger. Each highlighter has its own set of events, identified by the highlighter name. 7 | */ 8 | export interface HighlightEvents { 9 | [highlighterName: string]: { 10 | /** Event triggered before a fragment is highlighted, giving the last selection. */ 11 | onBeforeHighlight: OBC.Event; 12 | /** Event triggered when a fragment is highlighted. */ 13 | onHighlight: OBC.Event; 14 | /** Event triggered when a fragment is cleared. */ 15 | onClear: OBC.Event; 16 | }; 17 | } 18 | 19 | /** 20 | * Interface defining the configuration options for the Highlighter class. 21 | */ 22 | export interface HighlighterConfig { 23 | /** Name of the selection event. */ 24 | selectName: string; 25 | /** Toggles the select functionality. */ 26 | selectEnabled: boolean; 27 | /** 28 | * Color used for selection. 29 | * @deprecated use selectMaterialDefinition instead 30 | * */ 31 | selectionColor: THREE.Color | null; 32 | /** Whether to automatically highlight fragments on click. */ 33 | autoHighlightOnClick: boolean; 34 | /** The world in which the highlighter operates. */ 35 | world: OBC.World | null; 36 | selectMaterialDefinition: Omit | null; 37 | /** Whether to automatically update fragments when highlighting. */ 38 | autoUpdateFragments: boolean; 39 | } 40 | -------------------------------------------------------------------------------- /packages/core/src/core/OrthoPerspectiveCamera/src/first-person-mode.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import CameraControls from "camera-controls"; 3 | import { NavigationMode } from "./types"; 4 | import { OrthoPerspectiveCamera } from "../index"; 5 | 6 | /** 7 | * A {@link NavigationMode} that allows first person navigation, simulating FPS video games. 8 | */ 9 | export class FirstPersonMode implements NavigationMode { 10 | /** {@link NavigationMode.enabled} */ 11 | enabled = false; 12 | 13 | /** {@link NavigationMode.id} */ 14 | readonly id = "FirstPerson"; 15 | 16 | constructor(private camera: OrthoPerspectiveCamera) {} 17 | 18 | /** {@link NavigationMode.set} */ 19 | set(active: boolean) { 20 | this.enabled = active; 21 | if (active) { 22 | const projection = this.camera.projection.current; 23 | if (projection !== "Perspective") { 24 | this.camera.set("Orbit"); 25 | return; 26 | } 27 | this.setupFirstPersonCamera(); 28 | } 29 | } 30 | 31 | private setupFirstPersonCamera() { 32 | const controls = this.camera.controls; 33 | const newTargetPosition = new THREE.Vector3(); 34 | controls.distance--; 35 | controls.getPosition(newTargetPosition); 36 | controls.minDistance = 1; 37 | controls.maxDistance = 1; 38 | controls.distance = 1; 39 | 40 | controls.moveTo( 41 | newTargetPosition.x, 42 | newTargetPosition.y, 43 | newTargetPosition.z, 44 | ); 45 | 46 | controls.truckSpeed = 50; 47 | controls.mouseButtons.wheel = CameraControls.ACTION.DOLLY; 48 | controls.touches.two = CameraControls.ACTION.TOUCH_ZOOM_TRUCK; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /resources/specs.ids: -------------------------------------------------------------------------------- 1 | 2 | 3 | New IDS File 4 | 5 | 6 | 12 | 13 | 14 | 15 | IFCWALL 16 | 17 | 18 | PARTITIONING 19 | 20 | 21 | 22 | 23 | 24 | 25 | Name 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /packages/front/src/core/PlatformComponents/test.ts: -------------------------------------------------------------------------------- 1 | import * as OBC from "@thatopen/components"; 2 | import Stats from "stats.js"; 3 | import * as BUI from "@thatopen/ui"; 4 | import * as OBCF from "../.."; 5 | 6 | BUI.Manager.init(); 7 | 8 | const container = document.getElementById("container")!; 9 | 10 | const components = new OBC.Components(); 11 | 12 | const worlds = components.get(OBC.Worlds); 13 | 14 | const world = worlds.create< 15 | OBC.SimpleScene, 16 | OBC.SimpleCamera, 17 | OBC.SimpleRenderer 18 | >(); 19 | 20 | world.scene = new OBC.SimpleScene(components); 21 | world.renderer = new OBC.SimpleRenderer(components, container); 22 | world.camera = new OBC.SimpleCamera(components); 23 | 24 | components.init(); 25 | 26 | world.camera.controls.setLookAt(5, 5, 5, 0, 0, 0); 27 | 28 | container.appendChild(world.renderer.three.domElement); 29 | 30 | const grids = components.get(OBC.Grids); 31 | grids.create(world); 32 | 33 | world.scene.three.background = null; 34 | 35 | const stats = new Stats(); 36 | stats.showPanel(2); 37 | document.body.append(stats.dom); 38 | stats.dom.style.left = "0px"; 39 | stats.dom.style.zIndex = "unset"; 40 | world.renderer.onBeforeUpdate.add(() => stats.begin()); 41 | world.renderer.onAfterUpdate.add(() => stats.end()); 42 | 43 | (window as any).ThatOpenCompany = { OBC, BUI }; 44 | 45 | const cloudComponents = components.get(OBCF.PlatformComponents); 46 | 47 | async function importComponent(url: string) { 48 | const fetched = await fetch(url); 49 | const componentData = await fetched.text(); 50 | const test = await cloudComponents.import(componentData); 51 | console.log(test); 52 | } 53 | 54 | await importComponent("../../../../../resources/mock-cloud-component.js"); 55 | -------------------------------------------------------------------------------- /packages/core/src/core/Views/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Views 13 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | }, 6 | extends: ["airbnb-base", "prettier"], 7 | parser: "@typescript-eslint/parser", 8 | parserOptions: { 9 | ecmaVersion: 12, 10 | sourceType: "module", 11 | }, 12 | plugins: ["@typescript-eslint", "prettier"], 13 | ignorePatterns: ["**/dist/*", "**/node_modules/*", "**/*.json", "**/*.js"], 14 | rules: { 15 | "prettier/prettier": [ 16 | "error", 17 | { 18 | endOfLine: "auto", 19 | }, 20 | ], 21 | indent: "off", 22 | "no-shadow": "off", 23 | "lines-between-class-members": "off", 24 | "linebreak-style": "off", 25 | "arrow-body-style": "off", 26 | "prefer-destructuring": "off", 27 | "no-console": "off", 28 | "no-param-reassign": "off", 29 | "eol-last": "off", 30 | "no-unused-vars": "off", 31 | "class-methods-use-this": "off", 32 | "no-await-in-loop": "off", 33 | "no-return-assign": "off", 34 | "no-restricted-syntax": "off", 35 | "no-useless-constructor": "off", 36 | "no-empty-function": "off", 37 | "no-continue": "off", 38 | "no-underscore-dangle": "off", 39 | "guard-for-in": "off", 40 | "import/extensions": [ 41 | "error", 42 | "ignorePackages", 43 | { 44 | ts: "never", 45 | }, 46 | ], 47 | "import/prefer-default-export": "off", 48 | "no-plusplus": "off", 49 | }, 50 | settings: { 51 | "import/resolver": { 52 | node: { 53 | extensions: [".js", ".jsx", ".ts", ".tsx", ".d.ts"], 54 | }, 55 | }, 56 | }, 57 | overrides: [ 58 | { 59 | files: ["*.test.ts"], 60 | env: { 61 | jest: true, 62 | }, 63 | }, 64 | ], 65 | }; 66 | -------------------------------------------------------------------------------- /packages/core/src/core/Viewpoints/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Viewpoints 13 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /packages/front/src/fragments/Hoverer/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Hoverer 13 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /packages/core/src/openbim/BCFTopics/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | BCFTopics 13 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /packages/front/src/fragments/Outliner/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Outliner 13 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /packages/core/src/fragments/ItemsFinder/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Items Finder 13 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /packages/front/src/fragments/Highlighter/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Highlighter 13 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /packages/core/src/measurement/MeasurementUtils/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | MeasurementUtils 13 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /packages/core/src/openbim/IDSSpecifications/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | IDS Specifications 13 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /packages/core/src/core/Types/src/async-event.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple event handler by [Jason Kleban](https://gist.github.com/JasonKleban/50cee44960c225ac1993c922563aa540). Keep in mind that if you want to remove it later, you might want to declare the callback as an object. If you want to maintain the reference to `this`, you will need to declare the callback as an arrow function. 3 | */ 4 | export class AsyncEvent { 5 | /** 6 | * Whether this event is active or not. If not, it won't trigger. 7 | */ 8 | enabled = true; 9 | 10 | /** 11 | * Add a callback to this event instance. 12 | * @param handler - the callback to be added to this event. 13 | */ 14 | add( 15 | handler: T extends void 16 | ? { (): Promise } 17 | : { (data: T): Promise }, 18 | ): void { 19 | this.handlers.push(handler); 20 | } 21 | 22 | /** 23 | * Removes a callback from this event instance. 24 | * @param handler - the callback to be removed from this event. 25 | */ 26 | remove( 27 | handler: T extends void 28 | ? { (): Promise } 29 | : { (data: T): Promise }, 30 | ): void { 31 | this.handlers = this.handlers.filter((h) => h !== handler); 32 | } 33 | 34 | /** Triggers all the callbacks assigned to this event. */ 35 | trigger = async (data?: T) => { 36 | if (!this.enabled) { 37 | return; 38 | } 39 | const handlers = this.handlers.slice(0); 40 | for (const handler of handlers) { 41 | await handler(data as any); 42 | } 43 | }; 44 | 45 | /** Gets rid of all the suscribed events. */ 46 | reset() { 47 | this.handlers.length = 0; 48 | } 49 | 50 | private handlers: (T extends void 51 | ? { (): Promise } 52 | : { (data: T): Promise })[] = []; 53 | } 54 | -------------------------------------------------------------------------------- /packages/front/src/core/ClipStyler/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Clip Styler 13 | 61 | 62 | 63 | 64 |
65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /packages/front/src/fragments/Mesher/sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Mesher 13 | 61 | 62 | 63 | 64 |
65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /packages/front/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@thatopen/components-front", 3 | "description": "Collection of frontend tools to author BIM apps.", 4 | "version": "3.2.16", 5 | "author": "That Open Company", 6 | "contributors": [ 7 | "Antonio Gonzalez Viegas (https://github.com/agviegas)", 8 | "Juan Hoyos (https://github.com/HoyosJuan)", 9 | "Harry Collin (https://github.com/harrycollin)", 10 | "Pablo Aguilar (https://github.com/pabloaguilarv)" 11 | ], 12 | "license": "MIT", 13 | "homepage": "https://github.com/ThatOpen/engine_components/tree/main/packages/components-front#readme", 14 | "bugs": { 15 | "url": "https://github.com/ThatOpen/engine_components/issues" 16 | }, 17 | "type": "module", 18 | "main": "dist/index.js", 19 | "types": "dist/index.d.ts", 20 | "files": [ 21 | "dist", 22 | "README.md" 23 | ], 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/ThatOpen/engine_components/tree/main/packages/components-front.git" 27 | }, 28 | "scripts": { 29 | "dev": "vite --host", 30 | "test": "jest", 31 | "build": "tsc --p ./tsconfig-build.json && vite build", 32 | "prepublishOnly": "yarn build", 33 | "publish-repo": "npm publish" 34 | }, 35 | "peerDependencies": { 36 | "@thatopen/fragments": "~3.2.0", 37 | "three": ">=0.175.0", 38 | "web-ifc": ">=0.0.72" 39 | }, 40 | "devDependencies": { 41 | "@thatopen/fragments": "~3.2.0", 42 | "@thatopen/ui": "~3.2.0", 43 | "@thatopen/ui-obc": "~3.2.0", 44 | "@types/convert-units": "2.3.11", 45 | "@types/earcut": "3.0.0", 46 | "@types/three": "^0.175.0", 47 | "three": "^0.175.0", 48 | "web-ifc": "0.0.72" 49 | }, 50 | "dependencies": { 51 | "@thatopen/components": "~3.2.0", 52 | "earcut": "^3.0.1" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/front/src/measurement/AreaMeasurement/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Area Measurement 13 | 61 | 62 | 63 | 64 |
65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /packages/front/src/measurement/LengthMeasurement/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Length Measurement 13 | 61 | 62 | 63 | 64 |
65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /packages/front/src/measurement/VolumeMeasurement/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Volume Measurement 13 | 61 | 62 | 63 | 64 |
65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import * as fs from "fs"; 3 | import * as path from "path"; 4 | import { defineConfig } from "vite"; 5 | import { globSync } from "glob"; 6 | 7 | const writeIndexHTML = () => { 8 | let links: string = ""; 9 | const examplePaths = globSync("packages/**/src/**/example.html"); 10 | for (const examplePath of examplePaths) { 11 | const directory = path.dirname(examplePath); 12 | const packageNameMatch = directory.match(/packages\\([^\\]+)/); 13 | if (!(packageNameMatch && packageNameMatch.length > 1)) continue; 14 | const packageName = packageNameMatch[1]; 15 | const exampleName = path.basename(directory); 16 | links += `${packageName}/${exampleName}\n`; 17 | } 18 | const index = ` 19 | 20 | 21 | 22 | 23 | 24 | 25 | Examples Index 26 | 39 | 40 | 41 | 42 |
43 |

Choose an example

44 | ${links} 45 |
46 | 47 | 48 | 49 | `; 50 | fs.writeFileSync("./index.html", index); 51 | }; 52 | 53 | const createIndex = () => ({ 54 | name: "create-index", 55 | configureServer() { 56 | // fs.watch("./packages", { recursive: true }, writeIndexHTML); 57 | writeIndexHTML(); 58 | }, 59 | }); 60 | 61 | export default defineConfig({ 62 | plugins: [createIndex()], 63 | }); 64 | -------------------------------------------------------------------------------- /packages/core/src/core/Clipper/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Clipper 13 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /packages/core/src/core/Worlds/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Worlds 13 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /packages/core/src/fragments/Hider/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Hider 13 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /packages/front/src/core/Marker/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Marker 13 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /packages/core/src/core/OrthoPerspectiveCamera/src/plan-mode.ts: -------------------------------------------------------------------------------- 1 | import CameraControls from "camera-controls"; 2 | import { NavigationMode } from "./types"; 3 | import { OrthoPerspectiveCamera } from "../index"; 4 | 5 | /** 6 | * A {@link NavigationMode} that allows to navigate floorplans in 2D, like many BIM tools. 7 | */ 8 | export class PlanMode implements NavigationMode { 9 | /** {@link NavigationMode.enabled} */ 10 | enabled = false; 11 | 12 | /** {@link NavigationMode.id} */ 13 | readonly id = "Plan"; 14 | 15 | private mouseAction1?: any; 16 | private mouseAction2?: any; 17 | private mouseInitialized = false; 18 | 19 | private readonly defaultAzimuthSpeed: number; 20 | private readonly defaultPolarSpeed: number; 21 | 22 | constructor(private camera: OrthoPerspectiveCamera) { 23 | this.defaultAzimuthSpeed = camera.controls.azimuthRotateSpeed; 24 | this.defaultPolarSpeed = camera.controls.polarRotateSpeed; 25 | } 26 | 27 | /** {@link NavigationMode.set} */ 28 | set(active: boolean) { 29 | this.enabled = active; 30 | const controls = this.camera.controls; 31 | controls.azimuthRotateSpeed = active ? 0 : this.defaultAzimuthSpeed; 32 | controls.polarRotateSpeed = active ? 0 : this.defaultPolarSpeed; 33 | 34 | if (!this.mouseInitialized) { 35 | this.mouseAction1 = controls.touches.one; 36 | this.mouseAction2 = controls.touches.two; 37 | this.mouseInitialized = true; 38 | } 39 | 40 | if (active) { 41 | controls.mouseButtons.left = CameraControls.ACTION.TRUCK; 42 | controls.touches.one = CameraControls.ACTION.TOUCH_TRUCK; 43 | controls.touches.two = CameraControls.ACTION.TOUCH_ZOOM; 44 | } else { 45 | controls.mouseButtons.left = CameraControls.ACTION.ROTATE; 46 | controls.touches.one = this.mouseAction1!; 47 | controls.touches.two = this.mouseAction2!; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/core/src/core/Raycasters/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Raycasters 13 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /packages/core/src/fragments/Classifier/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Classifier 13 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /packages/core/src/fragments/IfcLoader/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | IfcLoader 13 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /packages/core/src/fragments/BoundingBoxer/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | BoundingBoxer 13 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /packages/front/src/core/PlatformComponents/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | PlatformComponents 13 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /examples/Views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Views 13 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /packages/core/src/fragments/FragmentsManager/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | FragmentsManager 13 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /packages/front/src/core/Marker/src/renderer-with-2d.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer.js"; 3 | import { Components, SimpleRenderer } from "@thatopen/components"; 4 | 5 | /** 6 | * A basic renderer capable of rendering 3D and 2D objects ([Objec3Ds](https://threejs.org/docs/#api/en/core/Object3D) and [CSS2DObjects](https://threejs.org/docs/#examples/en/renderers/CSS2DRenderer) respectively). 7 | */ 8 | export class RendererWith2D extends SimpleRenderer { 9 | /** 10 | * This renderer is used to render 2D objects (CSS2DObjects) in a 3D scene. 11 | */ 12 | three2D = new CSS2DRenderer(); 13 | 14 | constructor( 15 | components: Components, 16 | container: HTMLElement, 17 | parameters?: Partial, 18 | ) { 19 | super(components, container, parameters); 20 | 21 | this.onAfterUpdate.add(() => { 22 | this.onBeforeUpdate.trigger(this); 23 | if (!this.enabled || !this.currentWorld) return; 24 | const scene = this.currentWorld.scene.three; 25 | const camera = this.currentWorld.camera.three; 26 | if (scene instanceof THREE.Scene) { 27 | this.three2D.render(scene, camera); 28 | } 29 | }); 30 | 31 | this.onDisposed.add(() => { 32 | this.three2D.domElement.remove(); 33 | }); 34 | 35 | this.onResize.add(({ x, y }) => { 36 | this.three2D.setSize(x, y); 37 | }); 38 | 39 | this.setupHtmlRenderer(); 40 | this.resize(); 41 | } 42 | 43 | private setupHtmlRenderer() { 44 | this.three2D.domElement.style.position = "absolute"; 45 | this.three2D.domElement.style.top = "0px"; 46 | this.three2D.domElement.style.pointerEvents = "none"; 47 | if (this.container) { 48 | this.container.appendChild(this.three2D.domElement); 49 | this.container.style.position = "relative"; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/core/src/core/OrthoPerspectiveCamera/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | OrthoPerspectiveCamera 13 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /packages/front/src/core/PostproductionRenderer/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | PostproductionRenderer 13 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /examples/BCFTopics/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | BCFTopics 13 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /examples/IDSSpecifications/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | IDS Specifications 13 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /packages/core/src/core/ShadowedScene/src/pixel-reader.ts: -------------------------------------------------------------------------------- 1 | // Thanks to the advice here https://github.com/zalo/TetSim/commit/9696c2e1cd6354fb9bd40dbd299c58f4de0341dd 2 | 3 | function clientWaitAsync( 4 | gl: WebGL2RenderingContext, 5 | sync: WebGLSync, 6 | flags: any, 7 | intervalMilliseconds: number, 8 | ) { 9 | return new Promise((resolve, reject) => { 10 | function test() { 11 | const res = gl.clientWaitSync(sync, flags, 0); 12 | if (res === gl.WAIT_FAILED) { 13 | reject(); 14 | return; 15 | } 16 | if (res === gl.TIMEOUT_EXPIRED) { 17 | setTimeout(test, intervalMilliseconds); 18 | return; 19 | } 20 | resolve(); 21 | } 22 | 23 | test(); 24 | }); 25 | } 26 | 27 | async function getBufferSubDataAsync( 28 | gl: WebGL2RenderingContext, 29 | target: number, 30 | buffer: WebGLBuffer, 31 | srcByteOffset: number, 32 | dstBuffer: ArrayBufferView, 33 | dstOffset?: number, 34 | length?: number, 35 | ) { 36 | const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0)!; 37 | gl.flush(); 38 | 39 | await clientWaitAsync(gl, sync, 0, 10); 40 | gl.deleteSync(sync); 41 | gl.bindBuffer(target, buffer); 42 | gl.getBufferSubData(target, srcByteOffset, dstBuffer, dstOffset, length); 43 | gl.bindBuffer(target, null); 44 | } 45 | 46 | export async function readPixelsAsync( 47 | gl: WebGL2RenderingContext, 48 | x: number, 49 | y: number, 50 | w: number, 51 | h: number, 52 | format: any, 53 | type: any, 54 | dest: ArrayBufferView, 55 | ) { 56 | const buf = gl.createBuffer()!; 57 | gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buf); 58 | gl.bufferData(gl.PIXEL_PACK_BUFFER, dest.byteLength, gl.STREAM_READ); 59 | gl.readPixels(x, y, w, h, format, type, 0); 60 | gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); 61 | 62 | await getBufferSubDataAsync(gl, gl.PIXEL_PACK_BUFFER, buf, 0, dest); 63 | 64 | gl.deleteBuffer(buf); 65 | return dest; 66 | } 67 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@thatopen/components", 3 | "description": "Collection of core functionalities to author BIM apps.", 4 | "version": "3.2.6", 5 | "author": "That Open Company", 6 | "contributors": [ 7 | "Antonio Gonzalez Viegas (https://github.com/agviegas)", 8 | "Juan Hoyos (https://github.com/HoyosJuan)", 9 | "Harry Collin (https://github.com/harrycollin)", 10 | "Pablo Aguilar (https://github.com/pabloaguilarv" 11 | ], 12 | "license": "MIT", 13 | "homepage": "https://github.com/ThatOpen/engine_components/tree/main/packages/components#readme", 14 | "bugs": { 15 | "url": "https://github.com/ThatOpen/engine_components/issues" 16 | }, 17 | "type": "module", 18 | "main": "dist/index.cjs", 19 | "module": "dist/index.mjs", 20 | "types": "dist/index.d.ts", 21 | "files": [ 22 | "dist", 23 | "README.md" 24 | ], 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/ThatOpen/engine_components/tree/main/packages/components.git" 28 | }, 29 | "scripts": { 30 | "dev": "vite --host", 31 | "test": "jest", 32 | "build": "tsc --p ./tsconfig-build.json && vite build", 33 | "prepublishOnly": "node ./resources/updateComponentsVersion.mjs && yarn build", 34 | "publish-repo": "npm publish" 35 | }, 36 | "publishConfig": { 37 | "access": "public" 38 | }, 39 | "devDependencies": { 40 | "@thatopen/fragments": "~3.2.0", 41 | "@thatopen/ui": "~3.2.0", 42 | "@types/convert-units": "2.3.11", 43 | "@types/three": "0.175.0", 44 | "stats.js": "^0.17.0", 45 | "three": "^0.175.0", 46 | "web-ifc": "0.0.72" 47 | }, 48 | "dependencies": { 49 | "camera-controls": "^2.9.0", 50 | "fast-xml-parser": "4.4.1", 51 | "jszip": "3.10.1", 52 | "three-mesh-bvh": "0.7.0" 53 | }, 54 | "peerDependencies": { 55 | "@thatopen/fragments": "~3.2.0", 56 | "three": ">=0.175.0", 57 | "web-ifc": ">=0.0.72" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/Viewpoints/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Viewpoints 13 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | 68 | 69 | -------------------------------------------------------------------------------- /examples/ItemsFinder/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Items Finder 13 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | 68 | 69 | -------------------------------------------------------------------------------- /packages/core/src/core/Grids/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Grids 13 | 66 | 67 | 68 | 69 |
70 |
71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /examples/Hider/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Hider 13 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | 68 | 69 | -------------------------------------------------------------------------------- /examples/Worlds/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Worlds 13 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | 68 | 69 | -------------------------------------------------------------------------------- /examples/Clipper/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Clipper 13 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | 68 | 69 | -------------------------------------------------------------------------------- /packages/core/src/core/ShadowedScene/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | ShadowedScene 13 | 66 | 67 | 68 | 69 |
70 |
71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /examples/Raycasters/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Raycasters 13 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | 68 | 69 | -------------------------------------------------------------------------------- /packages/core/src/core/Types/src/base-scene.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import { Disposable } from "./interfaces"; 3 | import { Event } from "./event"; 4 | import { Components } from "../../Components"; 5 | import { Disposer } from "../../Disposer"; 6 | import { BaseWorldItem } from "./base-world-item"; 7 | 8 | /** 9 | * Abstract class representing a base scene in the application. All scenes should use this class as a base. 10 | */ 11 | export abstract class BaseScene extends BaseWorldItem implements Disposable { 12 | /** {@link Disposable.onDisposed} */ 13 | readonly onDisposed = new Event(); 14 | 15 | /** 16 | * Abstract property representing the three.js object associated with this scene. 17 | * It should be implemented by subclasses. 18 | */ 19 | abstract three: THREE.Object3D; 20 | 21 | /** The set of directional lights managed by this scene component. */ 22 | directionalLights = new Map(); 23 | 24 | /** The set of ambient lights managed by this scene component. */ 25 | ambientLights = new Map(); 26 | 27 | protected constructor(components: Components) { 28 | super(components); 29 | } 30 | 31 | /** {@link Disposable.dispose} */ 32 | dispose() { 33 | const disposer = this.components.get(Disposer); 34 | for (const child of this.three.children) { 35 | const mesh = child as THREE.Mesh; 36 | if (mesh.geometry) { 37 | disposer.destroy(mesh); 38 | } 39 | } 40 | 41 | this.deleteAllLights(); 42 | 43 | this.three.children = []; 44 | this.onDisposed.trigger(); 45 | this.onDisposed.reset(); 46 | } 47 | 48 | deleteAllLights() { 49 | for (const [, light] of this.directionalLights) { 50 | light.removeFromParent(); 51 | light.target.removeFromParent(); 52 | light.dispose(); 53 | } 54 | this.directionalLights.clear(); 55 | for (const [, light] of this.ambientLights) { 56 | light.removeFromParent(); 57 | light.dispose(); 58 | } 59 | this.ambientLights.clear(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /examples/BoundingBoxer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | BoundingBoxer 13 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | 68 | 69 | -------------------------------------------------------------------------------- /packages/front/src/utils/volume.ts: -------------------------------------------------------------------------------- 1 | import * as OBC from "@thatopen/components"; 2 | 3 | export class Volume { 4 | private _components: OBC.Components; 5 | 6 | id = OBC.UUID.create(); 7 | 8 | readonly onItemsChanged = new OBC.Event(); 9 | private _items: OBC.ModelIdMap = {}; 10 | 11 | set items(value: OBC.ModelIdMap) { 12 | this._items = value; 13 | this.onItemsChanged.trigger(); 14 | } 15 | 16 | get items() { 17 | return this._items; 18 | } 19 | 20 | private _units: "m3" | "cm3" | "mm3" | "km3" = "m3"; 21 | 22 | set units(value: "m3" | "cm3" | "mm3" | "km3") { 23 | this._units = value; 24 | } 25 | 26 | get units() { 27 | return this._units; 28 | } 29 | 30 | private _rounding = 2; 31 | 32 | set rounding(value: number) { 33 | this._rounding = value; 34 | } 35 | 36 | get rounding() { 37 | return this._rounding; 38 | } 39 | 40 | async getRawValue() { 41 | const utils = this._components.get(OBC.MeasurementUtils); 42 | const volume = await utils.getItemsVolume(this.items); 43 | return volume 44 | } 45 | 46 | async getValue() { 47 | const volume = await this.getRawValue() 48 | const convertedValue = OBC.MeasurementUtils.convertUnits( 49 | volume, 50 | "m3", 51 | this.units, 52 | this.rounding, 53 | ); 54 | return convertedValue; 55 | } 56 | 57 | async getCenter() { 58 | const boxer = this._components.get(OBC.BoundingBoxer); 59 | const center = await boxer.getCenter(this.items); 60 | return center; 61 | } 62 | 63 | constructor(components: OBC.Components) { 64 | this._components = components; 65 | } 66 | 67 | async getBox() { 68 | const boxer = this._components.get(OBC.BoundingBoxer); 69 | boxer.list.clear(); 70 | await boxer.addFromModelIdMap(this.items); 71 | const box = boxer.get(); 72 | boxer.list.clear(); 73 | return box; 74 | } 75 | 76 | clone() { 77 | const volume = new Volume(this._components); 78 | volume.items = OBC.ModelIdMapUtils.clone(this.items); 79 | return volume; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /examples/Highlighter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Highlighter 13 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | 69 | 70 | -------------------------------------------------------------------------------- /packages/core/src/openbim/IDSSpecifications/src/facets/PartOf.ts: -------------------------------------------------------------------------------- 1 | import { Components } from "../../../../core/Components"; 2 | import { IDSFacet } from "./Facet"; 3 | import { 4 | IDSFacetParameter, 5 | IDSPartOfRelations, 6 | IDSSimpleCardinality, 7 | } from "../types"; 8 | import { IDSEntity } from "./Entity"; 9 | import { ModelIdMap } from "../../../../fragments"; 10 | 11 | // https://github.com/buildingSMART/IDS/blob/development/Documentation/UserManual/partof-facet.md 12 | 13 | export class IDSPartOf extends IDSFacet { 14 | facetType = "PartOf" as const; 15 | 16 | private _entityFacet: IDSEntity; 17 | 18 | private _entity: { 19 | name: IDSFacetParameter; 20 | predefinedType?: IDSFacetParameter; 21 | }; 22 | 23 | set entity(value: { 24 | name: IDSFacetParameter; 25 | predefinedType?: IDSFacetParameter; 26 | }) { 27 | this._entity = value; 28 | const { name, predefinedType } = value; 29 | this._entityFacet = new IDSEntity(this._components, name); 30 | this._entityFacet.predefinedType = predefinedType; 31 | } 32 | 33 | get entity() { 34 | return this._entity; 35 | } 36 | 37 | relation?: IDSPartOfRelations; 38 | 39 | cardinality: IDSSimpleCardinality = "required"; 40 | 41 | constructor( 42 | components: Components, 43 | entity: { name: IDSFacetParameter; predefinedType?: IDSFacetParameter }, 44 | ) { 45 | super(components); 46 | this._entity = entity; 47 | this._entityFacet = new IDSEntity(components, entity.name); 48 | this._entityFacet.predefinedType = entity.predefinedType; 49 | } 50 | 51 | serialize() { 52 | return ""; 53 | } 54 | 55 | async getEntities(_modelIds: RegExp[], _collector: ModelIdMap) { 56 | // const fragments = this._components.get(FragmentsManager); 57 | // const items: ModelIdMap = {}; 58 | // await this._entityFacet.getEntities(modelIds, items); 59 | // for (const [modelId, localIds] of Object.entries(items)) { 60 | // const isValidModel = modelIds.find((regex) => regex.test(modelId)); 61 | // if (!isValidModel) continue; 62 | // } 63 | } 64 | 65 | async test(_items: ModelIdMap) {} 66 | } 67 | -------------------------------------------------------------------------------- /examples/IfcLoader/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | IfcLoader 13 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | 69 | 70 | -------------------------------------------------------------------------------- /examples/Classifier/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Classifier 13 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | 69 | 70 | -------------------------------------------------------------------------------- /examples/ClipStyler/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Clip Styler 13 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /examples/Marker/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Marker 13 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | 69 | 70 | -------------------------------------------------------------------------------- /examples/Hoverer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Hoverer 13 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
69 | 70 | 71 | -------------------------------------------------------------------------------- /examples/FragmentsManager/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | FragmentsManager 13 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | 69 | 70 | -------------------------------------------------------------------------------- /examples/assets/index-BOl1U6wY.js: -------------------------------------------------------------------------------- 1 | var A=Object.defineProperty;var F=(c,i,e)=>i in c?A(c,i,{enumerable:!0,configurable:!0,writable:!0,value:e}):c[i]=e;var h=(c,i,e)=>(F(c,typeof i!="symbol"?i+"":i,e),e);import{c as j,F as B,E}from"./graphic-vertex-picker-DjytwMNw.js";import{Y as p,d as Y,o as x,M as k}from"./index-b0R13blq.js";const y=class y extends j{constructor(){super(...arguments);h(this,"enabled",!0);h(this,"geometries",new p);h(this,"onDisposed",new E)}async get(e,t){const{material:s,applyTransformation:r}={applyTransformation:!0,...t},a=this.components.get(B),o=new p;for(const[n,T]of Object.entries(e)){const u=a.list.get(n);if(!u)continue;const G=this.getModelMeshes(n);for(const f of T){let l=o.get(n);l||(l=new p,o.set(n,l));let m=G.get(f);if(m&&m.length>0){const g=[];for(const[d,{geometry:M,transform:w}]of m.entries()){const b=await this.createMesh(u,M,w,{material:s,applyTransformation:r});g.push(b)}l.set(f,g);continue}else m=[],G.set(f,m);const[v]=await u.getItemsGeometry([f]);if(!v)continue;const I=[];for(const g of v){const d=this.createGeometry(g);if(!d)continue;const{geometry:M,transform:w}=d;m.push(d);const b=await this.createMesh(u,M,w,{material:s,applyTransformation:r});I.push(b)}l.set(f,I)}}return o}getModelMeshes(e){let t=this.geometries.get(e);return t||(t=new p,this.geometries.set(e,t)),t}remove(e=[...this.geometries.keys()]){for(const t of e){const s=this.geometries.get(t);if(s){for(const[r,a]of s)for(const{geometry:o}of a)o.dispose();this.geometries.delete(t)}}}dispose(e=!0){this.remove(),e&&this.geometries.dispose(),this.onDisposed.trigger(y.uuid)}getMeshesFromResult(e){const t=[];for(const s of e.values())for(const r of s.values())t.push(...r);return t}createGeometry(e){const{positions:t,indices:s,normals:r,transform:a}=e;if(!(t&&s&&r))return null;const o=new Y;return o.setAttribute("position",new x(t,3)),o.setAttribute("normal",new x(r,3,!0)),o.setIndex(new x(s,1)),{geometry:o,transform:a}}async createMesh(e,t,s,r){const{material:a,applyTransformation:o}={applyTransformation:!0,...r},n=new k(t,a);return n.applyMatrix4(s),n.applyMatrix4(e.object.matrixWorld),o||(n.position.set(0,0,0),n.rotation.set(0,0,0)),n}};h(y,"uuid","ab45d0a7-feea-4afc-927c-80832dae76dd");let D=y;export{D as M}; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Collection of tools to author BIM apps.", 3 | "author": "That Open Company", 4 | "contributors": [ 5 | "Antonio Gonzalez Viegas (https://github.com/agviegas)", 6 | "Juan Hoyos (https://github.com/HoyosJuan)", 7 | "Harry Collin (https://github.com/harrycollin)", 8 | "Pablo Aguilar (https://github.com/pabloaguilarv" 9 | ], 10 | "scripts": { 11 | "dev": "vite --host", 12 | "build-core": "yarn workspace @thatopen/components build", 13 | "build-front": "yarn workspace @thatopen/components-front build", 14 | "build-libraries": "yarn build-core && yarn build-front", 15 | "build-examples": "vite build --config ./vite.config-examples.ts", 16 | "build": "yarn copy-readme && yarn build-libraries && yarn build-examples", 17 | "copy-readme": "node resources/readme-copier.mjs", 18 | "test": "echo 'test to be implemented!'", 19 | "publish-repo": "yarn workspace @thatopen/components publish-repo && yarn workspace @thatopen/components-front publish-repo", 20 | "reset-release-please": "git commit --allow-empty -m \"chore: release 2.0.0\" -m \"Release-As: 2.0.0\"" 21 | }, 22 | "license": "MIT", 23 | "homepage": "https://github.com/ThatOpen/engine_components#readme", 24 | "bugs": { 25 | "url": "https://github.com/ThatOpen/engine_components/issues" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/ThatOpen/engine_components.git" 30 | }, 31 | "workspaces": [ 32 | "./packages/*" 33 | ], 34 | "devDependencies": { 35 | "@rollup/plugin-terser": "^0.4.4", 36 | "@types/node": "latest", 37 | "@typescript-eslint/eslint-plugin": "7.2.0", 38 | "@typescript-eslint/parser": "7.2.0", 39 | "eslint": "8.57.0", 40 | "eslint-config-airbnb-base": "15.0.0", 41 | "eslint-config-prettier": "9.1.0", 42 | "eslint-plugin-import": "2.29.1", 43 | "eslint-plugin-prettier": "5.1.3", 44 | "glob": "latest", 45 | "prettier": "3.2.5", 46 | "typescript": "5.4.2", 47 | "vite": "5.1.6", 48 | "vite-plugin-dts": "3.7.3" 49 | }, 50 | "version": "3.2.0", 51 | "dependencies": { 52 | "camera-controls": "^2.9.0" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /examples/OrthoPerspectiveCamera/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | OrthoPerspectiveCamera 13 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | 69 | 70 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: "New feature proposal 🚀" 2 | description: Propose a new feature or cool thing 3 | labels: [feature] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for your interest in the project and taking the time to fill out this feature report! 🙏 9 | - type: textarea 10 | id: feature-description 11 | attributes: 12 | label: Description 📝 13 | description: "Clear and concise description of the problem. Please make the reason and usecases as detailed as possible. If you intend to submit a PR for this issue, tell us in the description. Thanks!" 14 | placeholder: "Example: As a BIM developer/engineer using IFC.js I want [goal / wish] so that [benefit]." 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: suggested-solution 19 | attributes: 20 | label: Suggested solution 💡 21 | description: If possible, include in this field a solution or approach that would solve or implement the described feature. 22 | placeholder: "In module/component [xy] we could provide the following implementation..." 23 | validations: 24 | required: false 25 | - type: textarea 26 | id: alternative 27 | attributes: 28 | label: Alternative ⛕ 29 | description: Clear and concise description of any alternative solutions or features you've considered. 30 | validations: 31 | required: false 32 | - type: textarea 33 | id: additional-context 34 | attributes: 35 | label: Additional context ☝️ 36 | description: Any other context or screenshots about the feature request here. 37 | validations: 38 | required: false 39 | - type: checkboxes 40 | id: checkboxes 41 | attributes: 42 | label: Validations ✅ 43 | description: "Before submitting the issue, please make sure that you have:" 44 | options: 45 | - label: Read the [docs](https://docs.thatopen.com/intro). 46 | required: true 47 | - label: Check that there isn't [already an issue](https://github.com/ThatOpen/engine_components/issues) that requests the same feature to avoid creating a duplicate. 48 | required: true 49 | -------------------------------------------------------------------------------- /packages/front/src/civil/CivilNavigators/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Civil3DNavigator 13 | 67 | 68 | 69 | 70 |
71 |
72 |
73 |
74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /examples/assets/hoverer.js: -------------------------------------------------------------------------------- 1 | import{l as p,u,V as l,f as m,a as b,C as f}from"./index-b0R13blq.js";import{C as g,W as h,S as w,O as C,F as v}from"./graphic-vertex-picker-DjytwMNw.js";import{P as y}from"./index-BMGFic9y.js";import{H as P}from"./index-DB1oElq1.js";import"./index-BOl1U6wY.js";const n=new g,S=n.get(h),e=S.create();e.scene=new w(n);e.scene.setup();e.scene.three.background=null;const k=document.getElementById("container");e.renderer=new y(n,k);e.camera=new C(n);await e.camera.controls.setLookAt(68,23,-8.5,21.5,-5.5,23);n.init();const L="https://thatopen.github.io/engine_fragment/resources/worker.mjs",r=n.get(v);r.init(L);e.camera.controls.addEventListener("rest",()=>r.core.update(!0));e.onCameraChanged.add(t=>{for(const[,a]of r.list)a.useCamera(t.three);r.core.update(!0)});r.list.onItemSet.add(({value:t})=>{t.useCamera(e.camera.three),e.scene.three.add(t.object),r.core.update(!0)});const B=["https://thatopen.github.io/engine_components/resources/frags/school_arq.frag"];await Promise.all(B.map(async t=>{var c;const a=(c=t.split("/").pop())==null?void 0:c.split(".").shift();if(!a)return null;const d=await(await fetch(t)).arrayBuffer();return r.core.load(d,{modelId:a})}));const o=n.get(P);o.world=e;o.enabled=!0;o.material=new p({color:6629591,transparent:!0,opacity:.5,depthTest:!1});u.init();const i=l.create(()=>{const t=({target:a})=>{"color"in o.material&&o.material.color instanceof f&&o.material.color.set(a.color)};return m` 2 | 3 | 4 | 5 | 6 | 7 | `});document.body.append(i);const H=l.create(()=>m` 8 | 10 | 11 | `);document.body.append(H);const s=new b;s.showPanel(2);document.body.append(s.dom);s.dom.style.left="0px";s.dom.style.zIndex="unset";e.renderer.onBeforeUpdate.add(()=>s.begin());e.renderer.onAfterUpdate.add(()=>s.end()); 12 | -------------------------------------------------------------------------------- /examples/AreaMeasurement/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Area Measurement 13 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
71 | 72 | 73 | -------------------------------------------------------------------------------- /examples/LengthMeasurement/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Length Measurement 13 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
71 | 72 | 73 | -------------------------------------------------------------------------------- /examples/Outliner/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Outliner 13 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /packages/core/src/openbim/BCFTopics/src/types.ts: -------------------------------------------------------------------------------- 1 | import { BCFViewpoint } from "../../../core/Viewpoints"; 2 | 3 | export type BCFVersion = "2.1" | "3"; 4 | 5 | export interface BCFTopic { 6 | guid: string; 7 | serverAssignedId?: string; 8 | type: string; 9 | status: string; 10 | title: string; 11 | priority?: string; 12 | index?: number; 13 | labels: Set; 14 | creationDate: Date; 15 | creationAuthor: string; 16 | modifiedDate?: Date; 17 | modifiedAuthor?: string; 18 | dueDate?: Date; 19 | assignedTo?: string; 20 | description?: string; 21 | stage?: string; 22 | } 23 | 24 | export interface DocumentReference { 25 | type: "internal" | "external"; 26 | description?: string; 27 | } 28 | 29 | export interface InternalDocumentReference extends DocumentReference { 30 | type: "internal"; 31 | fileName: string; 32 | data: Uint8Array; 33 | } 34 | 35 | export interface ExternalDocumentReference extends DocumentReference { 36 | type: "external"; 37 | url: string; 38 | } 39 | 40 | export interface BCFApiComment { 41 | guid: string; 42 | date: string; 43 | author: string; 44 | comment: string; 45 | topic_guid?: string; // this is required in the BCF API specification 46 | viewpoint_guid?: string; 47 | modified_date?: string; 48 | modified_author?: string; 49 | } 50 | 51 | export interface BCFApiTopic { 52 | guid: string; 53 | server_assigned_id?: string; 54 | topic_type?: string; 55 | topic_status?: string; 56 | reference_links?: string[]; 57 | title: string; 58 | priority?: string; 59 | index?: number; 60 | labels?: string[]; 61 | creation_date: string; 62 | creation_author: string; 63 | modified_date?: string; 64 | modified_author?: string; 65 | assigned_to?: string; 66 | stage?: string; 67 | description?: string; 68 | bim_snippet?: { 69 | snippet_type: string; 70 | is_external: boolean; 71 | reference: string; 72 | reference_schema: string; 73 | }; 74 | due_date?: string; 75 | comments?: BCFApiComment[]; 76 | viewpoints?: BCFViewpoint[]; 77 | relatedTopics?: { related_topic_guid: string }[]; 78 | document_references?: { 79 | guid: string; 80 | document_guid?: string; 81 | url?: string; 82 | description?: string; 83 | }[]; 84 | } 85 | -------------------------------------------------------------------------------- /packages/core/src/openbim/BCFTopics/src/Comment.ts: -------------------------------------------------------------------------------- 1 | import { BCFApiComment, BCFTopics, Topic } from ".."; 2 | import { Components } from "../../../core/Components"; 3 | import { UUID } from "../../../utils"; 4 | 5 | /** 6 | * Represents a comment in a BCF Topic. 7 | */ 8 | export class Comment { 9 | date = new Date(); 10 | author: string; 11 | guid = UUID.create(); 12 | viewpoint?: string; 13 | modifiedAuthor?: string; 14 | modifiedDate?: Date; 15 | topic?: Topic; 16 | 17 | private _components: Components; 18 | private _comment: string = ""; 19 | 20 | /** 21 | * Sets the comment text and updates the modified date and author. 22 | * The author will be the one defined in BCFTopics.config.author 23 | * @param value - The new comment text. 24 | */ 25 | set comment(value: string) { 26 | const manager = this._components.get(BCFTopics); 27 | this._comment = value; 28 | this.modifiedDate = new Date(); 29 | this.modifiedAuthor = manager.config.author; 30 | this.topic?.comments.set(this.guid, this); 31 | } 32 | 33 | /** 34 | * Gets the comment text. 35 | * @returns The comment text. 36 | */ 37 | get comment() { 38 | return this._comment; 39 | } 40 | 41 | /** 42 | * Constructs a new BCF Topic Comment instance. 43 | * @param components - The Components instance. 44 | * @param text - The initial comment text. 45 | */ 46 | constructor(components: Components, text: string) { 47 | this._components = components; 48 | this._comment = text; // Set the comment to the private property to prevent setting a modifiedDate and author 49 | const manager = this._components.get(BCFTopics); 50 | this.author = manager.config.author; 51 | } 52 | 53 | toJSON() { 54 | const result: BCFApiComment = { 55 | guid: this.guid, 56 | date: this.date.toISOString(), 57 | author: this.author, 58 | comment: this.comment, 59 | topic_guid: this.topic?.guid, 60 | viewpoint_guid: this.viewpoint, 61 | modified_date: this.modifiedDate?.toISOString(), 62 | modified_author: this.modifiedAuthor, 63 | }; 64 | 65 | for (const [key, value] of Object.entries(result)) { 66 | if (value === undefined) delete (result as any)[key]; 67 | } 68 | 69 | return result; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/Grids/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Grids 13 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
74 |
75 | 76 | 77 | -------------------------------------------------------------------------------- /packages/front/src/core/PlatformComponents/index.ts: -------------------------------------------------------------------------------- 1 | import * as OBC from "@thatopen/components"; 2 | 3 | export class PlatformComponents extends OBC.Component { 4 | /** 5 | * A unique identifier for the component. 6 | * This UUID is used to register the component within the Components system. 7 | */ 8 | static readonly uuid = "74c0c370-1af8-4ca9-900a-4a4196c0f2f5" as const; 9 | 10 | enabled = true; 11 | 12 | inputs = ["OBC", "BUI"]; 13 | 14 | private readonly _requestEventID = "thatOpenCompanyComponentRequested"; 15 | 16 | private readonly _createEventID = "thatOpenCompanyComponentCreated"; 17 | 18 | constructor(components: OBC.Components) { 19 | super(components); 20 | components.add(PlatformComponents.uuid, this); 21 | } 22 | 23 | async import(componentSource: string) { 24 | return new Promise((resolve) => { 25 | const script = document.createElement("script"); 26 | 27 | const src = ` 28 | function main() { 29 | const { ${this.inputs} } = window.ThatOpenCompany; 30 | 31 | ${componentSource} 32 | 33 | const onComponentRequested = () => { 34 | window.removeEventListener("${this._requestEventID}", onComponentRequested); 35 | const event = new CustomEvent("${this._createEventID}", { detail: main }); 36 | window.dispatchEvent(event); 37 | }; 38 | 39 | window.addEventListener("${this._requestEventID}", onComponentRequested); 40 | } 41 | 42 | main(); 43 | `; 44 | 45 | const onCreated = (event: any) => { 46 | window.removeEventListener(this._createEventID, onCreated); 47 | 48 | const ComponentClass = event.detail as new ( 49 | components: OBC.Components, 50 | ) => OBC.Component; 51 | 52 | const component = this.components.get(ComponentClass); 53 | script.remove(); 54 | resolve(component); 55 | }; 56 | 57 | script.addEventListener("load", () => { 58 | window.addEventListener(this._createEventID, onCreated); 59 | window.dispatchEvent(new Event(this._requestEventID)); 60 | }); 61 | 62 | script.src = URL.createObjectURL(new File([src], "temp.js")); 63 | document.head.appendChild(script); 64 | }); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /examples/VolumeMeasurement/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | Volume Measurement 13 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /examples/ShadowedScene/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | ShadowedScene 13 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
74 |
75 | 76 | 77 | -------------------------------------------------------------------------------- /vite.config-examples.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import { defineConfig } from "vite"; 3 | import * as path from "path"; 4 | import * as fs from "fs"; 5 | import { globSync } from "glob"; 6 | 7 | const restructureExamples = () => { 8 | return { 9 | name: "examples-refactor", 10 | async writeBundle() { 11 | const outDir = "examples/packages"; 12 | const files = globSync(`${outDir}/**/example.html`); 13 | const paths: string[] = []; 14 | 15 | for (const file of files) { 16 | const urlPath = file 17 | .split("examples")[1] 18 | .slice(1) 19 | .replace(".html", ".ts") 20 | .replace(/\\/g, "/"); 21 | paths.push(urlPath); 22 | const directory = path.dirname(file); 23 | const exampleName = path.basename(directory); 24 | const rootFolder = directory.split(path.sep)[0]; 25 | const targetDirectory = path.join(rootFolder, exampleName); 26 | if (!fs.existsSync(targetDirectory)) fs.mkdirSync(targetDirectory); 27 | 28 | const buffer = fs.readFileSync(file); 29 | const newBuffer = buffer 30 | .toString() 31 | .replace(/(\.\.\/)+assets/g, "../assets") 32 | .replace(/(\.\.\/)+resources/g, "../../resources"); 33 | fs.writeFileSync(path.join(targetDirectory, "index.html"), newBuffer); 34 | } 35 | 36 | if (fs.existsSync(outDir)) fs.rmSync(outDir, { recursive: true }); 37 | fs.writeFileSync( 38 | path.join("examples", "paths.json"), 39 | JSON.stringify(paths), 40 | ); 41 | }, 42 | }; 43 | }; 44 | 45 | const entries = globSync("packages/**/src/**/example.html").map((file) => { 46 | const directory = path.dirname(file); 47 | const exampleName = path.basename(directory); 48 | const fixedName = exampleName[0].toLowerCase() + exampleName.slice(1); 49 | const entry = [fixedName, path.resolve(file)]; 50 | return entry; 51 | }); 52 | 53 | const input = Object.fromEntries(entries); 54 | 55 | export default defineConfig({ 56 | base: "./", 57 | esbuild: { 58 | supported: { 59 | "top-level-await": true, 60 | }, 61 | }, 62 | build: { 63 | outDir: "./examples", 64 | rollupOptions: { 65 | input, 66 | output: { 67 | entryFileNames: "assets/[name].js", 68 | }, 69 | }, 70 | }, 71 | plugins: [restructureExamples()], 72 | }); 73 | -------------------------------------------------------------------------------- /packages/core/src/core/Views/src/types.ts: -------------------------------------------------------------------------------- 1 | import { World } from "../../Types"; 2 | 3 | /** 4 | * Configuration options for creating views from a plane. 5 | */ 6 | export interface CreateViewConfig { 7 | /** 8 | * Optional identifier for the view. If not provided, a random ID will be generated. 9 | */ 10 | id?: string; 11 | 12 | /** 13 | * Optional world instance to use when creating the view. If not set, the component's default world will be used. 14 | */ 15 | world?: World; 16 | } 17 | 18 | /** 19 | * Configuration options for creating a view from IFC storeys. 20 | */ 21 | export interface CreateViewFromIfcStoreysConfig { 22 | /** 23 | * An optional array of regular expressions to match model IDs. If not provided, all models will be used. 24 | */ 25 | modelIds?: RegExp[]; 26 | 27 | /** 28 | * An optional array of regular expressions to match storey names. If not provided, all storeys will be used. 29 | */ 30 | storeyNames?: RegExp[]; 31 | 32 | /** 33 | * An optional offset value to displace the storey plane upward. Defaults to `0.25`. 34 | */ 35 | offset?: number; 36 | 37 | /** 38 | * Optional world instance to use when creating the view. If not set, the component's default world will be used. 39 | */ 40 | world?: World; 41 | } 42 | 43 | /** 44 | * Configuration options for creating views from bounding boxes. 45 | */ 46 | export interface CreateElevationViewsConfig { 47 | /** 48 | * Determines whether to combine all models into a single bounding box. If `true`, a single bounding box will be created from all models. If `false`, each model will be treated separately. Defaults to `false`. 49 | */ 50 | combine?: boolean; 51 | 52 | /** 53 | * An optional array of regular expressions to match model IDs. If not provided, all models will be used. 54 | */ 55 | modelIds?: RegExp[]; 56 | 57 | /** 58 | * Optional world instance to use when creating the view. If not set, the component's default world will be used. 59 | */ 60 | world?: World; 61 | 62 | /** 63 | * A callback function to generate names for the views based on the model ID. Defaults to a function that generates names in the format: `: Front`, `: Back`, `: Left`, `: Right`. 64 | */ 65 | namingCallback?: (modelId: string) => { 66 | front: string; 67 | back: string; 68 | left: string; 69 | right: string; 70 | }; 71 | } 72 | -------------------------------------------------------------------------------- /examples/PostproductionRenderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | PostproductionRenderer 13 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
71 | 72 | 73 | -------------------------------------------------------------------------------- /examples/assets/worlds.js: -------------------------------------------------------------------------------- 1 | import{u,V as l,C as d,f as c,a as p}from"./index-b0R13blq.js";import{C as b,W as g,S as f,a as h,f as w,F as y}from"./index-CEn02O1f.js";const v=document.getElementById("container"),o=new b,C=o.get(g),e=C.create();e.scene=new f(o);e.renderer=new h(o,v);e.camera=new w(o);o.init();e.scene.setup();e.scene.three.background=null;const k="https://thatopen.github.io/engine_fragment/resources/worker.mjs",t=o.get(y);t.init(k);e.camera.controls.addEventListener("rest",()=>t.core.update(!0));t.list.onItemSet.add(({value:n})=>{n.useCamera(e.camera.three),e.scene.three.add(n.object),t.core.update(!0)});const L=["https://thatopen.github.io/engine_components/resources/frags/school_arq.frag"];await Promise.all(L.map(async n=>{var r;const i=(r=n.split("/").pop())==null?void 0:r.split(".").shift();if(!i)return null;const m=await(await fetch(n)).arrayBuffer();return t.core.load(m,{modelId:i})}));await e.camera.controls.setLookAt(68,23,-8.5,21.5,-5.5,23);await t.core.update(!0);u.init();const s=l.create(()=>c` 2 | 3 | 4 | 5 | 8 | 9 | 10 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | `);document.body.append(s);const S=l.create(()=>c` 23 | 25 | 26 | `);document.body.append(S);const a=new p;a.showPanel(2);document.body.append(a.dom);a.dom.style.left="0px";a.dom.style.zIndex="unset";e.renderer.onBeforeUpdate.add(()=>a.begin());e.renderer.onAfterUpdate.add(()=>a.end()); 27 | --------------------------------------------------------------------------------