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 STSHelix extends STShape {
/**
*
* @param {STLayer} stLayer The STLayer instance used to create the shape
* @param {object} options Options of the shape
* @param {number} options.radius Radius of the helix
* @param {number} options.delta Distance between two versions on Z axis
*/
constructor(stLayer, options = {}) {
super(stLayer);
/** @type {number} */
this.radius = isNaN(options.radius) ? 1000 : options.radius;
/** @type {number} */
this.delta = isNaN(options.delta) ? 1000 : options.delta;
}
display(displayMode = DISPLAY_MODE.SEQUENTIAL) {
super.display();
const view = this.stLayer.view;
const rootObject3D = this.stLayer.rootObject3D;
// Init helix line
const pointsDisplayed = [];
const angleBetweenVersions = 240;
const helixAngle = angleBetweenVersions;
const helixLength = helixAngle * (this.stLayer.versions.length - 1);
for (let i = 0; i <= helixLength; i += 10) {
const angle = (i * -Math.PI) / 180;
pointsDisplayed.push(
new THREE.Vector3(
this.radius * Math.cos(angle) - this.radius,
this.radius * Math.sin(angle) - this.radius,
(this.delta / (helixAngle / 10)) * (i / 10)
)
);
}
const geometryDisplayed = new THREE.BufferGeometry().setFromPoints(
pointsDisplayed
);
const materialDisplayed = new THREE.LineBasicMaterial({ color: 0x0000ff });
const helixLine = new THREE.Line(geometryDisplayed, materialDisplayed);
rootObject3D.add(helixLine);
helixLine.position.y += this.radius;
helixLine.updateMatrixWorld();
// Place versions cdtlayers + labels on the circle
let yearDelta;
let heightDelta;
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);
switch (displayMode) {
case DISPLAY_MODE.SEQUENTIAL: {
interval = this.stLayer.versions.indexOf(version);
yearDelta = helixLength / (this.stLayer.versions.length - 1);
heightDelta = this.delta;
break;
}
case DISPLAY_MODE.CHRONOLOGICAL: {
interval = version.date - firstDate;
yearDelta = helixLength / this.stLayer.dateInterval;
heightDelta =
(this.delta * (this.stLayer.versions.length - 1)) /
this.stLayer.dateInterval;
break;
}
}
const angleDeg = yearDelta * -interval;
const angleRad = (angleDeg * Math.PI) / 180;
const point = new THREE.Vector3(
this.radius * Math.cos(angleRad) - this.radius,
this.radius * Math.sin(angleRad) - this.radius,
0
);
version.c3DTLayer.visible = false;
const dateSprite = createSpriteFromString(version.date.toString());
const newPosition = new THREE.Vector3(
helixLine.position.x + point.x,
helixLine.position.y + point.y,
heightDelta * interval
);
// position C3DTLayer
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();
}
}