Open BIM Components

repository·main·Indexed 20 days ago

https://github.com/thatopen/engine_components

A high-level toolkit for building browser-based 3D BIM applications using Three.js. It provides reusable components for complex BIM tasks such as navigation, dimensioning, DXF export, and geometric clipping. The library is distributed via @thatopen/components for environment-agnostic core functionality and @thatopen/components-front for browser-specific features.

Tokens
32.6K
Snippets
110
Records
148
Agent score
70%

What's inside thatopen-engine_components

  1. Overview of Open BIM Components

    main

    Open BIM Components is a collection of BIM (Building Information Modeling) tools built on top of Three.js. It provides pre-made features for building browser-based 3D BIM applications, including capabilities for postproduction, dimensions, floorplan navigation, and DXF export.

    To use this library effectively, developers should be familiar with the Three.js API.

  2. Quickstart: Create a 3D scene with Open BIM Components

    main

    To initialize a basic 3D scene, you need to instantiate the OBC.Components manager, create a World via the Worlds component, and then define the scene, renderer, and camera. Finally, call components.init() to activate the system.

    This example demonstrates creating a navigable 3D scene containing a single cube.

    import * as THREE from "three";
    import * as OBC from "@thatopen/components"; // Note: Import path depends on your setup
    
    const container = document.getElementById("container")!;
    
    // 1. Initialize the main components manager
    const components = new OBC.Components();
    
    // 2. Access the Worlds component to manage 3D environments
    const worlds = components.get(OBC.Worlds);
    
    // 3. Create a new world with specific implementations for Scene, Camera, and Renderer
    const world = worlds.create<
      OBC.SimpleScene,
      OBC.SimpleCamera,
      OBC.SimpleRenderer
    >();
    
    // 4. Assign the implementations to the world
    world.scene = new OBC.SimpleScene(components);
    world.renderer = new OBC.SimpleRenderer(components, container);
    world.camera = new OBC.SimpleCamera(components);
    
    // 5. Initialize the components system
    components.init();
    
    // 6. Add content to the scene using standard Three.js objects
    const material = new THREE.MeshLambertMaterial({ color: "#6528D7" });
    const geometry = new THREE.BoxGeometry();
    const cube = new THREE.Mesh(geometry, material);
    world.scene.three.add(cube);
    
    // 7. Finalize scene setup
    world.scene.setup();
    
    // 8. Configure camera view
    world.camera.controls.setLookAt(3, 3, 3, 0, 0, 0);
  3. Use ShadowedScene for efficient cast shadows

    main

    ShadowedScene is a specialized scene component that provides built-in support for efficient directional shadows. It automatically manages directional lights and adjusts their shadow frustums based on the camera's distance and direction to ensure shadows cover the visible area effectively.

    To use it, you must assign a World to the scene before calling setup(). The component uses a DistanceRenderer internally to track the farthest visible distance and recompute shadow parameters accordingly.

    // Example setup (conceptual)
    const shadowedScene = new ShadowedScene(components);
    shadowedScene.currentWorld = myWorld;
    shadowedScene.setup({
      shadows: {
        cascade: 1,
        resolution: 1024
      }
    });
    await shadowedScene.updateShadows();
  4. How SnapResolver priority and results work

    main

    When calling resolve with multiple SnapClass values, the resolver follows a specific priority order to determine the 'winner':

    1. SnapClass.POINT (Highest priority)
    2. SnapClass.LINE
    3. SnapClass.FACE (Lowest priority)

    The first class that finds a candidate within maxDistance wins.

    Important Note on FACE results: Even if a higher-priority class (like POINT) wins, the resolver always computes the FACE candidate and attaches its metadata (like normal, facePoints, and faceIndices) to the result. This ensures that tools requiring surface information (like area measurements) always have access to it, even when the cursor is snapped to a vertex or edge.

  5. Use the Classifier component to group items

    main

    The Classifier component allows you to group items from different models based on specific criteria (classifications and groups). It uses an internal DataMap to organize these groups and provides methods to aggregate items via queries, relations, or specific IFC structures like building storeys.

    Key workflows include:

    • Aggregating by Query: Use aggregateItems to find items matching a query and register them into a classification group using a callback.
    • Aggregating by Relations: Use aggregateItemRelations to group items based on their relationships (e.g., ContainsElements).
    • Finding Intersections: Use find to retrieve the intersection of items that belong to multiple classification groups across different classifications.
    • IFC Specifics: Use byIfcBuildingStorey to automatically classify items by their parent building storey.
    // Example: Aggregating items by a query
    await classifier.aggregateItems(
      "MyClassification",
      { categories: [/Wall/] },
      {
        aggregationCallback: (item, register) => {
          // 'register' associates the item's name with its local ID
          register(item.Name.value, item._localId.value);
        }
      }
    );
    
    // Example: Finding items that satisfy multiple classifications
    const intersection = await classifier.find({
      "ClassificationA": ["Group1"],
      "ClassificationB": ["Group2"]
    });
  6. Understand the LinearAnnotationState machine

    main

    The linear annotation tool operates as a state machine to guide the user through the measurement process. The states are:

    1. awaitingFirstPoint: The tool is active but no interaction has started.
    2. placingPoints: The first point has been placed. The tool tracks the direction of measured lines to ensure subsequent clicks are on parallel lines and constrains the cursor preview to the orthogonal direction.
      • In individual mode, the tool auto-advances after the second click.
      • In sequential mode, the user accumulates points and must send a CONFIRM event.
    3. positioningOffset: All measurement points are set. The user drags to define the perpendicular offset of the dimension line.
    4. committed: One or more annotations have been finalized.
  7. How callout annotation state transitions work

    main

    The CalloutAnnotations system uses a finite state machine (calloutAnnotationMachine) to guide the user through the creation process. The machineState property indicates the current step in the workflow.

    Common states include:

    • awaitingCenter: The initial state, waiting for the user to pick the center point of the callout.
    • awaitingRadius: Waiting for the user to define the radius/distance.
    • awaitingElbow: Waiting for the user to define the elbow point (the bend in the line).
    • awaitingExtension: Waiting for the user to define the final extension point.
    • committed: The final state where the annotation is persisted to the drawing.

    When sendMachineEvent is called, the system transitions to the next state and triggers _updatePreview() to render the current progress in the TechnicalDrawing.

  8. Use OrthoPerspectiveCamera for flexible 2D/3D navigation

    main

    The OrthoPerspectiveCamera is a high-level camera component that extends SimpleCamera. It provides seamless switching between orthographic and perspective projections and supports multiple navigation modes like Orbit, First Person, and Plan (2D floor plan) navigation. It uses camera-controls internally to manage user interactions.

    Key Features

    • Projection Switching: Managed via the projection property (a ProjectionManager).
    • Navigation Modes: Switch between predefined modes like Orbit, FirstPerson, and Plan using the .set(modeId) method.
    • Auto-fitting: Use the .fit(meshes) method to automatically adjust the camera view to encompass specific meshes or the entire scene.
    • User Input Control: Enable or disable user interaction via .setUserInput(active).
    // Example setup (assuming components is initialized)
    const camera = new OrthoPerspectiveCamera(components);
    
    // Switch to First Person mode
    camera.set("FirstPerson");
    
    // Switch to Orbit mode
    camera.set("Orbit");
    
    // Fit the camera to specific meshes
    await camera.fit(myMeshesArray);
    
    // Disable user interaction
    camera.setUserInput(false);
  9. Manage 2D sections with the Views component

    main

    The Views class manages a collection of 2D sections (views) within a 3D environment. It allows you to create views based on vectors, planes, IFC storeys, or bounding box elevations. The component handles camera state management, ensuring that when you open a view, the world's camera switches to the view's camera, and when you close it, the camera can automatically restore its previous pose.

    Key features:

    • Automatic Camera Restoration: If restoreCameraOnClose is true, the component snapshots the active camera's controls state before opening a view and restores it when the view is closed.
    • Input Routing: It automatically manages CameraControls.enabled to ensure only the active view's camera responds to user input, preventing navigation in one view from affecting others.
    • View Lifecycle: Views are stored in a DataMap called list. Opening a view in a specific World will automatically close any other currently open view in that same world.
    // Example of creating and opening a view
    const views = components.get(Views);
    
    // Create a view at a specific point with a specific normal
    const myView = views.create(
      new THREE.Vector3(0, 1, 0), // normal
      new THREE.Vector3(0, 0, 0)  // point
    );
    
    // Open the view
    views.open(myView.id);
    
    // Later, close the view to return to the default world camera
    views.close(myView.id);
  10. AngleAnnotationState machine lifecycle

    main

    The angle annotation creation process is managed by a state machine with the following states:

    1. awaitingFirstLine: The initial state, waiting for the user to click the first line.
    2. awaitingSecondLine: The first line has been selected. The machine holds the line1 and the pointA while waiting for the second line.
    3. positioningArc: Both lines are selected and the vertex is computed. The user moves the cursor to define the arcRadius. The state tracks the cursor position and whether the angle is flipped.
    4. committed: The annotation has been finalized and contains the resulting dimension (AngleAnnotation).
  11. Understand Classification Group Data

    main

    A ClassificationGroupData object represents the contents of a classification group. It can contain both static items and dynamic items:

    • map: A ModelIdMap representing the static collection of items.
    • query: An optional ClassificationGroupQuery used to find items dynamically via the ItemsFinder.
    • get(): An asynchronous method that returns a Promise<ModelIdMap> containing the combined set of both static and dynamically discovered items.
    // Accessing the combined items in a group
    const items = await classificationGroupData.get();
  12. Understand CalloutAnnotation data and state

    main

    Callout annotations are managed through a state machine and persisted as CalloutAnnotation objects.

    Data Structures

    • CalloutAnnotation: The committed data for an annotation, including uuid, center, halfW, halfH, elbow (the bend point), extensionEnd (the text anchor), text, and the style name.
    • CalloutAnnotationData: An editable version of the annotation (all fields except uuid).

    State Machine (CalloutAnnotationState)

    The annotation creation follows a specific lifecycle:

    1. awaitingCenter: Waiting for the user to click the enclosure centre.
    2. awaitingRadius: User defines halfW and halfH by moving the cursor.
    3. awaitingElbow: User clicks to place the bend in the extension line.
    4. awaitingExtension: User clicks to place the extension endpoint.
    5. enteringText: Geometry is set; the system waits for a SUBMIT_TEXT event.
    6. committed: The annotation is finalized.