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.
Files changed (185) hide show
  1. package/dist/455.js +2 -0
  2. package/dist/455.js.map +1 -0
  3. package/dist/debug.js +3 -0
  4. package/dist/debug.js.LICENSE.txt +13 -0
  5. package/dist/debug.js.map +1 -0
  6. package/dist/itowns.js +3 -0
  7. package/dist/itowns.js.LICENSE.txt +5 -0
  8. package/dist/itowns.js.map +1 -0
  9. package/dist/itowns_lasparser.js +2 -0
  10. package/dist/itowns_lasparser.js.map +1 -0
  11. package/dist/itowns_lasworker.js +2 -0
  12. package/dist/itowns_lasworker.js.map +1 -0
  13. package/dist/itowns_potree2worker.js +2 -0
  14. package/dist/itowns_potree2worker.js.map +1 -0
  15. package/dist/itowns_widgets.js +2 -0
  16. package/dist/itowns_widgets.js.map +1 -0
  17. package/lib/Controls/FirstPersonControls.js +308 -0
  18. package/lib/Controls/FlyControls.js +175 -0
  19. package/lib/Controls/GlobeControls.js +1178 -0
  20. package/lib/Controls/PlanarControls.js +1025 -0
  21. package/lib/Controls/StateControl.js +432 -0
  22. package/lib/Controls/StreetControls.js +392 -0
  23. package/lib/Converter/Feature2Mesh.js +612 -0
  24. package/lib/Converter/Feature2Texture.js +174 -0
  25. package/lib/Converter/convertToTile.js +70 -0
  26. package/lib/Converter/textureConverter.js +43 -0
  27. package/lib/Core/3DTiles/C3DTBatchTable.js +131 -0
  28. package/lib/Core/3DTiles/C3DTBatchTableHierarchyExtension.js +96 -0
  29. package/lib/Core/3DTiles/C3DTBoundingVolume.js +156 -0
  30. package/lib/Core/3DTiles/C3DTExtensions.js +97 -0
  31. package/lib/Core/3DTiles/C3DTFeature.js +110 -0
  32. package/lib/Core/3DTiles/C3DTilesEnums.js +20 -0
  33. package/lib/Core/3DTiles/C3DTileset.js +99 -0
  34. package/lib/Core/3DTiles/utils/BinaryPropertyAccessor.js +100 -0
  35. package/lib/Core/AnimationPlayer.js +142 -0
  36. package/lib/Core/CopcNode.js +174 -0
  37. package/lib/Core/Deprecated/Undeprecator.js +74 -0
  38. package/lib/Core/EntwinePointTileNode.js +126 -0
  39. package/lib/Core/Feature.js +488 -0
  40. package/lib/Core/Geographic/GeoidGrid.js +108 -0
  41. package/lib/Core/Label.js +222 -0
  42. package/lib/Core/MainLoop.js +209 -0
  43. package/lib/Core/Picking.js +255 -0
  44. package/lib/Core/PointCloudNode.js +42 -0
  45. package/lib/Core/Potree2Node.js +206 -0
  46. package/lib/Core/Potree2PointAttributes.js +139 -0
  47. package/lib/Core/PotreeNode.js +101 -0
  48. package/lib/Core/Prefab/Globe/Atmosphere.js +293 -0
  49. package/lib/Core/Prefab/Globe/GlobeLayer.js +152 -0
  50. package/lib/Core/Prefab/Globe/GlobeTileBuilder.js +110 -0
  51. package/lib/Core/Prefab/Globe/SkyShader.js +78 -0
  52. package/lib/Core/Prefab/GlobeView.js +155 -0
  53. package/lib/Core/Prefab/Planar/PlanarLayer.js +59 -0
  54. package/lib/Core/Prefab/Planar/PlanarTileBuilder.js +71 -0
  55. package/lib/Core/Prefab/PlanarView.js +62 -0
  56. package/lib/Core/Prefab/TileBuilder.js +82 -0
  57. package/lib/Core/Prefab/computeBufferTileGeometry.js +248 -0
  58. package/lib/Core/Scheduler/Cache.js +17 -0
  59. package/lib/Core/Scheduler/CancelledCommandException.js +15 -0
  60. package/lib/Core/Scheduler/Scheduler.js +294 -0
  61. package/lib/Core/Style.js +660 -0
  62. package/lib/Core/StyleOptions.js +486 -0
  63. package/lib/Core/System/Capabilities.js +63 -0
  64. package/lib/Core/Tile/Tile.js +205 -0
  65. package/lib/Core/Tile/TileGrid.js +49 -0
  66. package/lib/Core/TileGeometry.js +124 -0
  67. package/lib/Core/TileMesh.js +108 -0
  68. package/lib/Core/View.js +1115 -0
  69. package/lib/Layer/C3DTilesLayer.js +459 -0
  70. package/lib/Layer/ColorLayer.js +154 -0
  71. package/lib/Layer/CopcLayer.js +63 -0
  72. package/lib/Layer/ElevationLayer.js +139 -0
  73. package/lib/Layer/EntwinePointTileLayer.js +71 -0
  74. package/lib/Layer/FeatureGeometryLayer.js +77 -0
  75. package/lib/Layer/GeoidLayer.js +80 -0
  76. package/lib/Layer/GeometryLayer.js +233 -0
  77. package/lib/Layer/InfoLayer.js +64 -0
  78. package/lib/Layer/LabelLayer.js +469 -0
  79. package/lib/Layer/Layer.js +335 -0
  80. package/lib/Layer/LayerUpdateState.js +89 -0
  81. package/lib/Layer/LayerUpdateStrategy.js +80 -0
  82. package/lib/Layer/OGC3DTilesLayer.js +543 -0
  83. package/lib/Layer/OrientedImageLayer.js +227 -0
  84. package/lib/Layer/PointCloudLayer.js +405 -0
  85. package/lib/Layer/Potree2Layer.js +171 -0
  86. package/lib/Layer/PotreeLayer.js +72 -0
  87. package/lib/Layer/RasterLayer.js +37 -0
  88. package/lib/Layer/ReferencingLayerProperties.js +62 -0
  89. package/lib/Layer/TiledGeometryLayer.js +459 -0
  90. package/lib/Loader/LASLoader.js +193 -0
  91. package/lib/Loader/Potree2BrotliLoader.js +261 -0
  92. package/lib/Loader/Potree2Loader.js +207 -0
  93. package/lib/Main.js +113 -0
  94. package/lib/MainBundle.js +4 -0
  95. package/lib/Parser/B3dmParser.js +174 -0
  96. package/lib/Parser/CameraCalibrationParser.js +94 -0
  97. package/lib/Parser/GDFParser.js +72 -0
  98. package/lib/Parser/GTXParser.js +75 -0
  99. package/lib/Parser/GeoJsonParser.js +212 -0
  100. package/lib/Parser/GpxParser.js +25 -0
  101. package/lib/Parser/ISGParser.js +71 -0
  102. package/lib/Parser/KMLParser.js +25 -0
  103. package/lib/Parser/LASParser.js +137 -0
  104. package/lib/Parser/MapBoxUrlParser.js +83 -0
  105. package/lib/Parser/PntsParser.js +131 -0
  106. package/lib/Parser/Potree2BinParser.js +92 -0
  107. package/lib/Parser/PotreeBinParser.js +106 -0
  108. package/lib/Parser/PotreeCinParser.js +29 -0
  109. package/lib/Parser/ShapefileParser.js +78 -0
  110. package/lib/Parser/VectorTileParser.js +215 -0
  111. package/lib/Parser/XbilParser.js +120 -0
  112. package/lib/Parser/deprecated/LegacyGLTFLoader.js +1386 -0
  113. package/lib/Parser/iGLTFLoader.js +168 -0
  114. package/lib/Process/3dTilesProcessing.js +304 -0
  115. package/lib/Process/FeatureProcessing.js +76 -0
  116. package/lib/Process/LayeredMaterialNodeProcessing.js +229 -0
  117. package/lib/Process/ObjectRemovalHelper.js +97 -0
  118. package/lib/Process/handlerNodeError.js +23 -0
  119. package/lib/Provider/3dTilesProvider.js +149 -0
  120. package/lib/Provider/DataSourceProvider.js +24 -0
  121. package/lib/Provider/Fetcher.js +233 -0
  122. package/lib/Provider/PointCloudProvider.js +45 -0
  123. package/lib/Provider/TileProvider.js +16 -0
  124. package/lib/Provider/URLBuilder.js +116 -0
  125. package/lib/Renderer/Camera.js +281 -0
  126. package/lib/Renderer/Color.js +56 -0
  127. package/lib/Renderer/ColorLayersOrdering.js +115 -0
  128. package/lib/Renderer/CommonMaterial.js +31 -0
  129. package/lib/Renderer/Label2DRenderer.js +192 -0
  130. package/lib/Renderer/LayeredMaterial.js +243 -0
  131. package/lib/Renderer/OBB.js +150 -0
  132. package/lib/Renderer/OrientedImageCamera.js +118 -0
  133. package/lib/Renderer/OrientedImageMaterial.js +167 -0
  134. package/lib/Renderer/PointsMaterial.js +485 -0
  135. package/lib/Renderer/RasterTile.js +243 -0
  136. package/lib/Renderer/RenderMode.js +31 -0
  137. package/lib/Renderer/Shader/ShaderChunk.js +160 -0
  138. package/lib/Renderer/Shader/ShaderUtils.js +47 -0
  139. package/lib/Renderer/SphereHelper.js +17 -0
  140. package/lib/Renderer/WebXR.js +51 -0
  141. package/lib/Renderer/c3DEngine.js +214 -0
  142. package/lib/Source/C3DTilesGoogleSource.js +74 -0
  143. package/lib/Source/C3DTilesIonSource.js +54 -0
  144. package/lib/Source/C3DTilesSource.js +30 -0
  145. package/lib/Source/CopcSource.js +126 -0
  146. package/lib/Source/EntwinePointTileSource.js +72 -0
  147. package/lib/Source/FileSource.js +188 -0
  148. package/lib/Source/OGC3DTilesGoogleSource.js +29 -0
  149. package/lib/Source/OGC3DTilesIonSource.js +34 -0
  150. package/lib/Source/OGC3DTilesSource.js +21 -0
  151. package/lib/Source/OrientedImageSource.js +59 -0
  152. package/lib/Source/Potree2Source.js +167 -0
  153. package/lib/Source/PotreeSource.js +82 -0
  154. package/lib/Source/Source.js +202 -0
  155. package/lib/Source/TMSSource.js +144 -0
  156. package/lib/Source/VectorTilesSource.js +182 -0
  157. package/lib/Source/WFSSource.js +170 -0
  158. package/lib/Source/WMSSource.js +167 -0
  159. package/lib/Source/WMTSSource.js +92 -0
  160. package/lib/ThreeExtended/capabilities/WebGL.js +69 -0
  161. package/lib/ThreeExtended/libs/ktx-parse.module.js +506 -0
  162. package/lib/ThreeExtended/libs/zstddec.module.js +29 -0
  163. package/lib/ThreeExtended/loaders/DDSLoader.js +200 -0
  164. package/lib/ThreeExtended/loaders/DRACOLoader.js +400 -0
  165. package/lib/ThreeExtended/loaders/GLTFLoader.js +2879 -0
  166. package/lib/ThreeExtended/loaders/KTX2Loader.js +709 -0
  167. package/lib/ThreeExtended/math/ColorSpaces.js +59 -0
  168. package/lib/ThreeExtended/utils/BufferGeometryUtils.js +846 -0
  169. package/lib/ThreeExtended/utils/WorkerPool.js +70 -0
  170. package/lib/Utils/CameraUtils.js +554 -0
  171. package/lib/Utils/DEMUtils.js +350 -0
  172. package/lib/Utils/FeaturesUtils.js +156 -0
  173. package/lib/Utils/Gradients.js +16 -0
  174. package/lib/Utils/ThreeUtils.js +115 -0
  175. package/lib/Utils/gui/C3DTilesStyle.js +218 -0
  176. package/lib/Utils/gui/Main.js +7 -0
  177. package/lib/Utils/gui/Minimap.js +152 -0
  178. package/lib/Utils/gui/Navigation.js +245 -0
  179. package/lib/Utils/gui/Scale.js +104 -0
  180. package/lib/Utils/gui/Searchbar.js +234 -0
  181. package/lib/Utils/gui/Widget.js +80 -0
  182. package/lib/Utils/placeObjectOnGround.js +136 -0
  183. package/lib/Worker/LASLoaderWorker.js +19 -0
  184. package/lib/Worker/Potree2Worker.js +21 -0
  185. package/package.json +2 -2
@@ -0,0 +1,1025 @@
1
+ import * as THREE from 'three';
2
+ import { MAIN_LOOP_EVENTS } from "../Core/MainLoop.js";
3
+
4
+ // event keycode
5
+ export const keys = {
6
+ CTRL: 17,
7
+ SPACE: 32,
8
+ T: 84,
9
+ Y: 89
10
+ };
11
+ const mouseButtons = {
12
+ LEFTCLICK: THREE.MOUSE.LEFT,
13
+ MIDDLECLICK: THREE.MOUSE.MIDDLE,
14
+ RIGHTCLICK: THREE.MOUSE.RIGHT
15
+ };
16
+ let currentPressedButton;
17
+
18
+ // starting camera position and orientation target
19
+ const startPosition = new THREE.Vector3();
20
+ const startQuaternion = new THREE.Quaternion();
21
+ // camera initial zoom value if orthographic
22
+ let cameraInitialZoom = 0;
23
+
24
+ // point under the cursor
25
+ const pointUnderCursor = new THREE.Vector3();
26
+
27
+ // control state
28
+ export const STATE = {
29
+ NONE: -1,
30
+ DRAG: 0,
31
+ PAN: 1,
32
+ ROTATE: 2,
33
+ TRAVEL: 3,
34
+ ORTHO_ZOOM: 4
35
+ };
36
+
37
+ // cursor shape linked to control state
38
+ const cursor = {
39
+ default: 'auto',
40
+ drag: 'move',
41
+ pan: 'cell',
42
+ travel: 'wait',
43
+ rotate: 'move',
44
+ ortho_zoom: 'wait'
45
+ };
46
+ const vectorZero = new THREE.Vector3();
47
+
48
+ // mouse movement
49
+ const mousePosition = new THREE.Vector2();
50
+ const lastMousePosition = new THREE.Vector2();
51
+ const deltaMousePosition = new THREE.Vector2(0, 0);
52
+
53
+ // drag movement
54
+ const dragStart = new THREE.Vector3();
55
+ const dragEnd = new THREE.Vector3();
56
+ const dragDelta = new THREE.Vector3();
57
+
58
+ // camera focus point : ground point at screen center
59
+ const centerPoint = new THREE.Vector3(0, 0, 0);
60
+
61
+ // camera rotation
62
+ let phi = 0.0;
63
+
64
+ // displacement and rotation vectors
65
+ const vect = new THREE.Vector3();
66
+ const quat = new THREE.Quaternion();
67
+ const vect2 = new THREE.Vector2();
68
+
69
+ // animated travel
70
+ const travelEndPos = new THREE.Vector3();
71
+ const travelStartPos = new THREE.Vector3();
72
+ const travelStartRot = new THREE.Quaternion();
73
+ const travelEndRot = new THREE.Quaternion();
74
+ let travelAlpha = 0;
75
+ let travelDuration = 0;
76
+ let travelUseRotation = false;
77
+ let travelUseSmooth = false;
78
+
79
+ // zoom changes (for orthographic camera)
80
+ let startZoom = 0;
81
+ let endZoom = 0;
82
+
83
+ // ray caster for drag movement
84
+ const rayCaster = new THREE.Raycaster();
85
+ const plane = new THREE.Plane(new THREE.Vector3(0, 0, -1));
86
+
87
+ // default parameters :
88
+ const defaultOptions = {
89
+ enabled: true,
90
+ enableRotation: true,
91
+ rotateSpeed: 2.0,
92
+ minPanSpeed: 0.05,
93
+ maxPanSpeed: 15,
94
+ zoomTravelTime: 0.2,
95
+ // must be a number
96
+ zoomFactor: 2,
97
+ maxResolution: 1 / Infinity,
98
+ minResolution: Infinity,
99
+ maxAltitude: 50000000,
100
+ groundLevel: 200,
101
+ autoTravelTimeMin: 1.5,
102
+ autoTravelTimeMax: 4,
103
+ autoTravelTimeDist: 50000,
104
+ smartTravelHeightMin: 75,
105
+ smartTravelHeightMax: 500,
106
+ instantTravel: false,
107
+ minZenithAngle: 0,
108
+ maxZenithAngle: 82.5,
109
+ handleCollision: true,
110
+ minDistanceCollision: 30,
111
+ enableSmartTravel: true,
112
+ enablePan: true
113
+ };
114
+ export const PLANAR_CONTROL_EVENT = {
115
+ MOVED: 'moved'
116
+ };
117
+
118
+ /**
119
+ * Planar controls is a camera controller adapted for a planar view, with animated movements.
120
+ * Usage is as follow :
121
+ * <ul>
122
+ * <li><b>Left mouse button:</b> drag the camera (translation on the (xy) world plane).</li>
123
+ * <li><b>Right mouse button:</b> pan the camera (translation on the vertical (z) axis of the world plane).</li>
124
+ * <li><b>CTRL + Left mouse button:</b> rotate the camera around the focus point.</li>
125
+ * <li><b>Wheel scrolling:</b> zoom toward the cursor position.</li>
126
+ * <li><b>Wheel clicking:</b> smart zoom toward the cursor position (animated).</li>
127
+ * <li><b>Y key:</b> go to the starting view (animated).</li>
128
+ * <li><b>T key:</b> go to the top view (animated).</li>
129
+ * </ul>
130
+ *
131
+ * @class PlanarControls
132
+ * @param {PlanarView} view the view where the controls will be used
133
+ * @param {object} options
134
+ * @param {boolean} [options.enabled=true] Set to false to disable this control
135
+ * @param {boolean} [options.enableRotation=true] Enable the rotation with the `CTRL + Left mouse button`
136
+ * and in animations, like the smart zoom.
137
+ * @param {boolean} [options.enableSmartTravel=true] Enable smart travel with the `wheel-click / space-bar`.
138
+ * @param {boolean} [options.enablePan=true] Enable pan movements with the `right-click`.
139
+ * @param {number} [options.rotateSpeed=2.0] Rotate speed.
140
+ * @param {number} [options.maxPanSpeed=15] Pan speed when close to maxAltitude.
141
+ * @param {number} [options.minPanSpeed=0.05] Pan speed when close to the ground.
142
+ * @param {number} [options.zoomTravelTime=0.2] Animation time when zooming.
143
+ * @param {number} [options.zoomFactor=2] The factor the scale is multiplied by when zooming
144
+ * in and divided by when zooming out. This factor can't be null.
145
+ * @param {number} [options.maxResolution=0] The smallest size in meters a pixel at the center of the
146
+ * view can represent.
147
+ * @param {number} [options.minResolution=Infinity] The biggest size in meters a pixel at the center of the
148
+ * view can represent.
149
+ * @param {number} [options.maxAltitude=12000] Maximum altitude reachable when panning or zooming out.
150
+ * @param {number} [options.groundLevel=200] Minimum altitude reachable when panning.
151
+ * @param {number} [options.autoTravelTimeMin=1.5] Minimum duration for animated travels with the `auto`
152
+ * parameter.
153
+ * @param {number} [options.autoTravelTimeMax=4] Maximum duration for animated travels with the `auto`
154
+ * parameter.
155
+ * @param {number} [options.autoTravelTimeDist=20000] Maximum travel distance for animated travel with the
156
+ * `auto` parameter.
157
+ * @param {number} [options.smartTravelHeightMin=75] Minimum height above ground reachable after a smart
158
+ * travel.
159
+ * @param {number} [options.smartTravelHeightMax=500] Maximum height above ground reachable after a smart
160
+ * travel.
161
+ * @param {boolean} [options.instantTravel=false] If set to true, animated travels will have no duration.
162
+ * @param {number} [options.minZenithAngle=0] The minimum reachable zenith angle for a camera
163
+ * rotation, in degrees.
164
+ * @param {number} [options.maxZenithAngle=82.5] The maximum reachable zenith angle for a camera
165
+ * rotation, in degrees.
166
+ * @param {boolean} [options.handleCollision=true]
167
+ */
168
+ class PlanarControls extends THREE.EventDispatcher {
169
+ constructor(view) {
170
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
171
+ super();
172
+ this.view = view;
173
+ this.camera = view.camera3D;
174
+
175
+ // Set to false to disable this control
176
+ this.enabled = typeof options.enabled == 'boolean' ? options.enabled : defaultOptions.enabled;
177
+ if (this.camera.isOrthographicCamera) {
178
+ cameraInitialZoom = this.camera.zoom;
179
+
180
+ // enable rotation movements
181
+ this.enableRotation = false;
182
+
183
+ // enable pan movements
184
+ this.enablePan = false;
185
+
186
+ // Camera altitude is clamped under maxAltitude.
187
+ // This is not relevant for an orthographic camera (since the orthographic camera altitude won't change).
188
+ // Therefore, neutralizing by default the maxAltitude limit allows zooming out with an orthographic camera,
189
+ // no matter its initial position.
190
+ this.maxAltitude = Infinity;
191
+
192
+ // the zoom travel time (stored in `this.zoomTravelTime`) can't be `auto` with an orthographic camera
193
+ this.zoomTravelTime = typeof options.zoomTravelTime === 'number' ? options.zoomTravelTime : defaultOptions.zoomTravelTime;
194
+ } else {
195
+ // enable rotation movements
196
+ this.enableRotation = options.enableRotation === undefined ? defaultOptions.enableRotation : options.enableRotation;
197
+ this.rotateSpeed = options.rotateSpeed || defaultOptions.rotateSpeed;
198
+
199
+ // enable pan movements
200
+ this.enablePan = options.enablePan === undefined ? defaultOptions.enablePan : options.enablePan;
201
+
202
+ // minPanSpeed when close to the ground, maxPanSpeed when close to maxAltitude
203
+ this.minPanSpeed = options.minPanSpeed || defaultOptions.minPanSpeed;
204
+ this.maxPanSpeed = options.maxPanSpeed || defaultOptions.maxPanSpeed;
205
+
206
+ // camera altitude is clamped under maxAltitude
207
+ this.maxAltitude = options.maxAltitude || defaultOptions.maxAltitude;
208
+
209
+ // animation duration for the zoom
210
+ this.zoomTravelTime = options.zoomTravelTime || defaultOptions.zoomTravelTime;
211
+ }
212
+
213
+ // zoom movement is equal to the distance to the zoom target, multiplied by zoomFactor
214
+ if (options.zoomInFactor) {
215
+ console.warn('Controls zoomInFactor parameter is deprecated. Use zoomFactor instead.');
216
+ options.zoomFactor = options.zoomFactor || options.zoomInFactor;
217
+ }
218
+ if (options.zoomOutFactor) {
219
+ console.warn('Controls zoomOutFactor parameter is deprecated. Use zoomFactor instead.');
220
+ options.zoomFactor = options.zoomFactor || options.zoomInFactor || 1 / options.zoomOutFactor;
221
+ }
222
+ if (options.zoomFactor === 0) {
223
+ console.warn('Controls zoomFactor parameter can not be equal to 0. Its value will be set to default.');
224
+ options.zoomFactor = defaultOptions.zoomFactor;
225
+ }
226
+ this.zoomInFactor = options.zoomFactor || defaultOptions.zoomFactor;
227
+ this.zoomOutFactor = 1 / (options.zoomFactor || defaultOptions.zoomFactor);
228
+
229
+ // the maximum and minimum size (in meters) a pixel at the center of the view can represent
230
+ this.maxResolution = options.maxResolution || defaultOptions.maxResolution;
231
+ this.minResolution = options.minResolution || defaultOptions.minResolution;
232
+
233
+ // approximate ground altitude value. Camera altitude is clamped above groundLevel
234
+ this.groundLevel = options.groundLevel || defaultOptions.groundLevel;
235
+
236
+ // min and max duration in seconds, for animated travels with `auto` parameter
237
+ this.autoTravelTimeMin = options.autoTravelTimeMin || defaultOptions.autoTravelTimeMin;
238
+ this.autoTravelTimeMax = options.autoTravelTimeMax || defaultOptions.autoTravelTimeMax;
239
+
240
+ // max travel duration is reached for this travel distance (empirical smoothing value)
241
+ this.autoTravelTimeDist = options.autoTravelTimeDist || defaultOptions.autoTravelTimeDist;
242
+
243
+ // after a smartZoom, camera height above ground will be between these two values
244
+ if (options.smartZoomHeightMin) {
245
+ console.warn('Controls smartZoomHeightMin parameter is deprecated. Use smartTravelHeightMin instead.');
246
+ options.smartTravelHeightMin = options.smartTravelHeightMin || options.smartZoomHeightMin;
247
+ }
248
+ if (options.smartZoomHeightMax) {
249
+ console.warn('Controls smartZoomHeightMax parameter is deprecated. Use smartTravelHeightMax instead.');
250
+ options.smartTravelHeightMax = options.smartTravelHeightMax || options.smartZoomHeightMax;
251
+ }
252
+ this.smartTravelHeightMin = options.smartTravelHeightMin || defaultOptions.smartTravelHeightMin;
253
+ this.smartTravelHeightMax = options.smartTravelHeightMax || defaultOptions.smartTravelHeightMax;
254
+
255
+ // if set to true, animated travels have 0 duration
256
+ this.instantTravel = options.instantTravel || defaultOptions.instantTravel;
257
+
258
+ // the zenith angle for a camera rotation will be between these two values
259
+ this.minZenithAngle = (options.minZenithAngle || defaultOptions.minZenithAngle) * Math.PI / 180;
260
+ // max value should be less than 90 deg (90 = parallel to the ground)
261
+ this.maxZenithAngle = (options.maxZenithAngle || defaultOptions.maxZenithAngle) * Math.PI / 180;
262
+
263
+ // focus policy options
264
+ if (options.focusOnMouseOver) {
265
+ console.warn('Planar controls \'focusOnMouseOver\' optional parameter has been removed.');
266
+ }
267
+ if (options.focusOnMouseClick) {
268
+ console.warn('Planar controls \'focusOnMouseClick\' optional parameter has been removed.');
269
+ }
270
+
271
+ // set collision options
272
+ this.handleCollision = options.handleCollision === undefined ? defaultOptions.handleCollision : options.handleCollision;
273
+ this.minDistanceCollision = defaultOptions.minDistanceCollision;
274
+
275
+ // enable smart travel
276
+ this.enableSmartTravel = options.enableSmartTravel === undefined ? defaultOptions.enableSmartTravel : options.enableSmartTravel;
277
+ startPosition.copy(this.camera.position);
278
+ startQuaternion.copy(this.camera.quaternion);
279
+
280
+ // control state
281
+ this.state = STATE.NONE;
282
+ this.cursor = cursor;
283
+ if (this.view.controls) {
284
+ // esLint-disable-next-line no-console
285
+ console.warn('Deprecated use of PlanarControls. See examples to correct PlanarControls implementation.');
286
+ this.view.controls.dispose();
287
+ }
288
+ this.view.controls = this;
289
+
290
+ // eventListeners handlers
291
+ this._handlerOnKeyDown = this.onKeyDown.bind(this);
292
+ this._handlerOnMouseDown = this.onMouseDown.bind(this);
293
+ this._handlerOnMouseUp = this.onMouseUp.bind(this);
294
+ this._handlerOnMouseMove = this.onMouseMove.bind(this);
295
+ this._handlerOnMouseWheel = this.onMouseWheel.bind(this);
296
+ this._handlerContextMenu = this.onContextMenu.bind(this);
297
+ this._handlerUpdate = this.update.bind(this);
298
+
299
+ // add this PlanarControl instance to the view's frameRequesters
300
+ // with this, PlanarControl.update() will be called each frame
301
+ this.view.addFrameRequester(MAIN_LOOP_EVENTS.AFTER_CAMERA_UPDATE, this._handlerUpdate);
302
+
303
+ // event listeners for user input (to activate the controls)
304
+ this.addInputListeners();
305
+ }
306
+ dispose() {
307
+ this.removeInputListeners();
308
+ this.view.removeFrameRequester(MAIN_LOOP_EVENTS.AFTER_CAMERA_UPDATE, this._handlerUpdate);
309
+ }
310
+
311
+ /**
312
+ * update the view and camera if needed, and handles the animated travel
313
+ * @param {number} dt the delta time between two updates in millisecond
314
+ * @param {boolean} updateLoopRestarted true if we just started rendering
315
+ * @ignore
316
+ */
317
+ update(dt, updateLoopRestarted) {
318
+ // dt will not be relevant when we just started rendering. We consider a 1-frame move in this case
319
+ if (updateLoopRestarted) {
320
+ dt = 16;
321
+ }
322
+ const onMovement = this.state !== STATE.NONE;
323
+ switch (this.state) {
324
+ case STATE.TRAVEL:
325
+ this.handleTravel(dt);
326
+ this.view.notifyChange(this.camera);
327
+ break;
328
+ case STATE.ORTHO_ZOOM:
329
+ this.handleZoomOrtho(dt);
330
+ this.view.notifyChange(this.camera);
331
+ break;
332
+ case STATE.DRAG:
333
+ this.handleDragMovement();
334
+ this.view.notifyChange(this.camera);
335
+ break;
336
+ case STATE.ROTATE:
337
+ this.handleRotation();
338
+ this.view.notifyChange(this.camera);
339
+ break;
340
+ case STATE.PAN:
341
+ this.handlePanMovement();
342
+ this.view.notifyChange(this.camera);
343
+ break;
344
+ case STATE.NONE:
345
+ default:
346
+ break;
347
+ }
348
+ // We test if camera collides to the geometry layer or is too close to the ground, and adjust its altitude in
349
+ // case
350
+ if (this.handleCollision) {
351
+ // check distance to the ground/surface geometry (could be another geometry layer)
352
+ this.view.camera.adjustAltitudeToAvoidCollisionWithLayer(this.view, this.view.tileLayer, this.minDistanceCollision);
353
+ }
354
+ if (onMovement) {
355
+ this.view.dispatchEvent({
356
+ type: PLANAR_CONTROL_EVENT.MOVED
357
+ });
358
+ }
359
+ deltaMousePosition.set(0, 0);
360
+ }
361
+
362
+ /**
363
+ * Initiate a drag movement (translation on (xy) plane). The movement value is derived from the actual world
364
+ * point under the mouse cursor. This allows user to 'grab' a world point and drag it to move.
365
+ *
366
+ * @ignore
367
+ */
368
+ initiateDrag() {
369
+ this.state = STATE.DRAG;
370
+
371
+ // the world point under mouse cursor when the drag movement is started
372
+ dragStart.copy(this.getWorldPointAtScreenXY(mousePosition));
373
+
374
+ // the difference between start and end cursor position
375
+ dragDelta.set(0, 0, 0);
376
+ }
377
+
378
+ /**
379
+ * Handle the drag movement (translation on (xy) plane) when user moves the mouse while in STATE.DRAG. The
380
+ * drag movement is previously initiated by [initiateDrag]{@link PlanarControls#initiateDrag}. Compute the
381
+ * drag value and update the camera controls. The movement value is derived from the actual world point under
382
+ * the mouse cursor. This allows the user to 'grab' a world point and drag it to move.
383
+ *
384
+ * @ignore
385
+ */
386
+ handleDragMovement() {
387
+ // the world point under the current mouse cursor position, at same altitude than dragStart
388
+ this.getWorldPointFromMathPlaneAtScreenXY(mousePosition, dragStart.z, dragEnd);
389
+
390
+ // the difference between start and end cursor position
391
+ dragDelta.subVectors(dragStart, dragEnd);
392
+
393
+ // update the camera position
394
+ this.camera.position.add(dragDelta);
395
+ dragDelta.set(0, 0, 0);
396
+ }
397
+
398
+ /**
399
+ * Initiate a pan movement (local translation on (xz) plane).
400
+ *
401
+ * @ignore
402
+ */
403
+ initiatePan() {
404
+ this.state = STATE.PAN;
405
+ }
406
+
407
+ /**
408
+ * Handle the pan movement (translation on local x / world z plane) when user moves the mouse while
409
+ * STATE.PAN. The drag movement is previously initiated by [initiatePan]{@link PlanarControls#initiatePan}.
410
+ * Compute the pan value and update the camera controls.
411
+ *
412
+ * @ignore
413
+ */
414
+ handlePanMovement() {
415
+ vect.set(-deltaMousePosition.x, deltaMousePosition.y, 0);
416
+ this.camera.localToWorld(vect);
417
+ this.camera.position.copy(vect);
418
+ }
419
+
420
+ /**
421
+ * Initiate a rotate (orbit) movement.
422
+ *
423
+ * @ignore
424
+ */
425
+ initiateRotation() {
426
+ this.state = STATE.ROTATE;
427
+ centerPoint.copy(this.getWorldPointAtScreenXY(new THREE.Vector2(0.5 * this.view.mainLoop.gfxEngine.width, 0.5 * this.view.mainLoop.gfxEngine.height)));
428
+ const radius = this.camera.position.distanceTo(centerPoint);
429
+ phi = Math.acos((this.camera.position.z - centerPoint.z) / radius);
430
+ }
431
+
432
+ /**
433
+ * Handle the rotate movement (orbit) when user moves the mouse while in STATE.ROTATE. The movement is an
434
+ * orbit around `centerPoint`, the camera focus point (ground point at screen center). The rotate movement
435
+ * is previously initiated in [initiateRotation]{@link PlanarControls#initiateRotation}.
436
+ * Compute the new position value and update the camera controls.
437
+ *
438
+ * @ignore
439
+ */
440
+ handleRotation() {
441
+ // angle deltas
442
+ // deltaMousePosition is computed in onMouseMove / onMouseDowns
443
+ const thetaDelta = -this.rotateSpeed * deltaMousePosition.x / this.view.mainLoop.gfxEngine.width;
444
+ const phiDelta = -this.rotateSpeed * deltaMousePosition.y / this.view.mainLoop.gfxEngine.height;
445
+
446
+ // the vector from centerPoint (focus point) to camera position
447
+ const offset = this.camera.position.clone().sub(centerPoint);
448
+ if (thetaDelta !== 0 || phiDelta !== 0) {
449
+ if (phi + phiDelta >= this.minZenithAngle && phi + phiDelta <= this.maxZenithAngle && phiDelta !== 0) {
450
+ // rotation around X (altitude)
451
+ phi += phiDelta;
452
+ vect.set(0, 0, 1);
453
+ quat.setFromUnitVectors(this.camera.up, vect);
454
+ offset.applyQuaternion(quat);
455
+ vect.setFromMatrixColumn(this.camera.matrix, 0);
456
+ quat.setFromAxisAngle(vect, phiDelta);
457
+ offset.applyQuaternion(quat);
458
+ vect.set(0, 0, 1);
459
+ quat.setFromUnitVectors(this.camera.up, vect).invert();
460
+ offset.applyQuaternion(quat);
461
+ }
462
+ if (thetaDelta !== 0) {
463
+ // rotation around Z (azimuth)
464
+ vect.set(0, 0, 1);
465
+ quat.setFromAxisAngle(vect, thetaDelta);
466
+ offset.applyQuaternion(quat);
467
+ }
468
+ }
469
+ this.camera.position.copy(offset);
470
+ // TODO : lookAt calls an updateMatrixWorld(). It should be replaced by a new method that does not.
471
+ this.camera.lookAt(vectorZero);
472
+ this.camera.position.add(centerPoint);
473
+ this.camera.updateMatrixWorld();
474
+ }
475
+
476
+ /**
477
+ * Triggers a Zoom animated movement (travel) toward / away from the world point under the mouse cursor. The
478
+ * zoom intensity varies according to the distance between the camera and the point. The closer to the ground,
479
+ * the lower the intensity. Orientation will not change (null parameter in the call to
480
+ * [initiateTravel]{@link PlanarControls#initiateTravel} function).
481
+ *
482
+ * @param {Event} event the mouse wheel event.
483
+ * @ignore
484
+ */
485
+ initiateZoom(event) {
486
+ const delta = -event.deltaY;
487
+ pointUnderCursor.copy(this.getWorldPointAtScreenXY(mousePosition));
488
+ const newPos = new THREE.Vector3();
489
+ if (delta > 0 || delta < 0 && this.maxAltitude > this.camera.position.z) {
490
+ const zoomFactor = delta > 0 ? this.zoomInFactor : this.zoomOutFactor;
491
+
492
+ // do not zoom if the resolution after the zoom is outside resolution limits
493
+ const endResolution = this.view.getPixelsToMeters() / zoomFactor;
494
+ if (this.maxResolution > endResolution || endResolution > this.minResolution) {
495
+ return;
496
+ }
497
+
498
+ // change the camera field of view if the camera is orthographic
499
+ if (this.camera.isOrthographicCamera) {
500
+ // switch state to STATE.ZOOM
501
+ this.state = STATE.ORTHO_ZOOM;
502
+ this.view.notifyChange(this.camera);
503
+
504
+ // camera zoom at the beginning of zoom movement
505
+ startZoom = this.camera.zoom;
506
+ // camera zoom at the end of zoom movement
507
+ endZoom = startZoom * zoomFactor;
508
+ // the altitude of the target must be the same as camera's
509
+ pointUnderCursor.z = this.camera.position.z;
510
+ travelAlpha = 0;
511
+ travelDuration = this.zoomTravelTime;
512
+ this.updateMouseCursorType();
513
+ } else {
514
+ // target position
515
+ newPos.lerpVectors(this.camera.position, pointUnderCursor, 1 - 1 / zoomFactor);
516
+ // initiate travel
517
+ this.initiateTravel(newPos, this.zoomTravelTime, null, false);
518
+ }
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Handle the animated zoom change for an orthographic camera, when state is `ZOOM`.
524
+ *
525
+ * @param {number} dt the delta time between two updates in milliseconds
526
+ * @ignore
527
+ */
528
+ handleZoomOrtho(dt) {
529
+ travelAlpha = Math.min(travelAlpha + dt / 1000 / travelDuration, 1);
530
+
531
+ // new zoom
532
+ const zoom = startZoom + travelAlpha * (endZoom - startZoom);
533
+ if (this.camera.zoom !== zoom) {
534
+ // zoom has changed
535
+ this.camera.zoom = zoom;
536
+ this.camera.updateProjectionMatrix();
537
+
538
+ // current world coordinates under the mouse
539
+ this.view.viewToNormalizedCoords(mousePosition, vect);
540
+ vect.z = 0;
541
+ vect.unproject(this.camera);
542
+
543
+ // new camera position
544
+ this.camera.position.x += pointUnderCursor.x - vect.x;
545
+ this.camera.position.y += pointUnderCursor.y - vect.y;
546
+ this.camera.updateMatrixWorld(true);
547
+ }
548
+
549
+ // completion test
550
+ this.testAnimationEnd();
551
+ }
552
+
553
+ /**
554
+ * Triggers a 'smart zoom' animated movement (travel) toward the point under mouse cursor. The camera will be
555
+ * smoothly moved and oriented close to the target, at a determined height and distance.
556
+ *
557
+ * @ignore
558
+ */
559
+ initiateSmartTravel() {
560
+ const pointUnderCursor = this.getWorldPointAtScreenXY(mousePosition);
561
+
562
+ // direction of the movement, projected on xy plane and normalized
563
+ const dir = new THREE.Vector3();
564
+ dir.copy(pointUnderCursor).sub(this.camera.position);
565
+ dir.z = 0;
566
+ dir.normalize();
567
+ const distanceToPoint = this.camera.position.distanceTo(pointUnderCursor);
568
+
569
+ // camera height (altitude above ground) at the end of the travel, 5000 is an empirical smoothing distance
570
+ const targetHeight = THREE.MathUtils.lerp(this.smartTravelHeightMin, this.smartTravelHeightMax, Math.min(distanceToPoint / 5000, 1));
571
+
572
+ // camera position at the end of the travel
573
+ const moveTarget = new THREE.Vector3();
574
+ moveTarget.copy(pointUnderCursor);
575
+ if (this.enableRotation) {
576
+ moveTarget.add(dir.multiplyScalar(-targetHeight * 2));
577
+ }
578
+ moveTarget.z = pointUnderCursor.z + targetHeight;
579
+ if (this.camera.isOrthographicCamera) {
580
+ startZoom = this.camera.zoom;
581
+ // camera zoom at the end of the travel, 5000 is an empirical smoothing distance
582
+ endZoom = startZoom * (1 + Math.min(distanceToPoint / 5000, 1));
583
+ moveTarget.z = this.camera.position.z;
584
+ }
585
+
586
+ // initiate the travel
587
+ this.initiateTravel(moveTarget, 'auto', pointUnderCursor, true);
588
+ }
589
+
590
+ /**
591
+ * Triggers an animated movement and rotation for the camera.
592
+ *
593
+ * @param {THREE.Vector3} targetPos The target position of the camera (reached at the end).
594
+ * @param {number|string} travelTime Set to `auto` or set to a duration in seconds. If set to `auto`,
595
+ * travel time will be set to a duration between `autoTravelTimeMin` and `autoTravelTimeMax` according to
596
+ * the distance and the angular difference between start and finish.
597
+ * @param {(string|THREE.Vector3|THREE.Quaternion)} targetOrientation define the target rotation of
598
+ * the camera :
599
+ * <ul>
600
+ * <li>if targetOrientation is a world point (Vector3) : the camera will lookAt() this point</li>
601
+ * <li>if targetOrientation is a quaternion : this quaternion will define the final camera orientation </li>
602
+ * <li>if targetOrientation is neither a world point nor a quaternion : the camera will keep its starting
603
+ * orientation</li>
604
+ * </ul>
605
+ * @param {boolean} useSmooth animation is smoothed using the `smooth(value)` function (slower
606
+ * at start and finish).
607
+ *
608
+ * @ignore
609
+ */
610
+ initiateTravel(targetPos, travelTime, targetOrientation, useSmooth) {
611
+ this.state = STATE.TRAVEL;
612
+ this.view.notifyChange(this.camera);
613
+ // the progress of the travel (animation alpha)
614
+ travelAlpha = 0;
615
+ // update cursor
616
+ this.updateMouseCursorType();
617
+ travelUseRotation = this.enableRotation && targetOrientation && (targetOrientation.isQuaternion || targetOrientation.isVector3);
618
+ travelUseSmooth = useSmooth;
619
+
620
+ // start position (current camera position)
621
+ travelStartPos.copy(this.camera.position);
622
+
623
+ // start rotation (current camera rotation)
624
+ travelStartRot.copy(this.camera.quaternion);
625
+
626
+ // setup the end rotation :
627
+ if (travelUseRotation) {
628
+ if (targetOrientation.isQuaternion) {
629
+ // case where targetOrientation is a quaternion
630
+ travelEndRot.copy(targetOrientation);
631
+ } else if (targetOrientation.isVector3) {
632
+ // case where targetOrientation is a Vector3
633
+ if (targetPos === targetOrientation) {
634
+ this.camera.lookAt(targetOrientation);
635
+ travelEndRot.copy(this.camera.quaternion);
636
+ this.camera.quaternion.copy(travelStartRot);
637
+ } else {
638
+ this.camera.position.copy(targetPos);
639
+ this.camera.lookAt(targetOrientation);
640
+ travelEndRot.copy(this.camera.quaternion);
641
+ this.camera.quaternion.copy(travelStartRot);
642
+ this.camera.position.copy(travelStartPos);
643
+ }
644
+ }
645
+ }
646
+
647
+ // end position
648
+ travelEndPos.copy(targetPos);
649
+
650
+ // beginning of the travel duration setup
651
+ if (this.instantTravel) {
652
+ travelDuration = 0;
653
+ } else if (travelTime === 'auto') {
654
+ // case where travelTime is set to `auto` : travelDuration will be a value between autoTravelTimeMin and
655
+ // autoTravelTimeMax depending on travel distance and travel angular difference
656
+
657
+ // a value between 0 and 1 according to the travel distance. Adjusted by autoTravelTimeDist parameter
658
+ const normalizedDistance = Math.min(1, targetPos.distanceTo(this.camera.position) / this.autoTravelTimeDist);
659
+ travelDuration = THREE.MathUtils.lerp(this.autoTravelTimeMin, this.autoTravelTimeMax, normalizedDistance);
660
+
661
+ // if travel changes camera orientation, travel duration is adjusted according to angularDifference
662
+ // this allows for a smoother travel (more time for the camera to rotate)
663
+ // final duration will not exceed autoTravelTimeMax
664
+ if (travelUseRotation) {
665
+ // value is normalized between 0 and 1
666
+ const angularDifference = 0.5 - 0.5 * travelEndRot.normalize().dot(this.camera.quaternion.normalize());
667
+ travelDuration *= 1 + 2 * angularDifference;
668
+ travelDuration = Math.min(travelDuration, this.autoTravelTimeMax);
669
+ }
670
+ } else {
671
+ // case where travelTime !== `auto` : travelTime is a duration in seconds given as parameter
672
+ travelDuration = travelTime;
673
+ }
674
+ }
675
+
676
+ /**
677
+ * Handle the animated movement and rotation of the camera in `travel` state.
678
+ *
679
+ * @param {number} dt the delta time between two updates in milliseconds
680
+ * @ignore
681
+ */
682
+ handleTravel(dt) {
683
+ travelAlpha = Math.min(travelAlpha + dt / 1000 / travelDuration, 1);
684
+
685
+ // the animation alpha, between 0 (start) and 1 (finish)
686
+ const alpha = travelUseSmooth ? this.smooth(travelAlpha) : travelAlpha;
687
+
688
+ // new position
689
+ this.camera.position.lerpVectors(travelStartPos, travelEndPos, alpha);
690
+ const zoom = startZoom + alpha * (endZoom - startZoom);
691
+ // new zoom
692
+ if (this.camera.isOrthographicCamera && this.camera.zoom !== zoom) {
693
+ this.camera.zoom = zoom;
694
+ this.camera.updateProjectionMatrix();
695
+ }
696
+
697
+ // new rotation
698
+ if (travelUseRotation === true) {
699
+ this.camera.quaternion.slerpQuaternions(travelStartRot, travelEndRot, alpha);
700
+ }
701
+
702
+ // completion test
703
+ this.testAnimationEnd();
704
+ }
705
+
706
+ /**
707
+ * Test if the currently running animation is finished (travelAlpha reached 1).
708
+ * If it is, reset controls to state NONE.
709
+ *
710
+ * @ignore
711
+ */
712
+ testAnimationEnd() {
713
+ if (travelAlpha === 1) {
714
+ // Resume normal behaviour after animation is completed
715
+ this.state = STATE.NONE;
716
+ this.updateMouseCursorType();
717
+ }
718
+ }
719
+
720
+ /**
721
+ * Triggers an animated movement (travel) to set the camera to top view, above the focus point,
722
+ * at altitude = distanceToFocusPoint.
723
+ *
724
+ * @ignore
725
+ */
726
+ goToTopView() {
727
+ const topViewPos = new THREE.Vector3();
728
+ const targetQuat = new THREE.Quaternion();
729
+
730
+ // the top view position is above the camera focus point, at an altitude = distanceToPoint
731
+ topViewPos.copy(this.getWorldPointAtScreenXY(new THREE.Vector2(0.5 * this.view.mainLoop.gfxEngine.width, 0.5 * this.view.mainLoop.gfxEngine.height)));
732
+ topViewPos.z += Math.min(this.maxAltitude, this.camera.position.distanceTo(topViewPos));
733
+ targetQuat.setFromAxisAngle(new THREE.Vector3(1, 0, 0), 0);
734
+
735
+ // initiate the travel
736
+ this.initiateTravel(topViewPos, 'auto', targetQuat, true);
737
+ }
738
+
739
+ /**
740
+ * Triggers an animated movement (travel) to set the camera to starting view
741
+ *
742
+ * @ignore
743
+ */
744
+ goToStartView() {
745
+ // if startZoom and endZoom have not been set yet, give them neutral values
746
+ if (this.camera.isOrthographicCamera) {
747
+ startZoom = this.camera.zoom;
748
+ endZoom = cameraInitialZoom;
749
+ }
750
+ this.initiateTravel(startPosition, 'auto', startQuaternion, true);
751
+ }
752
+
753
+ /**
754
+ * Returns the world point (xyz) under the posXY screen point. The point belong to an abstract mathematical
755
+ * plane of specified altitude (does not us actual geometry).
756
+ *
757
+ * @param {THREE.Vector2} posXY the mouse position in screen space (unit : pixel)
758
+ * @param {number} altitude the altitude (z) of the mathematical plane
759
+ * @param {THREE.Vector3} target the target vector3
760
+ * @return {THREE.Vector3}
761
+ * @ignore
762
+ */
763
+ getWorldPointFromMathPlaneAtScreenXY(posXY, altitude) {
764
+ let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new THREE.Vector3();
765
+ vect2.copy(this.view.viewToNormalizedCoords(posXY));
766
+ rayCaster.setFromCamera(vect2, this.camera);
767
+ plane.constant = altitude;
768
+ rayCaster.ray.intersectPlane(plane, target);
769
+ return target;
770
+ }
771
+
772
+ /**
773
+ * Returns the world point (xyz) under the posXY screen point. If geometry is under the cursor, the point is
774
+ * obtained with getPickingPositionFromDepth. If no geometry is under the cursor, the point is obtained with
775
+ * [getWorldPointFromMathPlaneAtScreenXY]{@link PlanarControls#getWorldPointFromMathPlaneAtScreenXY}.
776
+ *
777
+ * @param {THREE.Vector2} posXY the mouse position in screen space (unit : pixel)
778
+ * @param {THREE.Vector3} target the target World coordinates.
779
+ * @return {THREE.Vector3}
780
+ * @ignore
781
+ */
782
+ getWorldPointAtScreenXY(posXY) {
783
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Vector3();
784
+ // check if there is a valid geometry under cursor
785
+ if (this.view.getPickingPositionFromDepth(posXY, target)) {
786
+ return target;
787
+ } else {
788
+ // if not, we use the mathematical plane at altitude = groundLevel
789
+ this.getWorldPointFromMathPlaneAtScreenXY(posXY, this.groundLevel, target);
790
+ return target;
791
+ }
792
+ }
793
+
794
+ /**
795
+ * Add all the input event listeners (activate the controls).
796
+ *
797
+ * @ignore
798
+ */
799
+ addInputListeners() {
800
+ this.view.domElement.addEventListener('keydown', this._handlerOnKeyDown, false);
801
+ this.view.domElement.addEventListener('mousedown', this._handlerOnMouseDown, false);
802
+ this.view.domElement.addEventListener('mouseup', this._handlerOnMouseUp, false);
803
+ this.view.domElement.addEventListener('mouseleave', this._handlerOnMouseUp, false);
804
+ this.view.domElement.addEventListener('mousemove', this._handlerOnMouseMove, false);
805
+ this.view.domElement.addEventListener('wheel', this._handlerOnMouseWheel, false);
806
+ // prevent the default context menu from appearing when right-clicking
807
+ // this allows to use right-click for input without the menu appearing
808
+ this.view.domElement.addEventListener('contextmenu', this._handlerContextMenu, false);
809
+ }
810
+
811
+ /**
812
+ * Removes all the input listeners (deactivate the controls).
813
+ *
814
+ * @ignore
815
+ */
816
+ removeInputListeners() {
817
+ this.view.domElement.removeEventListener('keydown', this._handlerOnKeyDown, true);
818
+ this.view.domElement.removeEventListener('mousedown', this._handlerOnMouseDown, false);
819
+ this.view.domElement.removeEventListener('mouseup', this._handlerOnMouseUp, false);
820
+ this.view.domElement.removeEventListener('mouseleave', this._handlerOnMouseUp, false);
821
+ this.view.domElement.removeEventListener('mousemove', this._handlerOnMouseMove, false);
822
+ this.view.domElement.removeEventListener('wheel', this._handlerOnMouseWheel, false);
823
+ this.view.domElement.removeEventListener('contextmenu', this._handlerContextMenu, false);
824
+ }
825
+
826
+ /**
827
+ * Update the cursor image according to the control state.
828
+ *
829
+ * @ignore
830
+ */
831
+ updateMouseCursorType() {
832
+ switch (this.state) {
833
+ case STATE.NONE:
834
+ this.view.domElement.style.cursor = this.cursor.default;
835
+ break;
836
+ case STATE.DRAG:
837
+ this.view.domElement.style.cursor = this.cursor.drag;
838
+ break;
839
+ case STATE.PAN:
840
+ this.view.domElement.style.cursor = this.cursor.pan;
841
+ break;
842
+ case STATE.TRAVEL:
843
+ this.view.domElement.style.cursor = this.cursor.travel;
844
+ break;
845
+ case STATE.ORTHO_ZOOM:
846
+ this.view.domElement.style.cursor = this.cursor.ortho_zoom;
847
+ break;
848
+ case STATE.ROTATE:
849
+ this.view.domElement.style.cursor = this.cursor.rotate;
850
+ break;
851
+ default:
852
+ break;
853
+ }
854
+ }
855
+ updateMousePositionAndDelta(event) {
856
+ this.view.eventToViewCoords(event, mousePosition);
857
+ deltaMousePosition.copy(mousePosition).sub(lastMousePosition);
858
+ lastMousePosition.copy(mousePosition);
859
+ }
860
+
861
+ /**
862
+ * cursor modification for a specifique state.
863
+ *
864
+ * @param {string} state the state in which we want to change the cursor ('default', 'drag', 'pan', 'travel', 'rotate').
865
+ * @param {string} newCursor the css cursor we want to have for the specified state.
866
+ * @ignore
867
+ */
868
+ setCursor(state, newCursor) {
869
+ this.cursor[state] = newCursor;
870
+ this.updateMouseCursorType();
871
+ }
872
+
873
+ /**
874
+ * Catch and manage the event when a touch on the mouse is downs.
875
+ *
876
+ * @param {Event} event the current event (mouse left or right button clicked, mouse wheel button actioned).
877
+ * @ignore
878
+ */
879
+ onMouseDown(event) {
880
+ if (!this.enabled) {
881
+ return;
882
+ }
883
+ event.preventDefault();
884
+ this.view.domElement.focus();
885
+ if (STATE.NONE !== this.state) {
886
+ return;
887
+ }
888
+ currentPressedButton = event.button;
889
+ this.updateMousePositionAndDelta(event);
890
+ if (mouseButtons.LEFTCLICK === event.button) {
891
+ if (event.ctrlKey) {
892
+ if (this.enableRotation) {
893
+ this.initiateRotation();
894
+ } else {
895
+ return;
896
+ }
897
+ } else {
898
+ this.initiateDrag();
899
+ }
900
+ } else if (mouseButtons.MIDDLECLICK === event.button) {
901
+ if (this.enableSmartTravel) {
902
+ this.initiateSmartTravel();
903
+ } else {
904
+ return;
905
+ }
906
+ } else if (mouseButtons.RIGHTCLICK === event.button) {
907
+ if (this.enablePan) {
908
+ this.initiatePan();
909
+ } else {
910
+ return;
911
+ }
912
+ }
913
+ this.updateMouseCursorType();
914
+ }
915
+
916
+ /**
917
+ * Catch and manage the event when a touch on the mouse is released.
918
+ *
919
+ * @param {Event} event the current event
920
+ * @ignore
921
+ */
922
+ onMouseUp(event) {
923
+ event.preventDefault();
924
+
925
+ // Does not interrupt ongoing camera action if state is TRAVEL or CAMERA_OTHO. This prevents interrupting a zoom
926
+ // movement or a smart travel by pressing any movement key.
927
+ // The camera action is also uninterrupted if the released button does not match the button triggering the
928
+ // ongoing action. This prevents for instance exiting drag mode when right-clicking while dragging the view.
929
+ if (STATE.TRAVEL !== this.state && STATE.ORTHO_ZOOM !== this.state && currentPressedButton === event.button) {
930
+ this.state = STATE.NONE;
931
+ }
932
+ this.updateMouseCursorType();
933
+ }
934
+
935
+ /**
936
+ * Catch and manage the event when the mouse is moved.
937
+ *
938
+ * @param {Event} event the current event.
939
+ * @ignore
940
+ */
941
+ onMouseMove(event) {
942
+ if (!this.enabled) {
943
+ return;
944
+ }
945
+ event.preventDefault();
946
+ this.updateMousePositionAndDelta(event);
947
+
948
+ // notify change if moving
949
+ if (STATE.NONE !== this.state) {
950
+ this.view.notifyChange();
951
+ }
952
+ }
953
+
954
+ /**
955
+ * Catch and manage the event when a key is down.
956
+ *
957
+ * @param {Event} event the current event
958
+ * @ignore
959
+ */
960
+ onKeyDown(event) {
961
+ if (STATE.NONE !== this.state || !this.enabled) {
962
+ return;
963
+ }
964
+ switch (event.keyCode) {
965
+ case keys.T:
966
+ // going to top view is not relevant for an orthographic camera, since it is always top view
967
+ if (!this.camera.isOrthographicCamera) {
968
+ this.goToTopView();
969
+ }
970
+ break;
971
+ case keys.Y:
972
+ this.goToStartView();
973
+ break;
974
+ case keys.SPACE:
975
+ if (this.enableSmartTravel) {
976
+ this.initiateSmartTravel(event);
977
+ }
978
+ break;
979
+ default:
980
+ break;
981
+ }
982
+ }
983
+
984
+ /**
985
+ * Catch and manage the event when the mouse wheel is rolled.
986
+ *
987
+ * @param {Event} event the current event
988
+ * @ignore
989
+ */
990
+ onMouseWheel(event) {
991
+ if (!this.enabled) {
992
+ return;
993
+ }
994
+ event.preventDefault();
995
+ event.stopPropagation();
996
+ if (STATE.NONE === this.state) {
997
+ this.initiateZoom(event);
998
+ }
999
+ }
1000
+
1001
+ /**
1002
+ * Catch and manage the event when the context menu is called (by a right-click on the window). We use this
1003
+ * to prevent the context menu from appearing so we can use right click for other inputs.
1004
+ *
1005
+ * @param {Event} event the current event
1006
+ * @ignore
1007
+ */
1008
+ onContextMenu(event) {
1009
+ event.preventDefault();
1010
+ }
1011
+
1012
+ /**
1013
+ * Smoothing function (sigmoid) : based on h01 Hermite function.
1014
+ *
1015
+ * @param {number} value the value to be smoothed, between 0 and 1.
1016
+ * @return {number} a value between 0 and 1.
1017
+ * @ignore
1018
+ */
1019
+ smooth(value) {
1020
+ // p between 1.0 and 1.5 (empirical)
1021
+
1022
+ return (value ** 2 * (3 - 2 * value)) ** 1.20;
1023
+ }
1024
+ }
1025
+ export default PlanarControls;