itowns 2.45.1-next.0 → 2.45.1-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/455.js +2 -0
- package/dist/455.js.map +1 -0
- package/dist/debug.js +3 -0
- package/dist/debug.js.LICENSE.txt +13 -0
- package/dist/debug.js.map +1 -0
- package/dist/itowns.js +3 -0
- package/dist/itowns.js.LICENSE.txt +5 -0
- package/dist/itowns.js.map +1 -0
- package/dist/itowns_lasparser.js +2 -0
- package/dist/itowns_lasparser.js.map +1 -0
- package/dist/itowns_lasworker.js +2 -0
- package/dist/itowns_lasworker.js.map +1 -0
- package/dist/itowns_potree2worker.js +2 -0
- package/dist/itowns_potree2worker.js.map +1 -0
- package/dist/itowns_widgets.js +2 -0
- package/dist/itowns_widgets.js.map +1 -0
- package/lib/Controls/FirstPersonControls.js +308 -0
- package/lib/Controls/FlyControls.js +175 -0
- package/lib/Controls/GlobeControls.js +1178 -0
- package/lib/Controls/PlanarControls.js +1025 -0
- package/lib/Controls/StateControl.js +432 -0
- package/lib/Controls/StreetControls.js +392 -0
- package/lib/Converter/Feature2Mesh.js +612 -0
- package/lib/Converter/Feature2Texture.js +174 -0
- package/lib/Converter/convertToTile.js +70 -0
- package/lib/Converter/textureConverter.js +43 -0
- package/lib/Core/3DTiles/C3DTBatchTable.js +131 -0
- package/lib/Core/3DTiles/C3DTBatchTableHierarchyExtension.js +96 -0
- package/lib/Core/3DTiles/C3DTBoundingVolume.js +156 -0
- package/lib/Core/3DTiles/C3DTExtensions.js +97 -0
- package/lib/Core/3DTiles/C3DTFeature.js +110 -0
- package/lib/Core/3DTiles/C3DTilesEnums.js +20 -0
- package/lib/Core/3DTiles/C3DTileset.js +99 -0
- package/lib/Core/3DTiles/utils/BinaryPropertyAccessor.js +100 -0
- package/lib/Core/AnimationPlayer.js +142 -0
- package/lib/Core/CopcNode.js +174 -0
- package/lib/Core/Deprecated/Undeprecator.js +74 -0
- package/lib/Core/EntwinePointTileNode.js +126 -0
- package/lib/Core/Feature.js +488 -0
- package/lib/Core/Geographic/GeoidGrid.js +108 -0
- package/lib/Core/Label.js +222 -0
- package/lib/Core/MainLoop.js +209 -0
- package/lib/Core/Picking.js +255 -0
- package/lib/Core/PointCloudNode.js +42 -0
- package/lib/Core/Potree2Node.js +206 -0
- package/lib/Core/Potree2PointAttributes.js +139 -0
- package/lib/Core/PotreeNode.js +101 -0
- package/lib/Core/Prefab/Globe/Atmosphere.js +293 -0
- package/lib/Core/Prefab/Globe/GlobeLayer.js +152 -0
- package/lib/Core/Prefab/Globe/GlobeTileBuilder.js +110 -0
- package/lib/Core/Prefab/Globe/SkyShader.js +78 -0
- package/lib/Core/Prefab/GlobeView.js +155 -0
- package/lib/Core/Prefab/Planar/PlanarLayer.js +59 -0
- package/lib/Core/Prefab/Planar/PlanarTileBuilder.js +71 -0
- package/lib/Core/Prefab/PlanarView.js +62 -0
- package/lib/Core/Prefab/TileBuilder.js +82 -0
- package/lib/Core/Prefab/computeBufferTileGeometry.js +248 -0
- package/lib/Core/Scheduler/Cache.js +17 -0
- package/lib/Core/Scheduler/CancelledCommandException.js +15 -0
- package/lib/Core/Scheduler/Scheduler.js +294 -0
- package/lib/Core/Style.js +660 -0
- package/lib/Core/StyleOptions.js +486 -0
- package/lib/Core/System/Capabilities.js +63 -0
- package/lib/Core/Tile/Tile.js +205 -0
- package/lib/Core/Tile/TileGrid.js +49 -0
- package/lib/Core/TileGeometry.js +124 -0
- package/lib/Core/TileMesh.js +108 -0
- package/lib/Core/View.js +1115 -0
- package/lib/Layer/C3DTilesLayer.js +459 -0
- package/lib/Layer/ColorLayer.js +154 -0
- package/lib/Layer/CopcLayer.js +63 -0
- package/lib/Layer/ElevationLayer.js +139 -0
- package/lib/Layer/EntwinePointTileLayer.js +71 -0
- package/lib/Layer/FeatureGeometryLayer.js +77 -0
- package/lib/Layer/GeoidLayer.js +80 -0
- package/lib/Layer/GeometryLayer.js +233 -0
- package/lib/Layer/InfoLayer.js +64 -0
- package/lib/Layer/LabelLayer.js +469 -0
- package/lib/Layer/Layer.js +335 -0
- package/lib/Layer/LayerUpdateState.js +89 -0
- package/lib/Layer/LayerUpdateStrategy.js +80 -0
- package/lib/Layer/OGC3DTilesLayer.js +543 -0
- package/lib/Layer/OrientedImageLayer.js +227 -0
- package/lib/Layer/PointCloudLayer.js +405 -0
- package/lib/Layer/Potree2Layer.js +171 -0
- package/lib/Layer/PotreeLayer.js +72 -0
- package/lib/Layer/RasterLayer.js +37 -0
- package/lib/Layer/ReferencingLayerProperties.js +62 -0
- package/lib/Layer/TiledGeometryLayer.js +459 -0
- package/lib/Loader/LASLoader.js +193 -0
- package/lib/Loader/Potree2BrotliLoader.js +261 -0
- package/lib/Loader/Potree2Loader.js +207 -0
- package/lib/Main.js +113 -0
- package/lib/MainBundle.js +4 -0
- package/lib/Parser/B3dmParser.js +174 -0
- package/lib/Parser/CameraCalibrationParser.js +94 -0
- package/lib/Parser/GDFParser.js +72 -0
- package/lib/Parser/GTXParser.js +75 -0
- package/lib/Parser/GeoJsonParser.js +212 -0
- package/lib/Parser/GpxParser.js +25 -0
- package/lib/Parser/ISGParser.js +71 -0
- package/lib/Parser/KMLParser.js +25 -0
- package/lib/Parser/LASParser.js +137 -0
- package/lib/Parser/MapBoxUrlParser.js +83 -0
- package/lib/Parser/PntsParser.js +131 -0
- package/lib/Parser/Potree2BinParser.js +92 -0
- package/lib/Parser/PotreeBinParser.js +106 -0
- package/lib/Parser/PotreeCinParser.js +29 -0
- package/lib/Parser/ShapefileParser.js +78 -0
- package/lib/Parser/VectorTileParser.js +215 -0
- package/lib/Parser/XbilParser.js +120 -0
- package/lib/Parser/deprecated/LegacyGLTFLoader.js +1386 -0
- package/lib/Parser/iGLTFLoader.js +168 -0
- package/lib/Process/3dTilesProcessing.js +304 -0
- package/lib/Process/FeatureProcessing.js +76 -0
- package/lib/Process/LayeredMaterialNodeProcessing.js +229 -0
- package/lib/Process/ObjectRemovalHelper.js +97 -0
- package/lib/Process/handlerNodeError.js +23 -0
- package/lib/Provider/3dTilesProvider.js +149 -0
- package/lib/Provider/DataSourceProvider.js +24 -0
- package/lib/Provider/Fetcher.js +233 -0
- package/lib/Provider/PointCloudProvider.js +45 -0
- package/lib/Provider/TileProvider.js +16 -0
- package/lib/Provider/URLBuilder.js +116 -0
- package/lib/Renderer/Camera.js +281 -0
- package/lib/Renderer/Color.js +56 -0
- package/lib/Renderer/ColorLayersOrdering.js +115 -0
- package/lib/Renderer/CommonMaterial.js +31 -0
- package/lib/Renderer/Label2DRenderer.js +192 -0
- package/lib/Renderer/LayeredMaterial.js +243 -0
- package/lib/Renderer/OBB.js +150 -0
- package/lib/Renderer/OrientedImageCamera.js +118 -0
- package/lib/Renderer/OrientedImageMaterial.js +167 -0
- package/lib/Renderer/PointsMaterial.js +485 -0
- package/lib/Renderer/RasterTile.js +243 -0
- package/lib/Renderer/RenderMode.js +31 -0
- package/lib/Renderer/Shader/ShaderChunk.js +160 -0
- package/lib/Renderer/Shader/ShaderUtils.js +47 -0
- package/lib/Renderer/SphereHelper.js +17 -0
- package/lib/Renderer/WebXR.js +51 -0
- package/lib/Renderer/c3DEngine.js +214 -0
- package/lib/Source/C3DTilesGoogleSource.js +74 -0
- package/lib/Source/C3DTilesIonSource.js +54 -0
- package/lib/Source/C3DTilesSource.js +30 -0
- package/lib/Source/CopcSource.js +126 -0
- package/lib/Source/EntwinePointTileSource.js +72 -0
- package/lib/Source/FileSource.js +188 -0
- package/lib/Source/OGC3DTilesGoogleSource.js +29 -0
- package/lib/Source/OGC3DTilesIonSource.js +34 -0
- package/lib/Source/OGC3DTilesSource.js +21 -0
- package/lib/Source/OrientedImageSource.js +59 -0
- package/lib/Source/Potree2Source.js +167 -0
- package/lib/Source/PotreeSource.js +82 -0
- package/lib/Source/Source.js +202 -0
- package/lib/Source/TMSSource.js +144 -0
- package/lib/Source/VectorTilesSource.js +182 -0
- package/lib/Source/WFSSource.js +170 -0
- package/lib/Source/WMSSource.js +167 -0
- package/lib/Source/WMTSSource.js +92 -0
- package/lib/ThreeExtended/capabilities/WebGL.js +69 -0
- package/lib/ThreeExtended/libs/ktx-parse.module.js +506 -0
- package/lib/ThreeExtended/libs/zstddec.module.js +29 -0
- package/lib/ThreeExtended/loaders/DDSLoader.js +200 -0
- package/lib/ThreeExtended/loaders/DRACOLoader.js +400 -0
- package/lib/ThreeExtended/loaders/GLTFLoader.js +2879 -0
- package/lib/ThreeExtended/loaders/KTX2Loader.js +709 -0
- package/lib/ThreeExtended/math/ColorSpaces.js +59 -0
- package/lib/ThreeExtended/utils/BufferGeometryUtils.js +846 -0
- package/lib/ThreeExtended/utils/WorkerPool.js +70 -0
- package/lib/Utils/CameraUtils.js +554 -0
- package/lib/Utils/DEMUtils.js +350 -0
- package/lib/Utils/FeaturesUtils.js +156 -0
- package/lib/Utils/Gradients.js +16 -0
- package/lib/Utils/ThreeUtils.js +115 -0
- package/lib/Utils/gui/C3DTilesStyle.js +218 -0
- package/lib/Utils/gui/Main.js +7 -0
- package/lib/Utils/gui/Minimap.js +152 -0
- package/lib/Utils/gui/Navigation.js +245 -0
- package/lib/Utils/gui/Scale.js +104 -0
- package/lib/Utils/gui/Searchbar.js +234 -0
- package/lib/Utils/gui/Widget.js +80 -0
- package/lib/Utils/placeObjectOnGround.js +136 -0
- package/lib/Worker/LASLoaderWorker.js +19 -0
- package/lib/Worker/Potree2Worker.js +21 -0
- package/package.json +2 -2
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Deepkolos / https://github.com/deepkolos
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export class WorkerPool {
|
|
6
|
+
constructor() {
|
|
7
|
+
let pool = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 4;
|
|
8
|
+
this.pool = pool;
|
|
9
|
+
this.queue = [];
|
|
10
|
+
this.workers = [];
|
|
11
|
+
this.workersResolve = [];
|
|
12
|
+
this.workerStatus = 0;
|
|
13
|
+
}
|
|
14
|
+
_initWorker(workerId) {
|
|
15
|
+
if (!this.workers[workerId]) {
|
|
16
|
+
const worker = this.workerCreator();
|
|
17
|
+
worker.addEventListener('message', this._onMessage.bind(this, workerId));
|
|
18
|
+
this.workers[workerId] = worker;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
_getIdleWorker() {
|
|
22
|
+
for (let i = 0; i < this.pool; i++) if (!(this.workerStatus & 1 << i)) return i;
|
|
23
|
+
return -1;
|
|
24
|
+
}
|
|
25
|
+
_onMessage(workerId, msg) {
|
|
26
|
+
const resolve = this.workersResolve[workerId];
|
|
27
|
+
resolve && resolve(msg);
|
|
28
|
+
if (this.queue.length) {
|
|
29
|
+
const {
|
|
30
|
+
resolve,
|
|
31
|
+
msg,
|
|
32
|
+
transfer
|
|
33
|
+
} = this.queue.shift();
|
|
34
|
+
this.workersResolve[workerId] = resolve;
|
|
35
|
+
this.workers[workerId].postMessage(msg, transfer);
|
|
36
|
+
} else {
|
|
37
|
+
this.workerStatus ^= 1 << workerId;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
setWorkerCreator(workerCreator) {
|
|
41
|
+
this.workerCreator = workerCreator;
|
|
42
|
+
}
|
|
43
|
+
setWorkerLimit(pool) {
|
|
44
|
+
this.pool = pool;
|
|
45
|
+
}
|
|
46
|
+
postMessage(msg, transfer) {
|
|
47
|
+
return new Promise(resolve => {
|
|
48
|
+
const workerId = this._getIdleWorker();
|
|
49
|
+
if (workerId !== -1) {
|
|
50
|
+
this._initWorker(workerId);
|
|
51
|
+
this.workerStatus |= 1 << workerId;
|
|
52
|
+
this.workersResolve[workerId] = resolve;
|
|
53
|
+
this.workers[workerId].postMessage(msg, transfer);
|
|
54
|
+
} else {
|
|
55
|
+
this.queue.push({
|
|
56
|
+
resolve,
|
|
57
|
+
msg,
|
|
58
|
+
transfer
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
dispose() {
|
|
64
|
+
this.workers.forEach(worker => worker.terminate());
|
|
65
|
+
this.workersResolve.length = 0;
|
|
66
|
+
this.workers.length = 0;
|
|
67
|
+
this.queue.length = 0;
|
|
68
|
+
this.workerStatus = 0;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import TWEEN from '@tweenjs/tween.js';
|
|
3
|
+
import DEMUtils from "./DEMUtils.js";
|
|
4
|
+
import { MAIN_LOOP_EVENTS } from "../Core/MainLoop.js";
|
|
5
|
+
import { Coordinates, Ellipsoid } from '@itowns/geographic';
|
|
6
|
+
import OBB from "../Renderer/OBB.js";
|
|
7
|
+
import { VIEW_EVENTS } from "../Core/View.js";
|
|
8
|
+
THREE.Object3D.DEFAULT_UP.set(0, 0, 1);
|
|
9
|
+
const targetPosition = new THREE.Vector3();
|
|
10
|
+
const targetCoord = new Coordinates('EPSG:4326', 0, 0, 0);
|
|
11
|
+
const ellipsoid = new Ellipsoid();
|
|
12
|
+
const rigs = [];
|
|
13
|
+
const obb = new OBB();
|
|
14
|
+
const size = new THREE.Vector3();
|
|
15
|
+
const deferred = () => {
|
|
16
|
+
let resolve;
|
|
17
|
+
let reject;
|
|
18
|
+
return {
|
|
19
|
+
promise: new Promise((re, rej) => {
|
|
20
|
+
resolve = re;
|
|
21
|
+
reject = rej;
|
|
22
|
+
}),
|
|
23
|
+
resolve,
|
|
24
|
+
reject
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Wrap angle in degrees to [-180 180]
|
|
29
|
+
function wrapTo180(angle) {
|
|
30
|
+
return angle - Math.floor((angle + 180.0) / 360) * 360;
|
|
31
|
+
}
|
|
32
|
+
function tileLayer(view) {
|
|
33
|
+
return view.getLayers(l => l.isTiledGeometryLayer)[0];
|
|
34
|
+
}
|
|
35
|
+
export function getLookAtFromMath(view, camera) {
|
|
36
|
+
const direction = new THREE.Vector3(0, 0, 0.5);
|
|
37
|
+
direction.unproject(camera);
|
|
38
|
+
direction.sub(camera.position).normalize();
|
|
39
|
+
if (view.referenceCrs == 'EPSG:4978') {
|
|
40
|
+
// Intersect Ellispoid
|
|
41
|
+
return ellipsoid.intersection({
|
|
42
|
+
direction,
|
|
43
|
+
origin: camera.position
|
|
44
|
+
});
|
|
45
|
+
} else {
|
|
46
|
+
// Intersect plane
|
|
47
|
+
const distance = camera.position.z / direction.z;
|
|
48
|
+
return direction.multiplyScalar(distance).add(camera.position);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function proxyProperty(view, camera, rig, key) {
|
|
52
|
+
rig.proxy.position[key] = camera.position[key];
|
|
53
|
+
Object.defineProperty(camera.position, key, {
|
|
54
|
+
get: () => rig.proxy.position[key],
|
|
55
|
+
set: newValue => {
|
|
56
|
+
rig.removeProxy(view, camera);
|
|
57
|
+
camera.position[key] = newValue;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// the rig is used to manipulate the camera
|
|
63
|
+
// It consists of a tree of 3D objects, each element is assigned a task
|
|
64
|
+
//
|
|
65
|
+
// Transformation
|
|
66
|
+
//
|
|
67
|
+
// rig position on Coordinate (for the globe is rotation)
|
|
68
|
+
// |
|
|
69
|
+
// └── sealevel position on altitude zero
|
|
70
|
+
// |
|
|
71
|
+
// └── target position on DEM, and rotation (pitch and heading)
|
|
72
|
+
// |
|
|
73
|
+
// └── camera distance to target
|
|
74
|
+
//
|
|
75
|
+
// When all transformations are calculated,
|
|
76
|
+
// this.camera's transformation is applied to view.camera.camera
|
|
77
|
+
class CameraRig extends THREE.Object3D {
|
|
78
|
+
constructor() {
|
|
79
|
+
super();
|
|
80
|
+
// seaLevel is on rig's z axis, it's at altitude zero
|
|
81
|
+
this.seaLevel = new THREE.Object3D();
|
|
82
|
+
// target is on seaLevel's z axis and target.position.z is the DEM altitude
|
|
83
|
+
this.target = new THREE.Object3D();
|
|
84
|
+
this.target.rotation.order = 'ZXY';
|
|
85
|
+
// camera look at target
|
|
86
|
+
this.camera = new THREE.Camera();
|
|
87
|
+
this.add(this.seaLevel);
|
|
88
|
+
this.seaLevel.add(this.target);
|
|
89
|
+
this.target.add(this.camera);
|
|
90
|
+
// target's geographic coordinate
|
|
91
|
+
this.coord = new Coordinates('EPSG:4978', 0, 0);
|
|
92
|
+
// sea level's worldPoistion
|
|
93
|
+
this.targetWorldPosition = new THREE.Vector3();
|
|
94
|
+
this.removeAll = () => {};
|
|
95
|
+
this._onChangeCallback = null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// apply rig.camera's transformation to camera
|
|
99
|
+
applyTransformToCamera(view, camera) {
|
|
100
|
+
if (this.proxy) {
|
|
101
|
+
camera.quaternion._onChange(this._onChangeCallback);
|
|
102
|
+
this.camera.matrixWorld.decompose(this.proxy.position, camera.quaternion, camera.scale);
|
|
103
|
+
camera.quaternion._onChange(() => this.removeProxy(view, camera));
|
|
104
|
+
} else {
|
|
105
|
+
this.camera.matrixWorld.decompose(camera.position, camera.quaternion, camera.scale);
|
|
106
|
+
}
|
|
107
|
+
view.dispatchEvent({
|
|
108
|
+
type: VIEW_EVENTS.CAMERA_MOVED,
|
|
109
|
+
coord: this.coord,
|
|
110
|
+
range: this.range,
|
|
111
|
+
heading: this.heading,
|
|
112
|
+
tilt: this.tilt
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
setProxy(view, camera) {
|
|
116
|
+
if (!this.proxy && view && camera) {
|
|
117
|
+
this.proxy = {
|
|
118
|
+
position: new THREE.Vector3()
|
|
119
|
+
};
|
|
120
|
+
Object.keys(camera.position).forEach(key => proxyProperty(view, camera, this, key));
|
|
121
|
+
this._onChangeCallback = camera.quaternion._onChangeCallback;
|
|
122
|
+
camera.quaternion._onChange(() => this.removeProxy(view, camera));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
removeProxy(view, camera) {
|
|
126
|
+
this.stop(view);
|
|
127
|
+
if (this.proxy && view && camera) {
|
|
128
|
+
Object.keys(camera.position).forEach(key => Object.defineProperty(camera.position, key, {
|
|
129
|
+
value: this.proxy.position[key],
|
|
130
|
+
writable: true
|
|
131
|
+
}));
|
|
132
|
+
camera.quaternion._onChange(this._onChangeCallback);
|
|
133
|
+
this.proxy = null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
setTargetFromCoordinate(view, coord) {
|
|
137
|
+
// compute precise coordinate (coord) altitude and clamp it above seaLevel
|
|
138
|
+
coord.as(tileLayer(view).extent.crs, this.coord);
|
|
139
|
+
const altitude = Math.max(0, DEMUtils.getElevationValueAt(tileLayer(view), this.coord, DEMUtils.PRECISE_READ_Z) || this.coord.z);
|
|
140
|
+
this.coord.z = altitude;
|
|
141
|
+
// adjust target's position with clamped altitude
|
|
142
|
+
this.coord.as(view.referenceCrs).toVector3(targetPosition);
|
|
143
|
+
if (view.referenceCrs == 'EPSG:4978') {
|
|
144
|
+
// ellipsoid geocentric projection
|
|
145
|
+
this.lookAt(targetPosition);
|
|
146
|
+
this.seaLevel.position.set(0, 0, targetPosition.length() - altitude);
|
|
147
|
+
} else {
|
|
148
|
+
// planar projection
|
|
149
|
+
this.position.set(targetPosition.x, targetPosition.y, 0);
|
|
150
|
+
this.seaLevel.position.set(0, 0, 0);
|
|
151
|
+
}
|
|
152
|
+
// place camera's target
|
|
153
|
+
this.target.position.set(0, 0, altitude);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// set rig's objects transformation from camera's position and target's position
|
|
157
|
+
setFromPositions(view, cameraPosition) {
|
|
158
|
+
this.setTargetFromCoordinate(view, new Coordinates(view.referenceCrs).setFromVector3(targetPosition));
|
|
159
|
+
this.target.rotation.set(0, 0, 0);
|
|
160
|
+
this.updateMatrixWorld(true);
|
|
161
|
+
this.camera.position.copy(cameraPosition);
|
|
162
|
+
this.target.worldToLocal(this.camera.position);
|
|
163
|
+
const range = this.camera.position.length();
|
|
164
|
+
this.target.rotation.x = Math.asin(this.camera.position.z / range);
|
|
165
|
+
const cosPlanXY = THREE.MathUtils.clamp(this.camera.position.y / (Math.cos(this.target.rotation.x) * range), -1, 1);
|
|
166
|
+
this.target.rotation.z = Math.sign(-this.camera.position.x || 1) * Math.acos(cosPlanXY);
|
|
167
|
+
this.camera.position.set(0, range, 0);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// set from target's coordinate, rotation and range between target and camera
|
|
171
|
+
applyParams(view, params) {
|
|
172
|
+
if (params.coord) {
|
|
173
|
+
this.setTargetFromCoordinate(view, params.coord);
|
|
174
|
+
}
|
|
175
|
+
if (params.tilt != undefined) {
|
|
176
|
+
this.target.rotation.x = THREE.MathUtils.degToRad(params.tilt);
|
|
177
|
+
}
|
|
178
|
+
if (params.heading != undefined) {
|
|
179
|
+
this.target.rotation.z = THREE.MathUtils.degToRad(-wrapTo180(params.heading + 180));
|
|
180
|
+
}
|
|
181
|
+
if (params.range) {
|
|
182
|
+
this.camera.position.set(0, params.range, 0);
|
|
183
|
+
}
|
|
184
|
+
this.camera.rotation.set(-Math.PI * 0.5, 0, Math.PI);
|
|
185
|
+
this.updateMatrixWorld(true);
|
|
186
|
+
this.targetWorldPosition.setFromMatrixPosition(this.seaLevel.matrixWorld);
|
|
187
|
+
}
|
|
188
|
+
getParams() {
|
|
189
|
+
return {
|
|
190
|
+
coord: this.coord.clone(),
|
|
191
|
+
tilt: this.tilt,
|
|
192
|
+
heading: this.heading,
|
|
193
|
+
range: this.range,
|
|
194
|
+
targetWorldPosition: this.targetWorldPosition
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
setfromCamera(view, camera, pickedPosition) {
|
|
198
|
+
camera.updateMatrixWorld(true);
|
|
199
|
+
if (pickedPosition == undefined) {
|
|
200
|
+
pickedPosition = view.getPickingPositionFromDepth() || getLookAtFromMath(view, camera);
|
|
201
|
+
}
|
|
202
|
+
const range = pickedPosition && !isNaN(pickedPosition.x) ? camera.position.distanceTo(pickedPosition) : 100;
|
|
203
|
+
camera.localToWorld(targetPosition.set(0, 0, -range));
|
|
204
|
+
this.setFromPositions(view, camera.position);
|
|
205
|
+
}
|
|
206
|
+
copyObject3D(rig) {
|
|
207
|
+
this.copy(rig, false);
|
|
208
|
+
this.seaLevel.copy(rig.seaLevel, false);
|
|
209
|
+
this.target.copy(rig.target, false);
|
|
210
|
+
this.camera.copy(rig.camera);
|
|
211
|
+
return this;
|
|
212
|
+
}
|
|
213
|
+
animateCameraToLookAtTarget(view, camera, params) {
|
|
214
|
+
params.easing = params.easing || TWEEN.Easing.Quartic.InOut;
|
|
215
|
+
this.setfromCamera(view, camera);
|
|
216
|
+
const tweenGroup = new TWEEN.Group();
|
|
217
|
+
this.start = (this.start || new CameraRig()).copyObject3D(this);
|
|
218
|
+
this.end = (this.end || new CameraRig()).copyObject3D(this);
|
|
219
|
+
const time = params.time || 2500;
|
|
220
|
+
const factor = {
|
|
221
|
+
t: 0
|
|
222
|
+
};
|
|
223
|
+
const animations = [];
|
|
224
|
+
const def = deferred();
|
|
225
|
+
this.addPlaceTargetOnGround(view, camera, params.coord, factor);
|
|
226
|
+
this.end.applyParams(view, params);
|
|
227
|
+
// compute the angle along z-axis between the starting position and the end position
|
|
228
|
+
const difference = this.end.target.rotation.z - this.start.target.rotation.z;
|
|
229
|
+
// if that angle is superior to 180°, recompute the rotation as the complementary angle.
|
|
230
|
+
if (Math.abs(difference) > Math.PI) {
|
|
231
|
+
this.end.target.rotation.z = this.start.target.rotation.z + difference - Math.sign(difference) * 2 * Math.PI;
|
|
232
|
+
}
|
|
233
|
+
animations.push(new TWEEN.Tween(factor).to({
|
|
234
|
+
t: 1
|
|
235
|
+
}, time).easing(params.easing).onUpdate(d => {
|
|
236
|
+
// rotate to coord destination in geocentric projection
|
|
237
|
+
if (view.referenceCrs == 'EPSG:4978') {
|
|
238
|
+
this.quaternion.slerpQuaternions(this.start.quaternion, this.end.quaternion, d.t);
|
|
239
|
+
}
|
|
240
|
+
// camera rotation
|
|
241
|
+
this.camera.quaternion.slerpQuaternions(this.start.camera.quaternion, this.end.camera.quaternion, d.t);
|
|
242
|
+
// camera's target rotation
|
|
243
|
+
this.target.rotation.set(0, 0, 0);
|
|
244
|
+
this.target.rotateZ(THREE.MathUtils.lerp(this.start.target.rotation.z, this.end.target.rotation.z, d.t));
|
|
245
|
+
this.target.rotateX(THREE.MathUtils.lerp(this.start.target.rotation.x, this.end.target.rotation.x, d.t));
|
|
246
|
+
}));
|
|
247
|
+
|
|
248
|
+
// translate to coordinate destination in planar projection
|
|
249
|
+
if (view.referenceCrs != 'EPSG:4978') {
|
|
250
|
+
animations.push(new TWEEN.Tween(this.position).to(this.end.position, time).easing(params.easing));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// translate to altitude zero
|
|
254
|
+
animations.push(new TWEEN.Tween(this.seaLevel.position).to(this.end.seaLevel.position, time).easing(params.easing));
|
|
255
|
+
|
|
256
|
+
// translate camera position
|
|
257
|
+
animations.push(new TWEEN.Tween(this.camera.position).to(this.end.camera.position, time).easing(params.easing));
|
|
258
|
+
tweenGroup.add(...animations);
|
|
259
|
+
|
|
260
|
+
// update animations, transformation and view
|
|
261
|
+
this.animationFrameRequester = () => {
|
|
262
|
+
tweenGroup.update();
|
|
263
|
+
this.updateMatrixWorld(true);
|
|
264
|
+
this.applyTransformToCamera(view, camera);
|
|
265
|
+
this.targetWorldPosition.setFromMatrixPosition(this.seaLevel.matrixWorld);
|
|
266
|
+
if (params.callback) {
|
|
267
|
+
params.callback(this);
|
|
268
|
+
}
|
|
269
|
+
targetCoord.crs = view.referenceCrs;
|
|
270
|
+
targetCoord.setFromVector3(this.targetWorldPosition).as(tileLayer(view).extent.crs, this.coord);
|
|
271
|
+
view.notifyChange(camera);
|
|
272
|
+
};
|
|
273
|
+
this.removeAll = function (o) {
|
|
274
|
+
this.removeAll = () => {};
|
|
275
|
+
tweenGroup.removeAll();
|
|
276
|
+
if (this.animationFrameRequester) {
|
|
277
|
+
view.removeFrameRequester(MAIN_LOOP_EVENTS.BEFORE_RENDER, this.animationFrameRequester);
|
|
278
|
+
}
|
|
279
|
+
def.resolve(o !== undefined);
|
|
280
|
+
this.animationFrameRequester = null;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// Waiting last animation complete,
|
|
284
|
+
// we assume that the animation that completes last is the one that was started last
|
|
285
|
+
animations[animations.length - 1].onComplete(this.removeAll);
|
|
286
|
+
animations.forEach(anim => anim.start());
|
|
287
|
+
view.addFrameRequester(MAIN_LOOP_EVENTS.BEFORE_RENDER, this.animationFrameRequester);
|
|
288
|
+
view.notifyChange(camera);
|
|
289
|
+
return def;
|
|
290
|
+
}
|
|
291
|
+
stop(view) {
|
|
292
|
+
this.removePlaceTargetOnGround(view);
|
|
293
|
+
this.removeAll();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// update target position to coordinate's altitude
|
|
297
|
+
addPlaceTargetOnGround(view, camera, coord) {
|
|
298
|
+
let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {
|
|
299
|
+
t: 1.0
|
|
300
|
+
};
|
|
301
|
+
this.removePlaceTargetOnGround(view);
|
|
302
|
+
if (view && camera) {
|
|
303
|
+
const startAltitude = this.target.position.z;
|
|
304
|
+
this.placeTargetOnGround = () => {
|
|
305
|
+
const altitude = Math.max(0, DEMUtils.getElevationValueAt(tileLayer(view), coord || this.coord, DEMUtils.PRECISE_READ_Z) || 0);
|
|
306
|
+
this.target.position.z = startAltitude * (1.0 - options.t) + altitude * options.t;
|
|
307
|
+
this.target.updateMatrixWorld(true);
|
|
308
|
+
this.applyTransformToCamera(view, camera);
|
|
309
|
+
};
|
|
310
|
+
this.placeTargetOnGround();
|
|
311
|
+
view.addFrameRequester(MAIN_LOOP_EVENTS.BEFORE_RENDER, this.placeTargetOnGround);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
removePlaceTargetOnGround(view) {
|
|
315
|
+
if (view && this.placeTargetOnGround) {
|
|
316
|
+
view.removeFrameRequester(MAIN_LOOP_EVENTS.BEFORE_RENDER, this.placeTargetOnGround);
|
|
317
|
+
this.placeTargetOnGround = null;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
get tilt() {
|
|
321
|
+
return THREE.MathUtils.radToDeg(this.target.rotation.x);
|
|
322
|
+
}
|
|
323
|
+
get heading() {
|
|
324
|
+
return -wrapTo180(THREE.MathUtils.radToDeg(this.target.rotation.z) + 180);
|
|
325
|
+
}
|
|
326
|
+
get range() {
|
|
327
|
+
return this.camera.position.y;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
export function getRig(camera) {
|
|
331
|
+
rigs[camera.uuid] = rigs[camera.uuid] || new CameraRig();
|
|
332
|
+
return rigs[camera.uuid];
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* @module CameraUtils
|
|
337
|
+
*/
|
|
338
|
+
export default {
|
|
339
|
+
/**
|
|
340
|
+
* @typedef {Object} CameraTransformOptions
|
|
341
|
+
* @property {Coordinate} [coord=currentCoordinate] Camera look at geographic coordinate
|
|
342
|
+
* @property {Number} [tilt=currentTilt] camera's tilt, in degree
|
|
343
|
+
* @property {Number} [heading=currentHeading] camera's heading, in degree
|
|
344
|
+
* @property {Number} [range=currentRange] camera distance to target coordinate, in meter
|
|
345
|
+
* @property {Number} [time=2500] duration of the animation, in ms
|
|
346
|
+
* @property {boolean} [proxy=true] use proxy to handling camera's transformation. if proxy == true, other camera's transformation stops rig's transformation
|
|
347
|
+
* @property {Number} [easing=TWEEN.Easing.Quartic.InOut] in and out easing animation
|
|
348
|
+
* @property {function} [callback] callback call each animation's frame (params are current cameraTransform and worldTargetPosition)
|
|
349
|
+
* @property {boolean} [stopPlaceOnGroundAtEnd=false] stop place target on the ground at animation ending
|
|
350
|
+
*/
|
|
351
|
+
/**
|
|
352
|
+
* Default value for option to stop place target
|
|
353
|
+
* on the ground at animation ending.
|
|
354
|
+
* Default value is false.
|
|
355
|
+
*/
|
|
356
|
+
defaultStopPlaceOnGroundAtEnd: false,
|
|
357
|
+
Easing: TWEEN.Easing,
|
|
358
|
+
/**
|
|
359
|
+
* Stop camera's animation
|
|
360
|
+
*
|
|
361
|
+
* @param {View} view The camera view
|
|
362
|
+
* @param {Camera} camera The camera to stop animation
|
|
363
|
+
*/
|
|
364
|
+
stop(view, camera) {
|
|
365
|
+
getRig(camera).stop(view);
|
|
366
|
+
},
|
|
367
|
+
/**
|
|
368
|
+
* Gets the current parameters transform camera looking at target.
|
|
369
|
+
*
|
|
370
|
+
* @param {View} view The camera view
|
|
371
|
+
* @param {Camera} camera The camera to get transform
|
|
372
|
+
* @param {THREE.Vector3} [target] - The optional target
|
|
373
|
+
* @return {CameraUtils~CameraTransformOptions} The transform camera looking at target
|
|
374
|
+
*/
|
|
375
|
+
getTransformCameraLookingAtTarget(view, camera, target) {
|
|
376
|
+
const rig = getRig(camera);
|
|
377
|
+
rig.setfromCamera(view, camera, target);
|
|
378
|
+
return rig.getParams();
|
|
379
|
+
},
|
|
380
|
+
/**
|
|
381
|
+
* Apply transform to camera
|
|
382
|
+
*
|
|
383
|
+
* @param {View} view The camera view
|
|
384
|
+
* @param {Camera} camera The camera to transform
|
|
385
|
+
* @param {CameraUtils~CameraTransformOptions|Extent} params The parameters
|
|
386
|
+
* @return {Promise} promise with resolve final CameraUtils~CameraTransformOptions
|
|
387
|
+
*/
|
|
388
|
+
transformCameraToLookAtTarget(view, camera) {
|
|
389
|
+
let params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
390
|
+
if (params.isExtent) {
|
|
391
|
+
params = this.getCameraTransformOptionsFromExtent(view, camera, params);
|
|
392
|
+
}
|
|
393
|
+
params.proxy = params.proxy === undefined || params.proxy;
|
|
394
|
+
const rig = getRig(camera);
|
|
395
|
+
rig.stop(view);
|
|
396
|
+
rig.setfromCamera(view, camera);
|
|
397
|
+
if (params.proxy) {
|
|
398
|
+
rig.setProxy(view, camera);
|
|
399
|
+
}
|
|
400
|
+
rig.applyParams(view, params);
|
|
401
|
+
rig.addPlaceTargetOnGround(view, camera, params.coord);
|
|
402
|
+
rig.applyTransformToCamera(view, camera);
|
|
403
|
+
view.notifyChange(camera);
|
|
404
|
+
return Promise.resolve(rig.getParams());
|
|
405
|
+
},
|
|
406
|
+
/**
|
|
407
|
+
* Compute the CameraTransformOptions that allow a given camera to display a given extent in its entirety.
|
|
408
|
+
*
|
|
409
|
+
* @param {View} view The camera view
|
|
410
|
+
* @param {THREE.Camera} camera The camera to get the CameraTransformOptions from
|
|
411
|
+
* @param {Extent} extent The extent the camera must display
|
|
412
|
+
*
|
|
413
|
+
* @return {CameraUtils~CameraTransformOptions} The CameraTransformOptions allowing camera to display the extent.
|
|
414
|
+
*/
|
|
415
|
+
getCameraTransformOptionsFromExtent(view, camera, extent) {
|
|
416
|
+
const cameraTransformOptions = {
|
|
417
|
+
coord: new Coordinates(extent.crs, 0, 0, 0),
|
|
418
|
+
heading: 0,
|
|
419
|
+
tilt: view.isPlanarView ? 90 : 89.9
|
|
420
|
+
};
|
|
421
|
+
let dimensions;
|
|
422
|
+
if (view.isGlobeView) {
|
|
423
|
+
extent = extent.as('EPSG:4326');
|
|
424
|
+
// compute extent's bounding box dimensions
|
|
425
|
+
obb.setFromExtent(extent);
|
|
426
|
+
// /!\ WARNING x and y are inverted, see issue #XXXX
|
|
427
|
+
obb.box3D.getSize(size);
|
|
428
|
+
dimensions = {
|
|
429
|
+
x: size.y,
|
|
430
|
+
y: size.x
|
|
431
|
+
};
|
|
432
|
+
} else {
|
|
433
|
+
extent = extent.as(view.referenceCrs);
|
|
434
|
+
dimensions = extent.planarDimensions();
|
|
435
|
+
}
|
|
436
|
+
extent.center(cameraTransformOptions.coord);
|
|
437
|
+
if (camera.isOrthographicCamera) {
|
|
438
|
+
// setup camera zoom
|
|
439
|
+
if (dimensions.x / dimensions.y > camera.aspect) {
|
|
440
|
+
camera.zoom = (camera.right - camera.left) / dimensions.x;
|
|
441
|
+
} else {
|
|
442
|
+
camera.zoom = (camera.top - camera.bottom) / dimensions.y;
|
|
443
|
+
}
|
|
444
|
+
camera.updateProjectionMatrix();
|
|
445
|
+
|
|
446
|
+
// setup camera placement
|
|
447
|
+
cameraTransformOptions.range = 1000;
|
|
448
|
+
} else if (camera.isPerspectiveCamera) {
|
|
449
|
+
// setup range for camera placement
|
|
450
|
+
const verticalFOV = THREE.MathUtils.degToRad(camera.fov);
|
|
451
|
+
if (dimensions.x / dimensions.y > camera.aspect) {
|
|
452
|
+
const focal = view.domElement.clientHeight * 0.5 / Math.tan(verticalFOV * 0.5);
|
|
453
|
+
const horizontalFOV = 2 * Math.atan(view.domElement.clientWidth * 0.5 / focal);
|
|
454
|
+
cameraTransformOptions.range = dimensions.x / (2 * Math.tan(horizontalFOV * 0.5));
|
|
455
|
+
} else {
|
|
456
|
+
cameraTransformOptions.range = dimensions.y / (2 * Math.tan(verticalFOV * 0.5));
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return cameraTransformOptions;
|
|
460
|
+
},
|
|
461
|
+
/**
|
|
462
|
+
* Apply transform to camera with animation
|
|
463
|
+
*
|
|
464
|
+
* @param {View} view The camera view
|
|
465
|
+
* @param {Camera} camera The camera to animate
|
|
466
|
+
* @param {CameraUtils~CameraTransformOptions} params The parameters
|
|
467
|
+
* @return {Promise} promise with resolve final CameraUtils~CameraTransformOptions
|
|
468
|
+
*/
|
|
469
|
+
animateCameraToLookAtTarget(view, camera) {
|
|
470
|
+
let params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
471
|
+
params.proxy = params.proxy === undefined || params.proxy;
|
|
472
|
+
const rig = getRig(camera);
|
|
473
|
+
rig.stop(view);
|
|
474
|
+
if (params.proxy) {
|
|
475
|
+
rig.setProxy(view, camera);
|
|
476
|
+
}
|
|
477
|
+
return rig.animateCameraToLookAtTarget(view, camera, params).promise.then(finished => {
|
|
478
|
+
const stopPlaceOnGround = params.stopPlaceOnGroundAtEnd === undefined ? this.defaultStopPlaceOnGroundAtEnd : params.stopPlaceOnGroundAtEnd;
|
|
479
|
+
const newTransformation = rig.getParams();
|
|
480
|
+
if (stopPlaceOnGround) {
|
|
481
|
+
rig.stop(view);
|
|
482
|
+
}
|
|
483
|
+
newTransformation.finished = finished;
|
|
484
|
+
return newTransformation;
|
|
485
|
+
});
|
|
486
|
+
},
|
|
487
|
+
/**
|
|
488
|
+
* chain animation transform to camera
|
|
489
|
+
*
|
|
490
|
+
* @param {View} view The camera view
|
|
491
|
+
* @param {Camera} camera The camera to animate
|
|
492
|
+
* @param {CameraUtils~CameraTransformOptions[]} params array parameters, each parameters transforms are apply to camera, in serial
|
|
493
|
+
* @return {Promise} promise with resolve final CameraUtils~CameraTransformOptions
|
|
494
|
+
*/
|
|
495
|
+
sequenceAnimationsToLookAtTarget(view, camera) {
|
|
496
|
+
let params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [{}];
|
|
497
|
+
// convert each param to a function
|
|
498
|
+
const funcs = params.map(param => () => this.animateCameraToLookAtTarget(view, camera, param));
|
|
499
|
+
|
|
500
|
+
// execute Promises in serial
|
|
501
|
+
return (funcs => funcs.reduce((promise, func) => promise.then(result => {
|
|
502
|
+
const finished = result.length ? result[result.length - 1].finished : true;
|
|
503
|
+
if (finished) {
|
|
504
|
+
return func().then(Array.prototype.concat.bind(result));
|
|
505
|
+
} else {
|
|
506
|
+
return Promise.resolve([{
|
|
507
|
+
finished: false
|
|
508
|
+
}]);
|
|
509
|
+
}
|
|
510
|
+
}), Promise.resolve([])))(funcs);
|
|
511
|
+
},
|
|
512
|
+
/**
|
|
513
|
+
* Gets the difference camera transformation
|
|
514
|
+
*
|
|
515
|
+
* @param {CameraUtils~CameraTransformOptions} first param to compare with the second
|
|
516
|
+
* @param {CameraUtils~CameraTransformOptions} second param to compare with the first
|
|
517
|
+
* @return {object} The difference parameters
|
|
518
|
+
*/
|
|
519
|
+
getDiffParams(first, second) {
|
|
520
|
+
if (!first || !second) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
let diff;
|
|
524
|
+
if (Math.abs(first.range - second.range) / first.range > 0.001) {
|
|
525
|
+
diff = diff || {};
|
|
526
|
+
diff.range = {
|
|
527
|
+
previous: first.range,
|
|
528
|
+
new: second.range
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
if (Math.abs(first.tilt - second.tilt) > 0.1) {
|
|
532
|
+
diff = diff || {};
|
|
533
|
+
diff.tilt = {
|
|
534
|
+
previous: first.tilt,
|
|
535
|
+
new: second.tilt
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
if (Math.abs(first.heading - second.heading) > 0.1) {
|
|
539
|
+
diff = diff || {};
|
|
540
|
+
diff.heading = {
|
|
541
|
+
previous: first.heading,
|
|
542
|
+
new: second.heading
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
if (Math.abs(first.coord.x - second.coord.x) > 0.000001 || Math.abs(first.coord.y - second.coord.y) > 0.000001) {
|
|
546
|
+
diff = diff || {};
|
|
547
|
+
diff.coord = {
|
|
548
|
+
previous: first.coord,
|
|
549
|
+
new: second.coord
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
return diff;
|
|
553
|
+
}
|
|
554
|
+
};
|