NavigationEventHandler

NavigationEventHandler – базовый класс работы с навигацией. Для того, чтобы создать свой обработчик для навигации по сцене необходимо унаследоваться от класса PilotWeb3D.NavigationEventHandler.

Например навигация для расширения BoxSelection: Здесь BoxSelectionNavigationHandler класс наследуется от PilotWeb3D.DesktopNavigationEventHandler который является наследником базового класса PilotWeb3D.NavigationEventHandler.


export class BoxSelectionNavigationHandler extends PilotWeb3D.DesktopNavigationEventHandler {
  private _selectionStartPoint: THREE.Vector2 = new THREE.Vector2();
  private _selectionEndPoint: THREE.Vector2 = new THREE.Vector2();
  private _selectionKeyEnable = false;
  private _boxSelectionEnable = false;
  private _selectionRectangle: HTMLDivElement;
  private _styleContains = 'box-selection-contains';
  private _styleIntersects = 'box-selection-intersects';

  override get name(): string {
    return "BoxSelectionNavigationHandler";
  }

  constructor(private _model: PilotWeb3D.Model) {
    super();
    this._selectionRectangle = document.createElement('div');
    this._selectionRectangle.classList.add(this._styleContains);
    this._selectionRectangle.style.pointerEvents = 'none';
  }

  protected override onMouseMove(ev: MouseEvent): void {
    if (this._boxSelectionEnable) {
      const currentPos = new THREE.Vector2(ev.clientX, ev.clientY);
      this.onSelectionMove(currentPos);
      this._prevMousePos.copy(currentPos);
    } else {
      super.onMouseMove(ev);
    }
  }

  protected override onMouseClick(ev: MouseEvent): void {
    if (this._selectionKeyEnable) {// left button
      return;
    }
    else {
      super.onMouseClick(ev);
    }
  }

  protected override onMouseDown(ev: MouseEvent): void {
    super.onMouseDown(ev);
    if (ev.button == MouseButtons.Left)
      this.switchSelectionMode();
  }

  protected override onMouseUp(ev: MouseEvent): void {
    super.onMouseUp(ev);
    if (ev.button == MouseButtons.Left)
      this.switchSelectionMode();
  }

  protected override onKeyDown(ev: KeyboardEvent): void {
    super.onKeyDown(ev);
    if (ev.key === "Shift") {
      if (ev.repeat)
        return;
      this._selectionKeyEnable = true;
      this.switchSelectionMode();
    }
  }

  protected override onKeyUp(ev: KeyboardEvent): void {
    super.onKeyUp(ev);
    if (ev.key === "Shift") {
      if (ev.repeat)
        return;
      this._selectionKeyEnable = false;
      this.switchSelectionMode();
    }
  }

  private switchSelectionMode(): void {
    if (this._selectionKeyEnable && this._mouseLftIsDown && !this._boxSelectionEnable) {
      this.onSelectionStart(this._prevMousePos);
    }
    else if (this._boxSelectionEnable) {
      this.onSelectionEnd(this._prevMousePos);
    }
  }

  private onSelectionStart(mousePos: THREE.Vector2): void {
    this._boxSelectionEnable = true;
    this._selectionStartPoint = mousePos.clone();
    this._canvas.parentElement.appendChild(this._selectionRectangle);
  }

  private onSelectionEnd(mousePos: THREE.Vector2): void {
    this._boxSelectionEnable = false;
    this._selectionEndPoint = mousePos.clone();
    this._selectionRectangle.style.left = mousePos.x + 'px';
    this._selectionRectangle.style.top = mousePos.y + 'px';
    this._selectionRectangle.style.width = 1 + "px";
    this._selectionRectangle.style.height = 1 + "px";
    this._canvas.parentElement.removeChild(this._selectionRectangle);
    this.handleBoxSelection();
  }

  private onSelectionMove(mousePos: THREE.Vector2): void {
    const left = Math.min(this._selectionStartPoint.x, mousePos.x);
    const right = Math.max(this._selectionStartPoint.x, mousePos.x);
    const top = Math.min(this._selectionStartPoint.y, mousePos.y);
    const bottom = Math.max(this._selectionStartPoint.y, mousePos.y);

    this._selectionRectangle.style.left = left + 'px';
    this._selectionRectangle.style.top = top + 'px';
    this._selectionRectangle.style.width = (right - left) + "px";
    this._selectionRectangle.style.height = (bottom - top) + "px";

    if (mousePos.x >= this._selectionStartPoint.x) {
      this._selectionRectangle.classList.replace(this._styleIntersects, this._styleContains);
    } else {
      this._selectionRectangle.classList.replace(this._styleContains, this._styleIntersects);
    }
  }

  private handleBoxSelection(): void {
    if (this._selectionStartPoint.x === this._selectionEndPoint.x) {
      this._selectionEndPoint.x += 1;
    }

    if (this._selectionStartPoint.y === this._selectionEndPoint.y) {
      this._selectionEndPoint.y += 1;
    }

    const isContainsOnly = this._selectionEndPoint.x > this._selectionStartPoint.x;

    const unProjMatrix = new THREE.Matrix4();
    const ndcFrustumBox = this.calcNdcFrustumBox(unProjMatrix);
    const intersectionIDs = this._intersectionChecker.getIntersectionIDByFrustumNdcPt(ndcFrustumBox, unProjMatrix, isContainsOnly);

    const selectionIDs: Map<string, string[]> = new Map<string, string[]>();
    for (const { modelId, guid } of intersectionIDs) {
      selectionIDs.has(modelId) ? selectionIDs.get(modelId).push(guid) : selectionIDs.set(modelId, [guid]);
    }

    this._model.clearSelection();
    for (const [modelId, guids] of selectionIDs) {
      this._model.select(guids, modelId, PilotWeb3D.SelectionMode.Append);
    }
  }

  private calcNdcFrustumBox(unprojectMatrix: THREE.Matrix4): THREE.Box3 {
    const camera = this._cameraControl.getCamera();
    camera.updateMatrix();
    camera.updateMatrixWorld(true);

    unprojectMatrix.multiplyMatrices(camera.matrixWorld, camera.projectionMatrixInverse);
    const ndcFrustumBox = new THREE.Box3();
    //Get screen coordinates
    const left = Math.min(this._selectionStartPoint.x, this._selectionEndPoint.x);
    const top = Math.min(this._selectionStartPoint.y, this._selectionEndPoint.y);
    const right = Math.max(this._selectionStartPoint.x, this._selectionEndPoint.x);
    const bottom = Math.max(this._selectionStartPoint.y, this._selectionEndPoint.y);

    //Get ndc coordinates
    const topRight = getNdcPos(new THREE.Vector2(right, top), this._canvas);
    const bottomLeft = getNdcPos(new THREE.Vector2(left, bottom), this._canvas);

    ndcFrustumBox.min = new THREE.Vector3(bottomLeft.x, bottomLeft.y, -1);
    ndcFrustumBox.max = new THREE.Vector3(topRight.x, topRight.y, 1);

    return ndcFrustumBox;
  }

Обработчик навигации, базовый класс.

export abstract class NavigationEventHandler {
    protected _isActive: boolean;
    protected _canvas: HTMLCanvasElement;
    protected _cameraControl: ICameraControl;
    protected _intersectionChecker: IModelIntersectionChecker;
    protected _viewCenter: THREE.Vector3;
    protected _pivotPoint: THREE.Vector3;
    abstract get name(): string;
    init(canvas: HTMLCanvasElement, cameraControl: ICameraControl, intersectionChecker: IModelIntersectionChecker): void;
    deInit(): void;
    dispose(): void;
    getViewCenter(): THREE.Vector3;
    setViewCenter(viewCenter: THREE.Vector3): void;
    getPivotPoint(): THREE.Vector3;
    setPivotPoint(pivotPoint: THREE.Vector3): void;
    setCameraParameters(iParams: {
        position: THREE.Vector3;
        eyeDir: THREE.Vector3;
        angle: number;
        viewCenter: THREE.Vector3;
    }): void;
    getCameraParameters(): {
        position: THREE.Vector3;
        eyeDir: THREE.Vector3;
        angle: number;
        viewCenter: THREE.Vector3;
    };
    getCamera(): THREE.Camera;
    clear(): void;
    protected handleHovered(object: THREE.Object3D): void;
    protected handleClick(object: THREE.Object3D, ctrlKey: boolean): void;
    protected handleDblClick(object: THREE.Object3D): void;
    protected abstract addEvents(): void;
    protected abstract removeEvents(): void;
    protected rotate(movement: THREE.Vector2): void;
    protected translate(prevPos: THREE.Vector2, currPos: THREE.Vector2): void;
    protected spin(movement: THREE.Vector2): void;
    protected zoom(deltaSign: number): void;
    protected resetSelection(): void;
}

DesktopNavigationEventHandler

Обработчик навигации для десктопной версии приложения

export class DesktopNavigationEventHandler extends NavigationEventHandler {
    protected _prevMousePos?: THREE.Vector2;
    protected _mouseLftIsDown: boolean;
    protected _mouseLftIsDownPos?: THREE.Vector2;
    protected _mouseRhtIsDown: boolean;
    protected _mouseMidIsDown: boolean;
    protected _boundOnMouseEnter: any;
    protected _boundOnMouseLeave: any;
    protected _boundOnMouseMove: any;
    protected _boundOnMouseClick: any;
    protected _boundOnMouseDoubleClick: any;
    protected _boundOnMouseScroll: any;
    protected _boundOnMouseDown: any;
    protected _boundOnMouseUp: any;
    protected _boundOnKeyDown: any;
    protected _boundOnKeyUp: any;
    get name(): string;
    protected addEvents(): void;
    protected removeEvents(): void;
    protected onMouseEnter(ev: MouseEvent): void;
    protected onMouseLeave(): void;
    protected onMouseMove(ev: MouseEvent): void;
    protected onMouseClick(ev: MouseEvent): void;
    protected onMouseDoubleClick(ev: MouseEvent): void;
    protected onMouseScroll(ev: WheelEvent): void;
    protected onMouseDown(ev: MouseEvent): void;
    protected onMouseUp(ev: MouseEvent): void;
    protected onKeyDown(ev: KeyboardEvent): void;
    protected onKeyUp(ev: KeyboardEvent): void;
    protected findSupportedEvent(array: string[]): string | undefined;
    protected isAllMouseButtonsUp(): boolean;
    protected setImpulseDirection(dir: Direction, add: boolean): void;
    protected setIncreasedImpulse(isIncreased: boolean): void;
}

MobileNavigationEventHandler

Обработчик навигации для мобильной версии приложения

export class MobileNavigationEventHandler extends NavigationEventHandler {
    protected _initialTap?: THREE.Vector2;
    protected _prevTap?: THREE.Vector2;
    protected _prevPinch?: [THREE.Vector2, THREE.Vector2];
    protected _boundOnTouchStart: any;
    protected _boundOnTouchEnd: any;
    protected _boundOnTouchMove: any;
    get name(): string;
    protected addEvents(): void;
    protected removeEvents(): void;
    protected onTouchStart(evt: TouchEvent): void;
    protected onTouchEnd(evt: TouchEvent): void;
    protected onTouchMove(evt: TouchEvent): void;
    protected onSwipe(currentPos: THREE.Vector2): void;
    protected onPinch(iTouchPair: [THREE.Vector2, THREE.Vector2]): void;
    protected getPinchCenter(iTouchPair: [THREE.Vector2, THREE.Vector2]): THREE.Vector2;
    protected getTouchPoint(touch: Touch): THREE.Vector2;
    protected getTouchPair(curTouches: TouchList): [THREE.Vector2, THREE.Vector2];
}