← TeiaViewer examples
Viewport

Interaction filters

Restrict which objects the user may select, pick or move with the viewport's InteractionController.

InteractionFilters.ts
import {
  type PointerIntersections, type SceneObject, type SceneObjectFilter, Viewer, ViewportOperator,
} from '@stereograph/teiaviewer';

/**
 * The viewport's `InteractionController` tells the operators which objects the user may select,
 * pick (raycast) and move with the transform tool: an object is kept when it allows it itself
 * (`isSelectable`, `isRaycastable`, `isTransformable`) and every filter added returns true.
 */
const viewer = new Viewer();
const { interactionController } = viewer.viewport;

// The objects of the loaded model only are selectable (not the measures, zones…).
const onlyLoadedObjects: SceneObjectFilter = (object) => {
  return object.controller === viewer.scene.bimMeshController;
};

// The hidden storeys are seen through: neither picked nor selected.
const hiddenStoreys = new Set<string>(['Storey-2']);
const notInHiddenStorey: SceneObjectFilter = (object) => !hiddenStoreys.has(object.uuid);

// Nothing moves with the transform tool while the model is locked.
let isModelLocked = true;
const unlessLocked: SceneObjectFilter = () => !isModelLocked;

interactionController.addSelectionFilter(onlyLoadedObjects);
interactionController.addRaycastFilter(notInHiddenStorey);
interactionController.addTransformFilter(unlessLocked);

// Unlocks the model: the filters are read at each interaction, nothing to update.
isModelLocked = false;

// Removed with the same function (e.g. in a plugin's `onUnregister`).
interactionController.removeTransformFilter(unlessLocked);

/**
 * An operator follows them as the default one does: the interaction controller's checks are
 * filters of the objects under the pointer.
 */
export class InspectOperator extends ViewportOperator {
  readonly name = 'InspectOperator';

  override onPointerClick(_event: PointerEvent, intersections: PointerIntersections) {
    const { interactionController: controller } = this._viewport;
    // The first object the user may pick, the others seen through.
    const hit = intersections.getFirst(controller.isObjectRaycastable);
    const object: SceneObject | null = hit?.object ?? null;
    if (object && controller.isObjectSelectable(object)) {
      console.info(`${object.uuid} may be selected`, controller.isObjectTransformable(object));
    }
  }
}