@giro3d/giro3d
Version:
A JS/WebGL framework for 3D geospatial data visualization
115 lines (85 loc) • 3.94 kB
text/typescript
/*
* Copyright (c) 2015-2018, IGN France.
* Copyright (c) 2018-2026, Giro3D team.
* SPDX-License-Identifier: MIT
*/
import type { Matrix4 } from 'three';
import { Box3, MathUtils, Sphere, Vector2, Vector3 } from 'three';
import type ElevationRange from '../../core/ElevationRange';
import type Extent from '../../core/geographic/Extent';
import Coordinates from '../../core/geographic/Coordinates';
import CoordinateSystem from '../../core/geographic/CoordinateSystem';
import { toCartesian } from './PanoramaTileGeometry';
import TileVolume from './TileVolume';
const vec3 = new Vector3();
const vec2 = new Vector2();
const coord = new Coordinates(CoordinateSystem.epsg4326, 0, 0);
const tmpCorners: Vector3[] = [];
export default class PanoramaTileVolume extends TileVolume {
private readonly _radius: number;
private readonly _extent: Extent;
private _corners: Vector3[] | null = null;
public get extent(): Readonly<Extent> {
return this._extent;
}
public get radius(): Readonly<number> {
return this._radius;
}
public constructor(options: { extent: Extent; radius: number }) {
super();
this._extent = options.extent;
this._radius = options.radius;
}
public getWorldSpaceCorners(matrix: Matrix4, target?: Vector3[]): Vector3[] {
if (this._corners == null) {
const dims = this._extent.dimensions(vec2);
const xCount = MathUtils.clamp(Math.round(dims.width / 5) + 1, 2, 6);
const yCount = MathUtils.clamp(Math.round(dims.height / 5) + 1, 2, 6);
this._corners = new Array(xCount * yCount);
const uStep = 1 / (xCount - 1);
const jStep = 1 / (yCount - 1);
let index = 0;
for (let i = 0; i < xCount; i++) {
for (let j = 0; j < yCount; j++) {
const u = i * uStep;
const v = j * jStep;
const { latitude, longitude } = this._extent.sampleUV(u, v, coord);
const p0 = toCartesian(latitude, longitude, this._radius, new Vector3());
this._corners[index++] = p0;
}
}
}
target = target ?? [];
target.length = this._corners.length;
for (let i = 0; i < target.length; i++) {
target[i] = this._corners[i].clone().applyMatrix4(matrix);
}
return target;
}
protected override computeLocalBox(): Box3 {
const extent = this._extent;
const radius = this._radius;
const nw = toCartesian(extent.maxY, extent.minX, radius, new Vector3());
const sw = toCartesian(extent.minY, extent.minX, radius, new Vector3());
const se = toCartesian(extent.minY, extent.maxX, radius, new Vector3());
const ne = toCartesian(extent.maxY, extent.maxX, radius, new Vector3());
const center = extent.center(coord);
const c = toCartesian(center.latitude, center.longitude, radius, new Vector3());
const nc = toCartesian(extent.maxY, center.longitude, radius, new Vector3());
const cw = toCartesian(center.latitude, extent.minX, radius, new Vector3());
const ce = toCartesian(center.latitude, extent.maxX, radius, new Vector3());
const sc = toCartesian(extent.minY, center.longitude, radius, new Vector3());
const worldBox = new Box3().setFromPoints([nw, sw, se, ne, c, nc, cw, ce, sc]);
return worldBox.setFromCenterAndSize(
worldBox.getCenter(vec3).sub(nw),
worldBox.getSize(new Vector3()),
);
}
public setElevationRange(_range: ElevationRange): void {
// Nothing to do
}
public override getWorldSpaceBoundingSphere(target: Sphere, matrix: Matrix4): Sphere {
target = target ?? new Sphere();
return target.setFromPoints(this.getWorldSpaceCorners(matrix, tmpCorners));
}
}