itowns
Version:
A JS/WebGL framework for 3D geospatial data visualization
150 lines (142 loc) • 5.64 kB
JavaScript
import * as THREE from 'three';
import { TileGeometry } from "../Core/TileGeometry.js";
import { GlobeTileBuilder } from "../Core/Prefab/Globe/GlobeTileBuilder.js";
import { CRS, Coordinates } from '@itowns/geographic';
// get oriented bounding box of tile
const builder = new GlobeTileBuilder({
uvCount: 1
});
const size = new THREE.Vector3();
const dimension = new THREE.Vector2();
const center = new THREE.Vector3();
const coord = new Coordinates('EPSG:4326', 0, 0, 0);
let obb;
// it could be considered to remove THREE.Object3D extend.
/**
* Oriented bounding box
* @extends THREE.Object3D
*/
class OBB extends THREE.Object3D {
/**
* @param {THREE.Vector3} min representing the lower (x, y, z) boundary of the box. Default is ( + Infinity, + Infinity, + Infinity ).
* @param {THREE.Vector3} max representing the lower upper (x, y, z) boundary of the box. Default is ( - Infinity, - Infinity, - Infinity ).
*/
constructor() {
let min = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new THREE.Vector3(+Infinity, +Infinity, +Infinity);
let max = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Vector3(-Infinity, -Infinity, -Infinity);
super();
this.box3D = new THREE.Box3(min.clone(), max.clone());
this.natBox = this.box3D.clone();
this.z = {
min: 0,
max: 0,
scale: 1.0
};
}
/**
* Creates a new instance of the object with same properties than original.
*
* @return {OBB} Copy of this object.
*/
clone() {
return new OBB().copy(this);
}
/**
* Copy the property of OBB
*
* @param {OBB} cOBB OBB to copy
* @return {OBB} the copy
*/
copy(cOBB) {
super.copy(cOBB);
this.box3D.copy(cOBB.box3D);
this.natBox.copy(cOBB.natBox);
this.z.min = cOBB.z.min;
this.z.max = cOBB.z.max;
this.z.scale = cOBB.z.scale;
return this;
}
/**
* Update z min, z max and z scale of oriented bounding box
*
* @param {Object} [elevation={}]
* @param {number} [elevation.min] The minimum of oriented bounding box
* @param {number} [elevation.max] The maximum of oriented bounding box
* @param {number} [elevation.scale] The scale of oriented bounding box Z axis
* @param {number} [elevation.geoidHeight] The geoid height added to ellipsoid.
*/
updateZ() {
let elevation = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this.z.min = elevation.min ?? this.z.min;
this.z.max = elevation.max ?? this.z.max;
this.z.scale = elevation.scale > 0 ? elevation.scale : this.z.scale;
this.z.delta = Math.abs(this.z.max - this.z.min) * this.z.scale;
const geoidHeight = elevation.geoidHeight || 0;
this.box3D.min.z = this.natBox.min.z + this.z.min * this.z.scale + geoidHeight;
this.box3D.max.z = this.natBox.max.z + this.z.max * this.z.scale + geoidHeight;
}
/**
* Determines if the sphere is above the XY space of the box
*
* @param {Sphere} sphere The sphere
* @return {boolean} True if sphere is above the XY space of the box, False otherwise.
*/
isSphereAboveXYBox(sphere) {
const localSpherePosition = this.worldToLocal(sphere.center);
// get obb closest point to sphere center by clamping
const x = Math.max(this.box3D.min.x, Math.min(localSpherePosition.x, this.box3D.max.x));
const y = Math.max(this.box3D.min.y, Math.min(localSpherePosition.y, this.box3D.max.y));
// this is the same as isPointInsideSphere.position
const distance = Math.sqrt((x - localSpherePosition.x) * (x - localSpherePosition.x) + (y - localSpherePosition.y) * (y - localSpherePosition.y));
return distance < sphere.radius;
}
/**
* Compute OBB from extent.
* The OBB resulted can be only in the system 'EPSG:3946'.
*
* @param {Extent} extent The extent (with crs 'EPSG:4326') to compute oriented bounding box
* @param {number} minHeight The minimum height of OBB
* @param {number} maxHeight The maximum height of OBB
* @return {OBB} return this object
*/
setFromExtent(extent) {
let minHeight = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : extent.min || 0;
let maxHeight = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : extent.max || 0;
if (extent.crs == 'EPSG:4326') {
const {
shareableExtent,
quaternion,
position
} = builder.computeShareableExtent(extent);
// Compute the minimum count of segment to build tile
const segments = Math.max(Math.floor(shareableExtent.planarDimensions(dimension).x / 90 + 1), 2);
const geometry = new TileGeometry(builder, {
extent: shareableExtent,
level: 0,
segments,
disableSkirt: true
});
obb.box3D.copy(geometry.boundingBox);
obb.natBox.copy(geometry.boundingBox);
this.copy(obb);
this.updateZ({
min: minHeight,
max: maxHeight
});
this.position.copy(position);
this.quaternion.copy(quaternion);
this.updateMatrixWorld(true);
} else if (CRS.isMetricUnit(extent.crs)) {
extent.center(coord).toVector3(this.position);
extent.planarDimensions(dimension);
size.set(dimension.x, dimension.y, Math.abs(maxHeight - minHeight));
this.box3D.setFromCenterAndSize(center, size);
this.updateMatrixWorld(true);
} else {
throw new Error('Unsupported extent crs');
}
return this;
}
}
obb = new OBB();
export default OBB;