STSVector.js

import * as THREE from 'three';
import { createSpriteFromString } from '@ud-viz/utils_browser/src/THREEUtil';
import { STLayer } from './STLayer';
import { DISPLAY_MODE, STShape } from './STShape';

export class STSVector extends STShape {
  /**
   *
   * @param {STLayer} stLayer The STLayer instance used to create the shape
   * @param {object} options Options of the shape
   * @param {number} options.delta Distance between two versions on Z axis
   * @param {number} options.alpha Distance between two versions on Y axis
   */
  constructor(stLayer, options = {}) {
    super(stLayer);

    /** @type {number} */
    this.delta = isNaN(options.delta) ? 1000 : options.delta;
    /** @type {number} */
    this.alpha = isNaN(options.alpha) ? 100 : options.alpha;
  }

  display(displayMode = DISPLAY_MODE.SEQUENTIAL) {
    super.display();

    const view = this.stLayer.view;
    const rootObject3D = this.stLayer.rootObject3D;

    const geometryDisplayed = new THREE.BufferGeometry().setFromPoints([
      new THREE.Vector3(0, 0, 0),
      new THREE.Vector3(
        0,
        this.alpha * (this.stLayer.versions.length - 1),
        this.delta * (this.stLayer.versions.length - 1)
      ),
    ]);
    const materialDisplayed = new THREE.LineBasicMaterial({ color: 0x0000ff });
    const vectorLine = new THREE.Line(geometryDisplayed, materialDisplayed);

    rootObject3D.add(vectorLine);
    vectorLine.updateMatrixWorld();

    let yearDelta;
    let yearAlpha;
    let interval;

    const firstDate = this.stLayer.versions[0].date;

    this.stLayer.versions.forEach((version) => {
      const objectCopy = new THREE.Object3D().copy(
        version.c3DTLayer.root,
        true
      );
      rootObject3D.add(objectCopy);
      const versionIndex = this.stLayer.versions.indexOf(version);

      switch (displayMode) {
        case DISPLAY_MODE.SEQUENTIAL: {
          interval = versionIndex;
          yearDelta = this.delta;
          yearAlpha = this.alpha;
          break;
        }
        case DISPLAY_MODE.CHRONOLOGICAL: {
          interval = version.date - firstDate;
          yearDelta =
            (this.delta * (this.stLayer.versions.length - 1)) /
            this.stLayer.dateInterval;
          yearAlpha =
            (this.alpha * (this.stLayer.versions.length - 1)) /
            this.stLayer.dateInterval;
          break;
        }
      }

      const newPosition = new THREE.Vector3(
        0,
        yearAlpha * interval,
        yearDelta * interval
      );

      version.c3DTLayer.visible = false;

      const dateSprite = createSpriteFromString(version.date.toString());
      objectCopy.position.copy(newPosition);
      for (let i = 0; i < objectCopy.children.length; i++) {
        const child = objectCopy.children[i];
        const tileId = version.c3DTLayer.root.children[i].tileId;
        const tile = version.c3DTLayer.tileset.tiles[tileId];
        const tileTransform = tile.transform.elements;
        const tilePosition = new THREE.Vector3(
          tileTransform[12],
          tileTransform[13],
          tileTransform[14]
        );
        child.position.copy(tilePosition.sub(this.layerCentroid));
      }

      dateSprite.position.copy(newPosition);

      // Date label sprite
      dateSprite.position.z += 40;
      dateSprite.scale.multiplyScalar(0.02);
      dateSprite.updateMatrixWorld();
      rootObject3D.add(dateSprite);
    });
    rootObject3D.updateMatrixWorld();

    view.notifyChange();
  }

  update() {}

  dispose() {
    super.dispose();
  }
}