ScreenSpaceCameraController.js 86 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896
  1. import Cartesian2 from "../Core/Cartesian2.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import Cartesian4 from "../Core/Cartesian4.js";
  4. import Cartographic from "../Core/Cartographic.js";
  5. import defaultValue from "../Core/defaultValue.js";
  6. import defined from "../Core/defined.js";
  7. import destroyObject from "../Core/destroyObject.js";
  8. import DeveloperError from "../Core/DeveloperError.js";
  9. import Ellipsoid from "../Core/Ellipsoid.js";
  10. import HeadingPitchRoll from "../Core/HeadingPitchRoll.js";
  11. import IntersectionTests from "../Core/IntersectionTests.js";
  12. import KeyboardEventModifier from "../Core/KeyboardEventModifier.js";
  13. import CesiumMath from "../Core/Math.js";
  14. import Matrix3 from "../Core/Matrix3.js";
  15. import Matrix4 from "../Core/Matrix4.js";
  16. import OrthographicFrustum from "../Core/OrthographicFrustum.js";
  17. import Plane from "../Core/Plane.js";
  18. import Quaternion from "../Core/Quaternion.js";
  19. import Ray from "../Core/Ray.js";
  20. import Transforms from "../Core/Transforms.js";
  21. import CameraEventAggregator from "./CameraEventAggregator.js";
  22. import CameraEventType from "./CameraEventType.js";
  23. import MapMode2D from "./MapMode2D.js";
  24. import SceneMode from "./SceneMode.js";
  25. import SceneTransforms from "./SceneTransforms.js";
  26. import TweenCollection from "./TweenCollection.js";
  27. /**
  28. * Modifies the camera position and orientation based on mouse input to a canvas.
  29. * @alias ScreenSpaceCameraController
  30. * @constructor
  31. *
  32. * @param {Scene} scene The scene.
  33. */
  34. function ScreenSpaceCameraController(scene) {
  35. //>>includeStart('debug', pragmas.debug);
  36. if (!defined(scene)) {
  37. throw new DeveloperError("scene is required.");
  38. }
  39. //>>includeEnd('debug');
  40. /**
  41. * If true, inputs are allowed conditionally with the flags enableTranslate, enableZoom,
  42. * enableRotate, enableTilt, and enableLook. If false, all inputs are disabled.
  43. *
  44. * NOTE: This setting is for temporary use cases, such as camera flights and
  45. * drag-selection of regions (see Picking demo). It is typically set to false at the
  46. * start of such events, and set true on completion. To keep inputs disabled
  47. * past the end of camera flights, you must use the other booleans (enableTranslate,
  48. * enableZoom, enableRotate, enableTilt, and enableLook).
  49. * @type {Boolean}
  50. * @default true
  51. */
  52. this.enableInputs = true;
  53. /**
  54. * If true, allows the user to pan around the map. If false, the camera stays locked at the current position.
  55. * This flag only applies in 2D and Columbus view modes.
  56. * @type {Boolean}
  57. * @default true
  58. */
  59. this.enableTranslate = true;
  60. /**
  61. * If true, allows the user to zoom in and out. If false, the camera is locked to the current distance from the ellipsoid.
  62. * @type {Boolean}
  63. * @default true
  64. */
  65. this.enableZoom = true;
  66. /**
  67. * If true, allows the user to rotate the world which translates the user's position.
  68. * This flag only applies in 2D and 3D.
  69. * @type {Boolean}
  70. * @default true
  71. */
  72. this.enableRotate = true;
  73. /**
  74. * If true, allows the user to tilt the camera. If false, the camera is locked to the current heading.
  75. * This flag only applies in 3D and Columbus view.
  76. * @type {Boolean}
  77. * @default true
  78. */
  79. this.enableTilt = true;
  80. /**
  81. * If true, allows the user to use free-look. If false, the camera view direction can only be changed through translating
  82. * or rotating. This flag only applies in 3D and Columbus view modes.
  83. * @type {Boolean}
  84. * @default true
  85. */
  86. this.enableLook = true;
  87. /**
  88. * A parameter in the range <code>[0, 1)</code> used to determine how long
  89. * the camera will continue to spin because of inertia.
  90. * With value of zero, the camera will have no inertia.
  91. * @type {Number}
  92. * @default 0.9
  93. */
  94. this.inertiaSpin = 0.9;
  95. /**
  96. * A parameter in the range <code>[0, 1)</code> used to determine how long
  97. * the camera will continue to translate because of inertia.
  98. * With value of zero, the camera will have no inertia.
  99. * @type {Number}
  100. * @default 0.9
  101. */
  102. this.inertiaTranslate = 0.9;
  103. /**
  104. * A parameter in the range <code>[0, 1)</code> used to determine how long
  105. * the camera will continue to zoom because of inertia.
  106. * With value of zero, the camera will have no inertia.
  107. * @type {Number}
  108. * @default 0.8
  109. */
  110. this.inertiaZoom = 0.8;
  111. /**
  112. * A parameter in the range <code>[0, 1)</code> used to limit the range
  113. * of various user inputs to a percentage of the window width/height per animation frame.
  114. * This helps keep the camera under control in low-frame-rate situations.
  115. * @type {Number}
  116. * @default 0.1
  117. */
  118. this.maximumMovementRatio = 0.1;
  119. /**
  120. * Sets the duration, in seconds, of the bounce back animations in 2D and Columbus view.
  121. * @type {Number}
  122. * @default 3.0
  123. */
  124. this.bounceAnimationTime = 3.0;
  125. /**
  126. * The minimum magnitude, in meters, of the camera position when zooming. Defaults to 1.0.
  127. * @type {Number}
  128. * @default 1.0
  129. */
  130. this.minimumZoomDistance = 1.0;
  131. /**
  132. * The maximum magnitude, in meters, of the camera position when zooming. Defaults to positive infinity.
  133. * @type {Number}
  134. * @default {@link Number.POSITIVE_INFINITY}
  135. */
  136. this.maximumZoomDistance = Number.POSITIVE_INFINITY;
  137. /**
  138. * The input that allows the user to pan around the map. This only applies in 2D and Columbus view modes.
  139. * <p>
  140. * The type came be a {@link CameraEventType}, <code>undefined</code>, an object with <code>eventType</code>
  141. * and <code>modifier</code> properties with types <code>CameraEventType</code> and {@link KeyboardEventModifier},
  142. * or an array of any of the preceding.
  143. * </p>
  144. * @type {CameraEventType|Array|undefined}
  145. * @default {@link CameraEventType.LEFT_DRAG}
  146. */
  147. this.translateEventTypes = CameraEventType.LEFT_DRAG;
  148. /**
  149. * The input that allows the user to zoom in/out.
  150. * <p>
  151. * The type came be a {@link CameraEventType}, <code>undefined</code>, an object with <code>eventType</code>
  152. * and <code>modifier</code> properties with types <code>CameraEventType</code> and {@link KeyboardEventModifier},
  153. * or an array of any of the preceding.
  154. * </p>
  155. * @type {CameraEventType|Array|undefined}
  156. * @default [{@link CameraEventType.RIGHT_DRAG}, {@link CameraEventType.WHEEL}, {@link CameraEventType.PINCH}]
  157. */
  158. this.zoomEventTypes = [
  159. CameraEventType.RIGHT_DRAG,
  160. CameraEventType.WHEEL,
  161. CameraEventType.PINCH,
  162. ];
  163. /**
  164. * The input that allows the user to rotate around the globe or another object. This only applies in 3D and Columbus view modes.
  165. * <p>
  166. * The type came be a {@link CameraEventType}, <code>undefined</code>, an object with <code>eventType</code>
  167. * and <code>modifier</code> properties with types <code>CameraEventType</code> and {@link KeyboardEventModifier},
  168. * or an array of any of the preceding.
  169. * </p>
  170. * @type {CameraEventType|Array|undefined}
  171. * @default {@link CameraEventType.LEFT_DRAG}
  172. */
  173. this.rotateEventTypes = CameraEventType.LEFT_DRAG;
  174. /**
  175. * The input that allows the user to tilt in 3D and Columbus view or twist in 2D.
  176. * <p>
  177. * The type came be a {@link CameraEventType}, <code>undefined</code>, an object with <code>eventType</code>
  178. * and <code>modifier</code> properties with types <code>CameraEventType</code> and {@link KeyboardEventModifier},
  179. * or an array of any of the preceding.
  180. * </p>
  181. * @type {CameraEventType|Array|undefined}
  182. * @default [{@link CameraEventType.MIDDLE_DRAG}, {@link CameraEventType.PINCH}, {
  183. * eventType : {@link CameraEventType.LEFT_DRAG},
  184. * modifier : {@link KeyboardEventModifier.CTRL}
  185. * }, {
  186. * eventType : {@link CameraEventType.RIGHT_DRAG},
  187. * modifier : {@link KeyboardEventModifier.CTRL}
  188. * }]
  189. */
  190. this.tiltEventTypes = [
  191. CameraEventType.MIDDLE_DRAG,
  192. CameraEventType.PINCH,
  193. {
  194. eventType: CameraEventType.LEFT_DRAG,
  195. modifier: KeyboardEventModifier.CTRL,
  196. },
  197. {
  198. eventType: CameraEventType.RIGHT_DRAG,
  199. modifier: KeyboardEventModifier.CTRL,
  200. },
  201. ];
  202. /**
  203. * The input that allows the user to change the direction the camera is viewing. This only applies in 3D and Columbus view modes.
  204. * <p>
  205. * The type came be a {@link CameraEventType}, <code>undefined</code>, an object with <code>eventType</code>
  206. * and <code>modifier</code> properties with types <code>CameraEventType</code> and {@link KeyboardEventModifier},
  207. * or an array of any of the preceding.
  208. * </p>
  209. * @type {CameraEventType|Array|undefined}
  210. * @default { eventType : {@link CameraEventType.LEFT_DRAG}, modifier : {@link KeyboardEventModifier.SHIFT} }
  211. */
  212. this.lookEventTypes = {
  213. eventType: CameraEventType.LEFT_DRAG,
  214. modifier: KeyboardEventModifier.SHIFT,
  215. };
  216. /**
  217. * The minimum height the camera must be before picking the terrain instead of the ellipsoid.
  218. * @type {Number}
  219. * @default 150000.0
  220. */
  221. this.minimumPickingTerrainHeight = 150000.0;
  222. this._minimumPickingTerrainHeight = this.minimumPickingTerrainHeight;
  223. /**
  224. * The minimum height the camera must be before testing for collision with terrain.
  225. * @type {Number}
  226. * @default 15000.0
  227. */
  228. this.minimumCollisionTerrainHeight = 15000.0;
  229. this._minimumCollisionTerrainHeight = this.minimumCollisionTerrainHeight;
  230. /**
  231. * The minimum height the camera must be before switching from rotating a track ball to
  232. * free look when clicks originate on the sky or in space.
  233. * @type {Number}
  234. * @default 7500000.0
  235. */
  236. this.minimumTrackBallHeight = 7500000.0;
  237. this._minimumTrackBallHeight = this.minimumTrackBallHeight;
  238. /**
  239. * Enables or disables camera collision detection with terrain.
  240. * @type {Boolean}
  241. * @default true
  242. */
  243. this.enableCollisionDetection = true;
  244. this._scene = scene;
  245. this._globe = undefined;
  246. this._ellipsoid = undefined;
  247. this._aggregator = new CameraEventAggregator(scene.canvas);
  248. this._lastInertiaSpinMovement = undefined;
  249. this._lastInertiaZoomMovement = undefined;
  250. this._lastInertiaTranslateMovement = undefined;
  251. this._lastInertiaTiltMovement = undefined;
  252. // Zoom disables tilt, spin, and translate inertia
  253. // Tilt disables spin and translate inertia
  254. this._inertiaDisablers = {
  255. _lastInertiaZoomMovement: [
  256. "_lastInertiaSpinMovement",
  257. "_lastInertiaTranslateMovement",
  258. "_lastInertiaTiltMovement",
  259. ],
  260. _lastInertiaTiltMovement: [
  261. "_lastInertiaSpinMovement",
  262. "_lastInertiaTranslateMovement",
  263. ],
  264. };
  265. this._tweens = new TweenCollection();
  266. this._tween = undefined;
  267. this._horizontalRotationAxis = undefined;
  268. this._tiltCenterMousePosition = new Cartesian2(-1.0, -1.0);
  269. this._tiltCenter = new Cartesian3();
  270. this._rotateMousePosition = new Cartesian2(-1.0, -1.0);
  271. this._rotateStartPosition = new Cartesian3();
  272. this._strafeStartPosition = new Cartesian3();
  273. this._strafeMousePosition = new Cartesian2();
  274. this._strafeEndMousePosition = new Cartesian2();
  275. this._zoomMouseStart = new Cartesian2(-1.0, -1.0);
  276. this._zoomWorldPosition = new Cartesian3();
  277. this._useZoomWorldPosition = false;
  278. this._tiltCVOffMap = false;
  279. this._looking = false;
  280. this._rotating = false;
  281. this._strafing = false;
  282. this._zoomingOnVector = false;
  283. this._zoomingUnderground = false;
  284. this._rotatingZoom = false;
  285. this._adjustedHeightForTerrain = false;
  286. this._cameraUnderground = false;
  287. var projection = scene.mapProjection;
  288. this._maxCoord = projection.project(
  289. new Cartographic(Math.PI, CesiumMath.PI_OVER_TWO)
  290. );
  291. // Constants, Make any of these public?
  292. this._zoomFactor = 5.0;
  293. this._rotateFactor = undefined;
  294. this._rotateRateRangeAdjustment = undefined;
  295. this._maximumRotateRate = 1.77;
  296. this._minimumRotateRate = 1.0 / 5000.0;
  297. this._minimumZoomRate = 20.0;
  298. this._maximumZoomRate = 5906376272000.0; // distance from the Sun to Pluto in meters.
  299. this._minimumUndergroundPickDistance = 2000.0;
  300. this._maximumUndergroundPickDistance = 10000.0;
  301. }
  302. function decay(time, coefficient) {
  303. if (time < 0) {
  304. return 0.0;
  305. }
  306. var tau = (1.0 - coefficient) * 25.0;
  307. return Math.exp(-tau * time);
  308. }
  309. function sameMousePosition(movement) {
  310. return Cartesian2.equalsEpsilon(
  311. movement.startPosition,
  312. movement.endPosition,
  313. CesiumMath.EPSILON14
  314. );
  315. }
  316. // If the time between mouse down and mouse up is not between
  317. // these thresholds, the camera will not move with inertia.
  318. // This value is probably dependent on the browser and/or the
  319. // hardware. Should be investigated further.
  320. var inertiaMaxClickTimeThreshold = 0.4;
  321. function maintainInertia(
  322. aggregator,
  323. type,
  324. modifier,
  325. decayCoef,
  326. action,
  327. object,
  328. lastMovementName
  329. ) {
  330. var movementState = object[lastMovementName];
  331. if (!defined(movementState)) {
  332. movementState = object[lastMovementName] = {
  333. startPosition: new Cartesian2(),
  334. endPosition: new Cartesian2(),
  335. motion: new Cartesian2(),
  336. inertiaEnabled: true,
  337. };
  338. }
  339. var ts = aggregator.getButtonPressTime(type, modifier);
  340. var tr = aggregator.getButtonReleaseTime(type, modifier);
  341. var threshold = ts && tr && (tr.getTime() - ts.getTime()) / 1000.0;
  342. var now = new Date();
  343. var fromNow = tr && (now.getTime() - tr.getTime()) / 1000.0;
  344. if (ts && tr && threshold < inertiaMaxClickTimeThreshold) {
  345. var d = decay(fromNow, decayCoef);
  346. var lastMovement = aggregator.getLastMovement(type, modifier);
  347. if (
  348. !defined(lastMovement) ||
  349. sameMousePosition(lastMovement) ||
  350. !movementState.inertiaEnabled
  351. ) {
  352. return;
  353. }
  354. movementState.motion.x =
  355. (lastMovement.endPosition.x - lastMovement.startPosition.x) * 0.5;
  356. movementState.motion.y =
  357. (lastMovement.endPosition.y - lastMovement.startPosition.y) * 0.5;
  358. movementState.startPosition = Cartesian2.clone(
  359. lastMovement.startPosition,
  360. movementState.startPosition
  361. );
  362. movementState.endPosition = Cartesian2.multiplyByScalar(
  363. movementState.motion,
  364. d,
  365. movementState.endPosition
  366. );
  367. movementState.endPosition = Cartesian2.add(
  368. movementState.startPosition,
  369. movementState.endPosition,
  370. movementState.endPosition
  371. );
  372. // If value from the decreasing exponential function is close to zero,
  373. // the end coordinates may be NaN.
  374. if (
  375. isNaN(movementState.endPosition.x) ||
  376. isNaN(movementState.endPosition.y) ||
  377. Cartesian2.distance(
  378. movementState.startPosition,
  379. movementState.endPosition
  380. ) < 0.5
  381. ) {
  382. return;
  383. }
  384. if (!aggregator.isButtonDown(type, modifier)) {
  385. var startPosition = aggregator.getStartMousePosition(type, modifier);
  386. action(object, startPosition, movementState);
  387. }
  388. }
  389. }
  390. function activateInertia(controller, inertiaStateName) {
  391. if (defined(inertiaStateName)) {
  392. // Re-enable inertia if it was disabled
  393. var movementState = controller[inertiaStateName];
  394. if (defined(movementState)) {
  395. movementState.inertiaEnabled = true;
  396. }
  397. // Disable inertia on other movements
  398. var inertiasToDisable = controller._inertiaDisablers[inertiaStateName];
  399. if (defined(inertiasToDisable)) {
  400. var length = inertiasToDisable.length;
  401. for (var i = 0; i < length; ++i) {
  402. movementState = controller[inertiasToDisable[i]];
  403. if (defined(movementState)) {
  404. movementState.inertiaEnabled = false;
  405. }
  406. }
  407. }
  408. }
  409. }
  410. var scratchEventTypeArray = [];
  411. function reactToInput(
  412. controller,
  413. enabled,
  414. eventTypes,
  415. action,
  416. inertiaConstant,
  417. inertiaStateName
  418. ) {
  419. if (!defined(eventTypes)) {
  420. return;
  421. }
  422. var aggregator = controller._aggregator;
  423. if (!Array.isArray(eventTypes)) {
  424. scratchEventTypeArray[0] = eventTypes;
  425. eventTypes = scratchEventTypeArray;
  426. }
  427. var length = eventTypes.length;
  428. for (var i = 0; i < length; ++i) {
  429. var eventType = eventTypes[i];
  430. var type = defined(eventType.eventType) ? eventType.eventType : eventType;
  431. var modifier = eventType.modifier;
  432. var movement =
  433. aggregator.isMoving(type, modifier) &&
  434. aggregator.getMovement(type, modifier);
  435. var startPosition = aggregator.getStartMousePosition(type, modifier);
  436. if (controller.enableInputs && enabled) {
  437. if (movement) {
  438. action(controller, startPosition, movement);
  439. activateInertia(controller, inertiaStateName);
  440. } else if (inertiaConstant < 1.0) {
  441. maintainInertia(
  442. aggregator,
  443. type,
  444. modifier,
  445. inertiaConstant,
  446. action,
  447. controller,
  448. inertiaStateName
  449. );
  450. }
  451. }
  452. }
  453. }
  454. var scratchZoomPickRay = new Ray();
  455. var scratchPickCartesian = new Cartesian3();
  456. var scratchZoomOffset = new Cartesian2();
  457. var scratchZoomDirection = new Cartesian3();
  458. var scratchCenterPixel = new Cartesian2();
  459. var scratchCenterPosition = new Cartesian3();
  460. var scratchPositionNormal = new Cartesian3();
  461. var scratchPickNormal = new Cartesian3();
  462. var scratchZoomAxis = new Cartesian3();
  463. var scratchCameraPositionNormal = new Cartesian3();
  464. // Scratch variables used in zooming algorithm
  465. var scratchTargetNormal = new Cartesian3();
  466. var scratchCameraPosition = new Cartesian3();
  467. var scratchCameraUpNormal = new Cartesian3();
  468. var scratchCameraRightNormal = new Cartesian3();
  469. var scratchForwardNormal = new Cartesian3();
  470. var scratchPositionToTarget = new Cartesian3();
  471. var scratchPositionToTargetNormal = new Cartesian3();
  472. var scratchPan = new Cartesian3();
  473. var scratchCenterMovement = new Cartesian3();
  474. var scratchCenter = new Cartesian3();
  475. var scratchCartesian = new Cartesian3();
  476. var scratchCartesianTwo = new Cartesian3();
  477. var scratchCartesianThree = new Cartesian3();
  478. var scratchZoomViewOptions = {
  479. orientation: new HeadingPitchRoll(),
  480. };
  481. function handleZoom(
  482. object,
  483. startPosition,
  484. movement,
  485. zoomFactor,
  486. distanceMeasure,
  487. unitPositionDotDirection
  488. ) {
  489. var percentage = 1.0;
  490. if (defined(unitPositionDotDirection)) {
  491. percentage = CesiumMath.clamp(
  492. Math.abs(unitPositionDotDirection),
  493. 0.25,
  494. 1.0
  495. );
  496. }
  497. // distanceMeasure should be the height above the ellipsoid.
  498. // The zoomRate slows as it approaches the surface and stops minimumZoomDistance above it.
  499. var minHeight = object.minimumZoomDistance * percentage;
  500. var maxHeight = object.maximumZoomDistance;
  501. var minDistance = distanceMeasure - minHeight;
  502. var zoomRate = zoomFactor * minDistance;
  503. zoomRate = CesiumMath.clamp(
  504. zoomRate,
  505. object._minimumZoomRate,
  506. object._maximumZoomRate
  507. );
  508. var diff = movement.endPosition.y - movement.startPosition.y;
  509. var rangeWindowRatio = diff / object._scene.canvas.clientHeight;
  510. rangeWindowRatio = Math.min(rangeWindowRatio, object.maximumMovementRatio);
  511. var distance = zoomRate * rangeWindowRatio;
  512. if (
  513. object.enableCollisionDetection ||
  514. object.minimumZoomDistance === 0.0 ||
  515. !defined(object._globe) // look-at mode
  516. ) {
  517. if (distance > 0.0 && Math.abs(distanceMeasure - minHeight) < 1.0) {
  518. return;
  519. }
  520. if (distance < 0.0 && Math.abs(distanceMeasure - maxHeight) < 1.0) {
  521. return;
  522. }
  523. if (distanceMeasure - distance < minHeight) {
  524. distance = distanceMeasure - minHeight - 1.0;
  525. } else if (distanceMeasure - distance > maxHeight) {
  526. distance = distanceMeasure - maxHeight;
  527. }
  528. }
  529. var scene = object._scene;
  530. var camera = scene.camera;
  531. var mode = scene.mode;
  532. var orientation = scratchZoomViewOptions.orientation;
  533. orientation.heading = camera.heading;
  534. orientation.pitch = camera.pitch;
  535. orientation.roll = camera.roll;
  536. if (camera.frustum instanceof OrthographicFrustum) {
  537. if (Math.abs(distance) > 0.0) {
  538. camera.zoomIn(distance);
  539. camera._adjustOrthographicFrustum();
  540. }
  541. return;
  542. }
  543. var sameStartPosition = Cartesian2.equals(
  544. startPosition,
  545. object._zoomMouseStart
  546. );
  547. var zoomingOnVector = object._zoomingOnVector;
  548. var rotatingZoom = object._rotatingZoom;
  549. var pickedPosition;
  550. if (!sameStartPosition) {
  551. object._zoomMouseStart = Cartesian2.clone(
  552. startPosition,
  553. object._zoomMouseStart
  554. );
  555. if (defined(object._globe)) {
  556. if (mode === SceneMode.SCENE2D) {
  557. pickedPosition = camera.getPickRay(startPosition, scratchZoomPickRay)
  558. .origin;
  559. pickedPosition = Cartesian3.fromElements(
  560. pickedPosition.y,
  561. pickedPosition.z,
  562. pickedPosition.x
  563. );
  564. } else {
  565. pickedPosition = pickGlobe(object, startPosition, scratchPickCartesian);
  566. }
  567. }
  568. if (defined(pickedPosition)) {
  569. object._useZoomWorldPosition = true;
  570. object._zoomWorldPosition = Cartesian3.clone(
  571. pickedPosition,
  572. object._zoomWorldPosition
  573. );
  574. } else {
  575. object._useZoomWorldPosition = false;
  576. }
  577. zoomingOnVector = object._zoomingOnVector = false;
  578. rotatingZoom = object._rotatingZoom = false;
  579. object._zoomingUnderground = object._cameraUnderground;
  580. }
  581. if (!object._useZoomWorldPosition) {
  582. camera.zoomIn(distance);
  583. return;
  584. }
  585. var zoomOnVector = mode === SceneMode.COLUMBUS_VIEW;
  586. if (camera.positionCartographic.height < 2000000) {
  587. rotatingZoom = true;
  588. }
  589. if (!sameStartPosition || rotatingZoom) {
  590. if (mode === SceneMode.SCENE2D) {
  591. var worldPosition = object._zoomWorldPosition;
  592. var endPosition = camera.position;
  593. if (
  594. !Cartesian3.equals(worldPosition, endPosition) &&
  595. camera.positionCartographic.height < object._maxCoord.x * 2.0
  596. ) {
  597. var savedX = camera.position.x;
  598. var direction = Cartesian3.subtract(
  599. worldPosition,
  600. endPosition,
  601. scratchZoomDirection
  602. );
  603. Cartesian3.normalize(direction, direction);
  604. var d =
  605. (Cartesian3.distance(worldPosition, endPosition) * distance) /
  606. (camera.getMagnitude() * 0.5);
  607. camera.move(direction, d * 0.5);
  608. if (
  609. (camera.position.x < 0.0 && savedX > 0.0) ||
  610. (camera.position.x > 0.0 && savedX < 0.0)
  611. ) {
  612. pickedPosition = camera.getPickRay(startPosition, scratchZoomPickRay)
  613. .origin;
  614. pickedPosition = Cartesian3.fromElements(
  615. pickedPosition.y,
  616. pickedPosition.z,
  617. pickedPosition.x
  618. );
  619. object._zoomWorldPosition = Cartesian3.clone(
  620. pickedPosition,
  621. object._zoomWorldPosition
  622. );
  623. }
  624. }
  625. } else if (mode === SceneMode.SCENE3D) {
  626. var cameraPositionNormal = Cartesian3.normalize(
  627. camera.position,
  628. scratchCameraPositionNormal
  629. );
  630. if (
  631. object._cameraUnderground ||
  632. object._zoomingUnderground ||
  633. (camera.positionCartographic.height < 3000.0 &&
  634. Math.abs(Cartesian3.dot(camera.direction, cameraPositionNormal)) <
  635. 0.6)
  636. ) {
  637. zoomOnVector = true;
  638. } else {
  639. var canvas = scene.canvas;
  640. var centerPixel = scratchCenterPixel;
  641. centerPixel.x = canvas.clientWidth / 2;
  642. centerPixel.y = canvas.clientHeight / 2;
  643. var centerPosition = pickGlobe(
  644. object,
  645. centerPixel,
  646. scratchCenterPosition
  647. );
  648. // If centerPosition is not defined, it means the globe does not cover the center position of screen
  649. if (
  650. defined(centerPosition) &&
  651. camera.positionCartographic.height < 1000000
  652. ) {
  653. var cameraPosition = scratchCameraPosition;
  654. Cartesian3.clone(camera.position, cameraPosition);
  655. var target = object._zoomWorldPosition;
  656. var targetNormal = scratchTargetNormal;
  657. targetNormal = Cartesian3.normalize(target, targetNormal);
  658. if (Cartesian3.dot(targetNormal, cameraPositionNormal) < 0.0) {
  659. return;
  660. }
  661. var center = scratchCenter;
  662. var forward = scratchForwardNormal;
  663. Cartesian3.clone(camera.direction, forward);
  664. Cartesian3.add(
  665. cameraPosition,
  666. Cartesian3.multiplyByScalar(forward, 1000, scratchCartesian),
  667. center
  668. );
  669. var positionToTarget = scratchPositionToTarget;
  670. var positionToTargetNormal = scratchPositionToTargetNormal;
  671. Cartesian3.subtract(target, cameraPosition, positionToTarget);
  672. Cartesian3.normalize(positionToTarget, positionToTargetNormal);
  673. var alphaDot = Cartesian3.dot(
  674. cameraPositionNormal,
  675. positionToTargetNormal
  676. );
  677. if (alphaDot >= 0.0) {
  678. // We zoomed past the target, and this zoom is not valid anymore.
  679. // This line causes the next zoom movement to pick a new starting point.
  680. object._zoomMouseStart.x = -1;
  681. return;
  682. }
  683. var alpha = Math.acos(-alphaDot);
  684. var cameraDistance = Cartesian3.magnitude(cameraPosition);
  685. var targetDistance = Cartesian3.magnitude(target);
  686. var remainingDistance = cameraDistance - distance;
  687. var positionToTargetDistance = Cartesian3.magnitude(positionToTarget);
  688. var gamma = Math.asin(
  689. CesiumMath.clamp(
  690. (positionToTargetDistance / targetDistance) * Math.sin(alpha),
  691. -1.0,
  692. 1.0
  693. )
  694. );
  695. var delta = Math.asin(
  696. CesiumMath.clamp(
  697. (remainingDistance / targetDistance) * Math.sin(alpha),
  698. -1.0,
  699. 1.0
  700. )
  701. );
  702. var beta = gamma - delta + alpha;
  703. var up = scratchCameraUpNormal;
  704. Cartesian3.normalize(cameraPosition, up);
  705. var right = scratchCameraRightNormal;
  706. right = Cartesian3.cross(positionToTargetNormal, up, right);
  707. right = Cartesian3.normalize(right, right);
  708. Cartesian3.normalize(
  709. Cartesian3.cross(up, right, scratchCartesian),
  710. forward
  711. );
  712. // Calculate new position to move to
  713. Cartesian3.multiplyByScalar(
  714. Cartesian3.normalize(center, scratchCartesian),
  715. Cartesian3.magnitude(center) - distance,
  716. center
  717. );
  718. Cartesian3.normalize(cameraPosition, cameraPosition);
  719. Cartesian3.multiplyByScalar(
  720. cameraPosition,
  721. remainingDistance,
  722. cameraPosition
  723. );
  724. // Pan
  725. var pMid = scratchPan;
  726. Cartesian3.multiplyByScalar(
  727. Cartesian3.add(
  728. Cartesian3.multiplyByScalar(
  729. up,
  730. Math.cos(beta) - 1,
  731. scratchCartesianTwo
  732. ),
  733. Cartesian3.multiplyByScalar(
  734. forward,
  735. Math.sin(beta),
  736. scratchCartesianThree
  737. ),
  738. scratchCartesian
  739. ),
  740. remainingDistance,
  741. pMid
  742. );
  743. Cartesian3.add(cameraPosition, pMid, cameraPosition);
  744. Cartesian3.normalize(center, up);
  745. Cartesian3.normalize(
  746. Cartesian3.cross(up, right, scratchCartesian),
  747. forward
  748. );
  749. var cMid = scratchCenterMovement;
  750. Cartesian3.multiplyByScalar(
  751. Cartesian3.add(
  752. Cartesian3.multiplyByScalar(
  753. up,
  754. Math.cos(beta) - 1,
  755. scratchCartesianTwo
  756. ),
  757. Cartesian3.multiplyByScalar(
  758. forward,
  759. Math.sin(beta),
  760. scratchCartesianThree
  761. ),
  762. scratchCartesian
  763. ),
  764. Cartesian3.magnitude(center),
  765. cMid
  766. );
  767. Cartesian3.add(center, cMid, center);
  768. // Update camera
  769. // Set new position
  770. Cartesian3.clone(cameraPosition, camera.position);
  771. // Set new direction
  772. Cartesian3.normalize(
  773. Cartesian3.subtract(center, cameraPosition, scratchCartesian),
  774. camera.direction
  775. );
  776. Cartesian3.clone(camera.direction, camera.direction);
  777. // Set new right & up vectors
  778. Cartesian3.cross(camera.direction, camera.up, camera.right);
  779. Cartesian3.cross(camera.right, camera.direction, camera.up);
  780. camera.setView(scratchZoomViewOptions);
  781. return;
  782. }
  783. if (defined(centerPosition)) {
  784. var positionNormal = Cartesian3.normalize(
  785. centerPosition,
  786. scratchPositionNormal
  787. );
  788. var pickedNormal = Cartesian3.normalize(
  789. object._zoomWorldPosition,
  790. scratchPickNormal
  791. );
  792. var dotProduct = Cartesian3.dot(pickedNormal, positionNormal);
  793. if (dotProduct > 0.0 && dotProduct < 1.0) {
  794. var angle = CesiumMath.acosClamped(dotProduct);
  795. var axis = Cartesian3.cross(
  796. pickedNormal,
  797. positionNormal,
  798. scratchZoomAxis
  799. );
  800. var denom =
  801. Math.abs(angle) > CesiumMath.toRadians(20.0)
  802. ? camera.positionCartographic.height * 0.75
  803. : camera.positionCartographic.height - distance;
  804. var scalar = distance / denom;
  805. camera.rotate(axis, angle * scalar);
  806. }
  807. } else {
  808. zoomOnVector = true;
  809. }
  810. }
  811. }
  812. object._rotatingZoom = !zoomOnVector;
  813. }
  814. if ((!sameStartPosition && zoomOnVector) || zoomingOnVector) {
  815. var ray;
  816. var zoomMouseStart = SceneTransforms.wgs84ToWindowCoordinates(
  817. scene,
  818. object._zoomWorldPosition,
  819. scratchZoomOffset
  820. );
  821. if (
  822. mode !== SceneMode.COLUMBUS_VIEW &&
  823. Cartesian2.equals(startPosition, object._zoomMouseStart) &&
  824. defined(zoomMouseStart)
  825. ) {
  826. ray = camera.getPickRay(zoomMouseStart, scratchZoomPickRay);
  827. } else {
  828. ray = camera.getPickRay(startPosition, scratchZoomPickRay);
  829. }
  830. var rayDirection = ray.direction;
  831. if (mode === SceneMode.COLUMBUS_VIEW || mode === SceneMode.SCENE2D) {
  832. Cartesian3.fromElements(
  833. rayDirection.y,
  834. rayDirection.z,
  835. rayDirection.x,
  836. rayDirection
  837. );
  838. }
  839. camera.move(rayDirection, distance);
  840. object._zoomingOnVector = true;
  841. } else {
  842. camera.zoomIn(distance);
  843. }
  844. if (!object._cameraUnderground) {
  845. camera.setView(scratchZoomViewOptions);
  846. }
  847. }
  848. var translate2DStart = new Ray();
  849. var translate2DEnd = new Ray();
  850. var scratchTranslateP0 = new Cartesian3();
  851. function translate2D(controller, startPosition, movement) {
  852. var scene = controller._scene;
  853. var camera = scene.camera;
  854. var start = camera.getPickRay(movement.startPosition, translate2DStart)
  855. .origin;
  856. var end = camera.getPickRay(movement.endPosition, translate2DEnd).origin;
  857. start = Cartesian3.fromElements(start.y, start.z, start.x, start);
  858. end = Cartesian3.fromElements(end.y, end.z, end.x, end);
  859. var direction = Cartesian3.subtract(start, end, scratchTranslateP0);
  860. var distance = Cartesian3.magnitude(direction);
  861. if (distance > 0.0) {
  862. Cartesian3.normalize(direction, direction);
  863. camera.move(direction, distance);
  864. }
  865. }
  866. function zoom2D(controller, startPosition, movement) {
  867. if (defined(movement.distance)) {
  868. movement = movement.distance;
  869. }
  870. var scene = controller._scene;
  871. var camera = scene.camera;
  872. handleZoom(
  873. controller,
  874. startPosition,
  875. movement,
  876. controller._zoomFactor,
  877. camera.getMagnitude()
  878. );
  879. }
  880. var twist2DStart = new Cartesian2();
  881. var twist2DEnd = new Cartesian2();
  882. function twist2D(controller, startPosition, movement) {
  883. if (defined(movement.angleAndHeight)) {
  884. singleAxisTwist2D(controller, startPosition, movement.angleAndHeight);
  885. return;
  886. }
  887. var scene = controller._scene;
  888. var camera = scene.camera;
  889. var canvas = scene.canvas;
  890. var width = canvas.clientWidth;
  891. var height = canvas.clientHeight;
  892. var start = twist2DStart;
  893. start.x = (2.0 / width) * movement.startPosition.x - 1.0;
  894. start.y = (2.0 / height) * (height - movement.startPosition.y) - 1.0;
  895. start = Cartesian2.normalize(start, start);
  896. var end = twist2DEnd;
  897. end.x = (2.0 / width) * movement.endPosition.x - 1.0;
  898. end.y = (2.0 / height) * (height - movement.endPosition.y) - 1.0;
  899. end = Cartesian2.normalize(end, end);
  900. var startTheta = CesiumMath.acosClamped(start.x);
  901. if (start.y < 0) {
  902. startTheta = CesiumMath.TWO_PI - startTheta;
  903. }
  904. var endTheta = CesiumMath.acosClamped(end.x);
  905. if (end.y < 0) {
  906. endTheta = CesiumMath.TWO_PI - endTheta;
  907. }
  908. var theta = endTheta - startTheta;
  909. camera.twistRight(theta);
  910. }
  911. function singleAxisTwist2D(controller, startPosition, movement) {
  912. var rotateRate =
  913. controller._rotateFactor * controller._rotateRateRangeAdjustment;
  914. if (rotateRate > controller._maximumRotateRate) {
  915. rotateRate = controller._maximumRotateRate;
  916. }
  917. if (rotateRate < controller._minimumRotateRate) {
  918. rotateRate = controller._minimumRotateRate;
  919. }
  920. var scene = controller._scene;
  921. var camera = scene.camera;
  922. var canvas = scene.canvas;
  923. var phiWindowRatio =
  924. (movement.endPosition.x - movement.startPosition.x) / canvas.clientWidth;
  925. phiWindowRatio = Math.min(phiWindowRatio, controller.maximumMovementRatio);
  926. var deltaPhi = rotateRate * phiWindowRatio * Math.PI * 4.0;
  927. camera.twistRight(deltaPhi);
  928. }
  929. function update2D(controller) {
  930. var rotatable2D = controller._scene.mapMode2D === MapMode2D.ROTATE;
  931. if (!Matrix4.equals(Matrix4.IDENTITY, controller._scene.camera.transform)) {
  932. reactToInput(
  933. controller,
  934. controller.enableZoom,
  935. controller.zoomEventTypes,
  936. zoom2D,
  937. controller.inertiaZoom,
  938. "_lastInertiaZoomMovement"
  939. );
  940. if (rotatable2D) {
  941. reactToInput(
  942. controller,
  943. controller.enableRotate,
  944. controller.translateEventTypes,
  945. twist2D,
  946. controller.inertiaSpin,
  947. "_lastInertiaSpinMovement"
  948. );
  949. }
  950. } else {
  951. reactToInput(
  952. controller,
  953. controller.enableTranslate,
  954. controller.translateEventTypes,
  955. translate2D,
  956. controller.inertiaTranslate,
  957. "_lastInertiaTranslateMovement"
  958. );
  959. reactToInput(
  960. controller,
  961. controller.enableZoom,
  962. controller.zoomEventTypes,
  963. zoom2D,
  964. controller.inertiaZoom,
  965. "_lastInertiaZoomMovement"
  966. );
  967. if (rotatable2D) {
  968. reactToInput(
  969. controller,
  970. controller.enableRotate,
  971. controller.tiltEventTypes,
  972. twist2D,
  973. controller.inertiaSpin,
  974. "_lastInertiaTiltMovement"
  975. );
  976. }
  977. }
  978. }
  979. var pickGlobeScratchRay = new Ray();
  980. var scratchDepthIntersection = new Cartesian3();
  981. var scratchRayIntersection = new Cartesian3();
  982. function pickGlobe(controller, mousePosition, result) {
  983. var scene = controller._scene;
  984. var globe = controller._globe;
  985. var camera = scene.camera;
  986. if (!defined(globe)) {
  987. return undefined;
  988. }
  989. var cullBackFaces = !controller._cameraUnderground;
  990. var depthIntersection;
  991. if (scene.pickPositionSupported) {
  992. depthIntersection = scene.pickPositionWorldCoordinates(
  993. mousePosition,
  994. scratchDepthIntersection
  995. );
  996. }
  997. var ray = camera.getPickRay(mousePosition, pickGlobeScratchRay);
  998. var rayIntersection = globe.pickWorldCoordinates(
  999. ray,
  1000. scene,
  1001. cullBackFaces,
  1002. scratchRayIntersection
  1003. );
  1004. var pickDistance = defined(depthIntersection)
  1005. ? Cartesian3.distance(depthIntersection, camera.positionWC)
  1006. : Number.POSITIVE_INFINITY;
  1007. var rayDistance = defined(rayIntersection)
  1008. ? Cartesian3.distance(rayIntersection, camera.positionWC)
  1009. : Number.POSITIVE_INFINITY;
  1010. if (pickDistance < rayDistance) {
  1011. return Cartesian3.clone(depthIntersection, result);
  1012. }
  1013. return Cartesian3.clone(rayIntersection, result);
  1014. }
  1015. var scratchDistanceCartographic = new Cartographic();
  1016. function getDistanceFromSurface(controller) {
  1017. var ellipsoid = controller._ellipsoid;
  1018. var scene = controller._scene;
  1019. var camera = scene.camera;
  1020. var mode = scene.mode;
  1021. var height = 0.0;
  1022. if (mode === SceneMode.SCENE3D) {
  1023. var cartographic = ellipsoid.cartesianToCartographic(
  1024. camera.position,
  1025. scratchDistanceCartographic
  1026. );
  1027. if (defined(cartographic)) {
  1028. height = cartographic.height;
  1029. }
  1030. } else {
  1031. height = camera.position.z;
  1032. }
  1033. var globeHeight = defaultValue(controller._scene.globeHeight, 0.0);
  1034. var distanceFromSurface = Math.abs(globeHeight - height);
  1035. return distanceFromSurface;
  1036. }
  1037. var scratchSurfaceNormal = new Cartesian3();
  1038. function getZoomDistanceUnderground(controller, ray) {
  1039. var origin = ray.origin;
  1040. var direction = ray.direction;
  1041. var distanceFromSurface = getDistanceFromSurface(controller);
  1042. // Weight zoom distance based on how strongly the pick ray is pointing inward.
  1043. // Geocentric normal is accurate enough for these purposes
  1044. var surfaceNormal = Cartesian3.normalize(origin, scratchSurfaceNormal);
  1045. var strength = Math.abs(Cartesian3.dot(surfaceNormal, direction));
  1046. strength = Math.max(strength, 0.5) * 2.0;
  1047. return distanceFromSurface * strength;
  1048. }
  1049. function getTiltCenterUnderground(controller, ray, pickedPosition, result) {
  1050. var distance = Cartesian3.distance(ray.origin, pickedPosition);
  1051. var distanceFromSurface = getDistanceFromSurface(controller);
  1052. var maximumDistance = CesiumMath.clamp(
  1053. distanceFromSurface * 5.0,
  1054. controller._minimumUndergroundPickDistance,
  1055. controller._maximumUndergroundPickDistance
  1056. );
  1057. if (distance > maximumDistance) {
  1058. // Simulate look-at behavior by tilting around a small invisible sphere
  1059. distance = Math.min(distance, distanceFromSurface / 5.0);
  1060. distance = Math.max(distance, 100.0);
  1061. }
  1062. return Ray.getPoint(ray, distance, result);
  1063. }
  1064. function getStrafeStartPositionUnderground(
  1065. controller,
  1066. ray,
  1067. pickedPosition,
  1068. result
  1069. ) {
  1070. var distance;
  1071. if (!defined(pickedPosition)) {
  1072. distance = getDistanceFromSurface(controller);
  1073. } else {
  1074. distance = Cartesian3.distance(ray.origin, pickedPosition);
  1075. if (distance > controller._maximumUndergroundPickDistance) {
  1076. // If the picked position is too far away set the strafe speed based on the
  1077. // camera's height from the globe surface
  1078. distance = getDistanceFromSurface(controller);
  1079. }
  1080. }
  1081. return Ray.getPoint(ray, distance, result);
  1082. }
  1083. var scratchInertialDelta = new Cartesian2();
  1084. function continueStrafing(controller, movement) {
  1085. // Update the end position continually based on the inertial delta
  1086. var originalEndPosition = movement.endPosition;
  1087. var inertialDelta = Cartesian2.subtract(
  1088. movement.endPosition,
  1089. movement.startPosition,
  1090. scratchInertialDelta
  1091. );
  1092. var endPosition = controller._strafeEndMousePosition;
  1093. Cartesian2.add(endPosition, inertialDelta, endPosition);
  1094. movement.endPosition = endPosition;
  1095. strafe(controller, movement, controller._strafeStartPosition);
  1096. movement.endPosition = originalEndPosition;
  1097. }
  1098. var translateCVStartRay = new Ray();
  1099. var translateCVEndRay = new Ray();
  1100. var translateCVStartPos = new Cartesian3();
  1101. var translateCVEndPos = new Cartesian3();
  1102. var translateCVDifference = new Cartesian3();
  1103. var translateCVOrigin = new Cartesian3();
  1104. var translateCVPlane = new Plane(Cartesian3.UNIT_X, 0.0);
  1105. var translateCVStartMouse = new Cartesian2();
  1106. var translateCVEndMouse = new Cartesian2();
  1107. function translateCV(controller, startPosition, movement) {
  1108. if (!Cartesian3.equals(startPosition, controller._translateMousePosition)) {
  1109. controller._looking = false;
  1110. }
  1111. if (!Cartesian3.equals(startPosition, controller._strafeMousePosition)) {
  1112. controller._strafing = false;
  1113. }
  1114. if (controller._looking) {
  1115. look3D(controller, startPosition, movement);
  1116. return;
  1117. }
  1118. if (controller._strafing) {
  1119. continueStrafing(controller, movement);
  1120. return;
  1121. }
  1122. var scene = controller._scene;
  1123. var camera = scene.camera;
  1124. var cameraUnderground = controller._cameraUnderground;
  1125. var startMouse = Cartesian2.clone(
  1126. movement.startPosition,
  1127. translateCVStartMouse
  1128. );
  1129. var endMouse = Cartesian2.clone(movement.endPosition, translateCVEndMouse);
  1130. var startRay = camera.getPickRay(startMouse, translateCVStartRay);
  1131. var origin = Cartesian3.clone(Cartesian3.ZERO, translateCVOrigin);
  1132. var normal = Cartesian3.UNIT_X;
  1133. var globePos;
  1134. if (camera.position.z < controller._minimumPickingTerrainHeight) {
  1135. globePos = pickGlobe(controller, startMouse, translateCVStartPos);
  1136. if (defined(globePos)) {
  1137. origin.x = globePos.x;
  1138. }
  1139. }
  1140. if (
  1141. cameraUnderground ||
  1142. (origin.x > camera.position.z && defined(globePos))
  1143. ) {
  1144. var pickPosition = globePos;
  1145. if (cameraUnderground) {
  1146. pickPosition = getStrafeStartPositionUnderground(
  1147. controller,
  1148. startRay,
  1149. globePos,
  1150. translateCVStartPos
  1151. );
  1152. }
  1153. Cartesian2.clone(startPosition, controller._strafeMousePosition);
  1154. Cartesian2.clone(startPosition, controller._strafeEndMousePosition);
  1155. Cartesian3.clone(pickPosition, controller._strafeStartPosition);
  1156. controller._strafing = true;
  1157. strafe(controller, movement, controller._strafeStartPosition);
  1158. return;
  1159. }
  1160. var plane = Plane.fromPointNormal(origin, normal, translateCVPlane);
  1161. startRay = camera.getPickRay(startMouse, translateCVStartRay);
  1162. var startPlanePos = IntersectionTests.rayPlane(
  1163. startRay,
  1164. plane,
  1165. translateCVStartPos
  1166. );
  1167. var endRay = camera.getPickRay(endMouse, translateCVEndRay);
  1168. var endPlanePos = IntersectionTests.rayPlane(
  1169. endRay,
  1170. plane,
  1171. translateCVEndPos
  1172. );
  1173. if (!defined(startPlanePos) || !defined(endPlanePos)) {
  1174. controller._looking = true;
  1175. look3D(controller, startPosition, movement);
  1176. Cartesian2.clone(startPosition, controller._translateMousePosition);
  1177. return;
  1178. }
  1179. var diff = Cartesian3.subtract(
  1180. startPlanePos,
  1181. endPlanePos,
  1182. translateCVDifference
  1183. );
  1184. var temp = diff.x;
  1185. diff.x = diff.y;
  1186. diff.y = diff.z;
  1187. diff.z = temp;
  1188. var mag = Cartesian3.magnitude(diff);
  1189. if (mag > CesiumMath.EPSILON6) {
  1190. Cartesian3.normalize(diff, diff);
  1191. camera.move(diff, mag);
  1192. }
  1193. }
  1194. var rotateCVWindowPos = new Cartesian2();
  1195. var rotateCVWindowRay = new Ray();
  1196. var rotateCVCenter = new Cartesian3();
  1197. var rotateCVVerticalCenter = new Cartesian3();
  1198. var rotateCVTransform = new Matrix4();
  1199. var rotateCVVerticalTransform = new Matrix4();
  1200. var rotateCVOrigin = new Cartesian3();
  1201. var rotateCVPlane = new Plane(Cartesian3.UNIT_X, 0.0);
  1202. var rotateCVCartesian3 = new Cartesian3();
  1203. var rotateCVCart = new Cartographic();
  1204. var rotateCVOldTransform = new Matrix4();
  1205. var rotateCVQuaternion = new Quaternion();
  1206. var rotateCVMatrix = new Matrix3();
  1207. var tilt3DCartesian3 = new Cartesian3();
  1208. function rotateCV(controller, startPosition, movement) {
  1209. if (defined(movement.angleAndHeight)) {
  1210. movement = movement.angleAndHeight;
  1211. }
  1212. if (!Cartesian2.equals(startPosition, controller._tiltCenterMousePosition)) {
  1213. controller._tiltCVOffMap = false;
  1214. controller._looking = false;
  1215. }
  1216. if (controller._looking) {
  1217. look3D(controller, startPosition, movement);
  1218. return;
  1219. }
  1220. var scene = controller._scene;
  1221. var camera = scene.camera;
  1222. if (
  1223. controller._tiltCVOffMap ||
  1224. !controller.onMap() ||
  1225. Math.abs(camera.position.z) > controller._minimumPickingTerrainHeight
  1226. ) {
  1227. controller._tiltCVOffMap = true;
  1228. rotateCVOnPlane(controller, startPosition, movement);
  1229. } else {
  1230. rotateCVOnTerrain(controller, startPosition, movement);
  1231. }
  1232. }
  1233. function rotateCVOnPlane(controller, startPosition, movement) {
  1234. var scene = controller._scene;
  1235. var camera = scene.camera;
  1236. var canvas = scene.canvas;
  1237. var windowPosition = rotateCVWindowPos;
  1238. windowPosition.x = canvas.clientWidth / 2;
  1239. windowPosition.y = canvas.clientHeight / 2;
  1240. var ray = camera.getPickRay(windowPosition, rotateCVWindowRay);
  1241. var normal = Cartesian3.UNIT_X;
  1242. var position = ray.origin;
  1243. var direction = ray.direction;
  1244. var scalar;
  1245. var normalDotDirection = Cartesian3.dot(normal, direction);
  1246. if (Math.abs(normalDotDirection) > CesiumMath.EPSILON6) {
  1247. scalar = -Cartesian3.dot(normal, position) / normalDotDirection;
  1248. }
  1249. if (!defined(scalar) || scalar <= 0.0) {
  1250. controller._looking = true;
  1251. look3D(controller, startPosition, movement);
  1252. Cartesian2.clone(startPosition, controller._tiltCenterMousePosition);
  1253. return;
  1254. }
  1255. var center = Cartesian3.multiplyByScalar(direction, scalar, rotateCVCenter);
  1256. Cartesian3.add(position, center, center);
  1257. var projection = scene.mapProjection;
  1258. var ellipsoid = projection.ellipsoid;
  1259. Cartesian3.fromElements(center.y, center.z, center.x, center);
  1260. var cart = projection.unproject(center, rotateCVCart);
  1261. ellipsoid.cartographicToCartesian(cart, center);
  1262. var transform = Transforms.eastNorthUpToFixedFrame(
  1263. center,
  1264. ellipsoid,
  1265. rotateCVTransform
  1266. );
  1267. var oldGlobe = controller._globe;
  1268. var oldEllipsoid = controller._ellipsoid;
  1269. controller._globe = undefined;
  1270. controller._ellipsoid = Ellipsoid.UNIT_SPHERE;
  1271. controller._rotateFactor = 1.0;
  1272. controller._rotateRateRangeAdjustment = 1.0;
  1273. var oldTransform = Matrix4.clone(camera.transform, rotateCVOldTransform);
  1274. camera._setTransform(transform);
  1275. rotate3D(controller, startPosition, movement, Cartesian3.UNIT_Z);
  1276. camera._setTransform(oldTransform);
  1277. controller._globe = oldGlobe;
  1278. controller._ellipsoid = oldEllipsoid;
  1279. var radius = oldEllipsoid.maximumRadius;
  1280. controller._rotateFactor = 1.0 / radius;
  1281. controller._rotateRateRangeAdjustment = radius;
  1282. }
  1283. function rotateCVOnTerrain(controller, startPosition, movement) {
  1284. var scene = controller._scene;
  1285. var camera = scene.camera;
  1286. var cameraUnderground = controller._cameraUnderground;
  1287. var center;
  1288. var ray;
  1289. var normal = Cartesian3.UNIT_X;
  1290. if (Cartesian2.equals(startPosition, controller._tiltCenterMousePosition)) {
  1291. center = Cartesian3.clone(controller._tiltCenter, rotateCVCenter);
  1292. } else {
  1293. if (camera.position.z < controller._minimumPickingTerrainHeight) {
  1294. center = pickGlobe(controller, startPosition, rotateCVCenter);
  1295. }
  1296. if (!defined(center)) {
  1297. ray = camera.getPickRay(startPosition, rotateCVWindowRay);
  1298. var position = ray.origin;
  1299. var direction = ray.direction;
  1300. var scalar;
  1301. var normalDotDirection = Cartesian3.dot(normal, direction);
  1302. if (Math.abs(normalDotDirection) > CesiumMath.EPSILON6) {
  1303. scalar = -Cartesian3.dot(normal, position) / normalDotDirection;
  1304. }
  1305. if (!defined(scalar) || scalar <= 0.0) {
  1306. controller._looking = true;
  1307. look3D(controller, startPosition, movement);
  1308. Cartesian2.clone(startPosition, controller._tiltCenterMousePosition);
  1309. return;
  1310. }
  1311. center = Cartesian3.multiplyByScalar(direction, scalar, rotateCVCenter);
  1312. Cartesian3.add(position, center, center);
  1313. }
  1314. if (cameraUnderground) {
  1315. if (!defined(ray)) {
  1316. ray = camera.getPickRay(startPosition, rotateCVWindowRay);
  1317. }
  1318. getTiltCenterUnderground(controller, ray, center, center);
  1319. }
  1320. Cartesian2.clone(startPosition, controller._tiltCenterMousePosition);
  1321. Cartesian3.clone(center, controller._tiltCenter);
  1322. }
  1323. var canvas = scene.canvas;
  1324. var windowPosition = rotateCVWindowPos;
  1325. windowPosition.x = canvas.clientWidth / 2;
  1326. windowPosition.y = controller._tiltCenterMousePosition.y;
  1327. ray = camera.getPickRay(windowPosition, rotateCVWindowRay);
  1328. var origin = Cartesian3.clone(Cartesian3.ZERO, rotateCVOrigin);
  1329. origin.x = center.x;
  1330. var plane = Plane.fromPointNormal(origin, normal, rotateCVPlane);
  1331. var verticalCenter = IntersectionTests.rayPlane(
  1332. ray,
  1333. plane,
  1334. rotateCVVerticalCenter
  1335. );
  1336. var projection = camera._projection;
  1337. var ellipsoid = projection.ellipsoid;
  1338. Cartesian3.fromElements(center.y, center.z, center.x, center);
  1339. var cart = projection.unproject(center, rotateCVCart);
  1340. ellipsoid.cartographicToCartesian(cart, center);
  1341. var transform = Transforms.eastNorthUpToFixedFrame(
  1342. center,
  1343. ellipsoid,
  1344. rotateCVTransform
  1345. );
  1346. var verticalTransform;
  1347. if (defined(verticalCenter)) {
  1348. Cartesian3.fromElements(
  1349. verticalCenter.y,
  1350. verticalCenter.z,
  1351. verticalCenter.x,
  1352. verticalCenter
  1353. );
  1354. cart = projection.unproject(verticalCenter, rotateCVCart);
  1355. ellipsoid.cartographicToCartesian(cart, verticalCenter);
  1356. verticalTransform = Transforms.eastNorthUpToFixedFrame(
  1357. verticalCenter,
  1358. ellipsoid,
  1359. rotateCVVerticalTransform
  1360. );
  1361. } else {
  1362. verticalTransform = transform;
  1363. }
  1364. var oldGlobe = controller._globe;
  1365. var oldEllipsoid = controller._ellipsoid;
  1366. controller._globe = undefined;
  1367. controller._ellipsoid = Ellipsoid.UNIT_SPHERE;
  1368. controller._rotateFactor = 1.0;
  1369. controller._rotateRateRangeAdjustment = 1.0;
  1370. var constrainedAxis = Cartesian3.UNIT_Z;
  1371. var oldTransform = Matrix4.clone(camera.transform, rotateCVOldTransform);
  1372. camera._setTransform(transform);
  1373. var tangent = Cartesian3.cross(
  1374. Cartesian3.UNIT_Z,
  1375. Cartesian3.normalize(camera.position, rotateCVCartesian3),
  1376. rotateCVCartesian3
  1377. );
  1378. var dot = Cartesian3.dot(camera.right, tangent);
  1379. rotate3D(controller, startPosition, movement, constrainedAxis, false, true);
  1380. camera._setTransform(verticalTransform);
  1381. if (dot < 0.0) {
  1382. var movementDelta = movement.startPosition.y - movement.endPosition.y;
  1383. if (
  1384. (cameraUnderground && movementDelta < 0.0) ||
  1385. (!cameraUnderground && movementDelta > 0.0)
  1386. ) {
  1387. // Prevent camera from flipping past the up axis
  1388. constrainedAxis = undefined;
  1389. }
  1390. var oldConstrainedAxis = camera.constrainedAxis;
  1391. camera.constrainedAxis = undefined;
  1392. rotate3D(controller, startPosition, movement, constrainedAxis, true, false);
  1393. camera.constrainedAxis = oldConstrainedAxis;
  1394. } else {
  1395. rotate3D(controller, startPosition, movement, constrainedAxis, true, false);
  1396. }
  1397. if (defined(camera.constrainedAxis)) {
  1398. var right = Cartesian3.cross(
  1399. camera.direction,
  1400. camera.constrainedAxis,
  1401. tilt3DCartesian3
  1402. );
  1403. if (
  1404. !Cartesian3.equalsEpsilon(right, Cartesian3.ZERO, CesiumMath.EPSILON6)
  1405. ) {
  1406. if (Cartesian3.dot(right, camera.right) < 0.0) {
  1407. Cartesian3.negate(right, right);
  1408. }
  1409. Cartesian3.cross(right, camera.direction, camera.up);
  1410. Cartesian3.cross(camera.direction, camera.up, camera.right);
  1411. Cartesian3.normalize(camera.up, camera.up);
  1412. Cartesian3.normalize(camera.right, camera.right);
  1413. }
  1414. }
  1415. camera._setTransform(oldTransform);
  1416. controller._globe = oldGlobe;
  1417. controller._ellipsoid = oldEllipsoid;
  1418. var radius = oldEllipsoid.maximumRadius;
  1419. controller._rotateFactor = 1.0 / radius;
  1420. controller._rotateRateRangeAdjustment = radius;
  1421. var originalPosition = Cartesian3.clone(
  1422. camera.positionWC,
  1423. rotateCVCartesian3
  1424. );
  1425. if (controller.enableCollisionDetection) {
  1426. adjustHeightForTerrain(controller);
  1427. }
  1428. if (!Cartesian3.equals(camera.positionWC, originalPosition)) {
  1429. camera._setTransform(verticalTransform);
  1430. camera.worldToCameraCoordinatesPoint(originalPosition, originalPosition);
  1431. var magSqrd = Cartesian3.magnitudeSquared(originalPosition);
  1432. if (Cartesian3.magnitudeSquared(camera.position) > magSqrd) {
  1433. Cartesian3.normalize(camera.position, camera.position);
  1434. Cartesian3.multiplyByScalar(
  1435. camera.position,
  1436. Math.sqrt(magSqrd),
  1437. camera.position
  1438. );
  1439. }
  1440. var angle = Cartesian3.angleBetween(originalPosition, camera.position);
  1441. var axis = Cartesian3.cross(
  1442. originalPosition,
  1443. camera.position,
  1444. originalPosition
  1445. );
  1446. Cartesian3.normalize(axis, axis);
  1447. var quaternion = Quaternion.fromAxisAngle(axis, angle, rotateCVQuaternion);
  1448. var rotation = Matrix3.fromQuaternion(quaternion, rotateCVMatrix);
  1449. Matrix3.multiplyByVector(rotation, camera.direction, camera.direction);
  1450. Matrix3.multiplyByVector(rotation, camera.up, camera.up);
  1451. Cartesian3.cross(camera.direction, camera.up, camera.right);
  1452. Cartesian3.cross(camera.right, camera.direction, camera.up);
  1453. camera._setTransform(oldTransform);
  1454. }
  1455. }
  1456. var zoomCVWindowPos = new Cartesian2();
  1457. var zoomCVWindowRay = new Ray();
  1458. var zoomCVIntersection = new Cartesian3();
  1459. function zoomCV(controller, startPosition, movement) {
  1460. if (defined(movement.distance)) {
  1461. movement = movement.distance;
  1462. }
  1463. var scene = controller._scene;
  1464. var camera = scene.camera;
  1465. var canvas = scene.canvas;
  1466. var cameraUnderground = controller._cameraUnderground;
  1467. var windowPosition;
  1468. if (cameraUnderground) {
  1469. windowPosition = startPosition;
  1470. } else {
  1471. windowPosition = zoomCVWindowPos;
  1472. windowPosition.x = canvas.clientWidth / 2;
  1473. windowPosition.y = canvas.clientHeight / 2;
  1474. }
  1475. var ray = camera.getPickRay(windowPosition, zoomCVWindowRay);
  1476. var position = ray.origin;
  1477. var direction = ray.direction;
  1478. var height = camera.position.z;
  1479. var intersection;
  1480. if (height < controller._minimumPickingTerrainHeight) {
  1481. intersection = pickGlobe(controller, windowPosition, zoomCVIntersection);
  1482. }
  1483. var distance;
  1484. if (defined(intersection)) {
  1485. distance = Cartesian3.distance(position, intersection);
  1486. }
  1487. if (cameraUnderground) {
  1488. var distanceUnderground = getZoomDistanceUnderground(
  1489. controller,
  1490. ray,
  1491. height
  1492. );
  1493. if (defined(distance)) {
  1494. distance = Math.min(distance, distanceUnderground);
  1495. } else {
  1496. distance = distanceUnderground;
  1497. }
  1498. }
  1499. if (!defined(distance)) {
  1500. var normal = Cartesian3.UNIT_X;
  1501. distance =
  1502. -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction);
  1503. }
  1504. handleZoom(
  1505. controller,
  1506. startPosition,
  1507. movement,
  1508. controller._zoomFactor,
  1509. distance
  1510. );
  1511. }
  1512. function updateCV(controller) {
  1513. var scene = controller._scene;
  1514. var camera = scene.camera;
  1515. if (!Matrix4.equals(Matrix4.IDENTITY, camera.transform)) {
  1516. reactToInput(
  1517. controller,
  1518. controller.enableRotate,
  1519. controller.rotateEventTypes,
  1520. rotate3D,
  1521. controller.inertiaSpin,
  1522. "_lastInertiaSpinMovement"
  1523. );
  1524. reactToInput(
  1525. controller,
  1526. controller.enableZoom,
  1527. controller.zoomEventTypes,
  1528. zoom3D,
  1529. controller.inertiaZoom,
  1530. "_lastInertiaZoomMovement"
  1531. );
  1532. } else {
  1533. var tweens = controller._tweens;
  1534. if (controller._aggregator.anyButtonDown) {
  1535. tweens.removeAll();
  1536. }
  1537. reactToInput(
  1538. controller,
  1539. controller.enableTilt,
  1540. controller.tiltEventTypes,
  1541. rotateCV,
  1542. controller.inertiaSpin,
  1543. "_lastInertiaTiltMovement"
  1544. );
  1545. reactToInput(
  1546. controller,
  1547. controller.enableTranslate,
  1548. controller.translateEventTypes,
  1549. translateCV,
  1550. controller.inertiaTranslate,
  1551. "_lastInertiaTranslateMovement"
  1552. );
  1553. reactToInput(
  1554. controller,
  1555. controller.enableZoom,
  1556. controller.zoomEventTypes,
  1557. zoomCV,
  1558. controller.inertiaZoom,
  1559. "_lastInertiaZoomMovement"
  1560. );
  1561. reactToInput(
  1562. controller,
  1563. controller.enableLook,
  1564. controller.lookEventTypes,
  1565. look3D
  1566. );
  1567. if (
  1568. !controller._aggregator.anyButtonDown &&
  1569. !tweens.contains(controller._tween)
  1570. ) {
  1571. var tween = camera.createCorrectPositionTween(
  1572. controller.bounceAnimationTime
  1573. );
  1574. if (defined(tween)) {
  1575. controller._tween = tweens.add(tween);
  1576. }
  1577. }
  1578. tweens.update();
  1579. }
  1580. }
  1581. var scratchStrafeRay = new Ray();
  1582. var scratchStrafePlane = new Plane(Cartesian3.UNIT_X, 0.0);
  1583. var scratchStrafeIntersection = new Cartesian3();
  1584. var scratchStrafeDirection = new Cartesian3();
  1585. var scratchMousePos = new Cartesian3();
  1586. function strafe(controller, movement, strafeStartPosition) {
  1587. var scene = controller._scene;
  1588. var camera = scene.camera;
  1589. var ray = camera.getPickRay(movement.endPosition, scratchStrafeRay);
  1590. var direction = Cartesian3.clone(camera.direction, scratchStrafeDirection);
  1591. if (scene.mode === SceneMode.COLUMBUS_VIEW) {
  1592. Cartesian3.fromElements(direction.z, direction.x, direction.y, direction);
  1593. }
  1594. var plane = Plane.fromPointNormal(
  1595. strafeStartPosition,
  1596. direction,
  1597. scratchStrafePlane
  1598. );
  1599. var intersection = IntersectionTests.rayPlane(
  1600. ray,
  1601. plane,
  1602. scratchStrafeIntersection
  1603. );
  1604. if (!defined(intersection)) {
  1605. return;
  1606. }
  1607. direction = Cartesian3.subtract(strafeStartPosition, intersection, direction);
  1608. if (scene.mode === SceneMode.COLUMBUS_VIEW) {
  1609. Cartesian3.fromElements(direction.y, direction.z, direction.x, direction);
  1610. }
  1611. Cartesian3.add(camera.position, direction, camera.position);
  1612. }
  1613. var spin3DPick = new Cartesian3();
  1614. var scratchCartographic = new Cartographic();
  1615. var scratchRadii = new Cartesian3();
  1616. var scratchEllipsoid = new Ellipsoid();
  1617. var scratchLookUp = new Cartesian3();
  1618. var scratchNormal = new Cartesian3();
  1619. function spin3D(controller, startPosition, movement) {
  1620. var scene = controller._scene;
  1621. var camera = scene.camera;
  1622. var cameraUnderground = controller._cameraUnderground;
  1623. var ellipsoid = controller._ellipsoid;
  1624. if (!Matrix4.equals(camera.transform, Matrix4.IDENTITY)) {
  1625. rotate3D(controller, startPosition, movement);
  1626. return;
  1627. }
  1628. var magnitude;
  1629. var radii;
  1630. var up = ellipsoid.geodeticSurfaceNormal(camera.position, scratchLookUp);
  1631. if (Cartesian2.equals(startPosition, controller._rotateMousePosition)) {
  1632. if (controller._looking) {
  1633. look3D(controller, startPosition, movement, up);
  1634. } else if (controller._rotating) {
  1635. rotate3D(controller, startPosition, movement);
  1636. } else if (controller._strafing) {
  1637. continueStrafing(controller, movement);
  1638. } else {
  1639. if (
  1640. Cartesian3.magnitude(camera.position) <
  1641. Cartesian3.magnitude(controller._rotateStartPosition)
  1642. ) {
  1643. // Pan action is no longer valid if camera moves below the pan ellipsoid
  1644. return;
  1645. }
  1646. magnitude = Cartesian3.magnitude(controller._rotateStartPosition);
  1647. radii = scratchRadii;
  1648. radii.x = radii.y = radii.z = magnitude;
  1649. ellipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid);
  1650. pan3D(controller, startPosition, movement, ellipsoid);
  1651. }
  1652. return;
  1653. }
  1654. controller._looking = false;
  1655. controller._rotating = false;
  1656. controller._strafing = false;
  1657. var height = ellipsoid.cartesianToCartographic(
  1658. camera.positionWC,
  1659. scratchCartographic
  1660. ).height;
  1661. var globe = controller._globe;
  1662. if (defined(globe) && height < controller._minimumPickingTerrainHeight) {
  1663. var mousePos = pickGlobe(
  1664. controller,
  1665. movement.startPosition,
  1666. scratchMousePos
  1667. );
  1668. if (defined(mousePos)) {
  1669. var strafing = false;
  1670. var ray = camera.getPickRay(movement.startPosition, pickGlobeScratchRay);
  1671. if (cameraUnderground) {
  1672. strafing = true;
  1673. getStrafeStartPositionUnderground(controller, ray, mousePos, mousePos);
  1674. } else {
  1675. var normal = ellipsoid.geodeticSurfaceNormal(mousePos, scratchNormal);
  1676. var tangentPick =
  1677. Math.abs(Cartesian3.dot(ray.direction, normal)) < 0.05;
  1678. if (tangentPick) {
  1679. strafing = true;
  1680. } else {
  1681. strafing =
  1682. Cartesian3.magnitude(camera.position) <
  1683. Cartesian3.magnitude(mousePos);
  1684. }
  1685. }
  1686. if (strafing) {
  1687. Cartesian2.clone(startPosition, controller._strafeEndMousePosition);
  1688. Cartesian3.clone(mousePos, controller._strafeStartPosition);
  1689. controller._strafing = true;
  1690. strafe(controller, movement, controller._strafeStartPosition);
  1691. } else {
  1692. magnitude = Cartesian3.magnitude(mousePos);
  1693. radii = scratchRadii;
  1694. radii.x = radii.y = radii.z = magnitude;
  1695. ellipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid);
  1696. pan3D(controller, startPosition, movement, ellipsoid);
  1697. Cartesian3.clone(mousePos, controller._rotateStartPosition);
  1698. }
  1699. } else {
  1700. controller._looking = true;
  1701. look3D(controller, startPosition, movement, up);
  1702. }
  1703. } else if (
  1704. defined(
  1705. camera.pickEllipsoid(
  1706. movement.startPosition,
  1707. controller._ellipsoid,
  1708. spin3DPick
  1709. )
  1710. )
  1711. ) {
  1712. pan3D(controller, startPosition, movement, controller._ellipsoid);
  1713. Cartesian3.clone(spin3DPick, controller._rotateStartPosition);
  1714. } else if (height > controller._minimumTrackBallHeight) {
  1715. controller._rotating = true;
  1716. rotate3D(controller, startPosition, movement);
  1717. } else {
  1718. controller._looking = true;
  1719. look3D(controller, startPosition, movement, up);
  1720. }
  1721. Cartesian2.clone(startPosition, controller._rotateMousePosition);
  1722. }
  1723. function rotate3D(
  1724. controller,
  1725. startPosition,
  1726. movement,
  1727. constrainedAxis,
  1728. rotateOnlyVertical,
  1729. rotateOnlyHorizontal
  1730. ) {
  1731. rotateOnlyVertical = defaultValue(rotateOnlyVertical, false);
  1732. rotateOnlyHorizontal = defaultValue(rotateOnlyHorizontal, false);
  1733. var scene = controller._scene;
  1734. var camera = scene.camera;
  1735. var canvas = scene.canvas;
  1736. var oldAxis = camera.constrainedAxis;
  1737. if (defined(constrainedAxis)) {
  1738. camera.constrainedAxis = constrainedAxis;
  1739. }
  1740. var rho = Cartesian3.magnitude(camera.position);
  1741. var rotateRate =
  1742. controller._rotateFactor * (rho - controller._rotateRateRangeAdjustment);
  1743. if (rotateRate > controller._maximumRotateRate) {
  1744. rotateRate = controller._maximumRotateRate;
  1745. }
  1746. if (rotateRate < controller._minimumRotateRate) {
  1747. rotateRate = controller._minimumRotateRate;
  1748. }
  1749. var phiWindowRatio =
  1750. (movement.startPosition.x - movement.endPosition.x) / canvas.clientWidth;
  1751. var thetaWindowRatio =
  1752. (movement.startPosition.y - movement.endPosition.y) / canvas.clientHeight;
  1753. phiWindowRatio = Math.min(phiWindowRatio, controller.maximumMovementRatio);
  1754. thetaWindowRatio = Math.min(
  1755. thetaWindowRatio,
  1756. controller.maximumMovementRatio
  1757. );
  1758. var deltaPhi = rotateRate * phiWindowRatio * Math.PI * 2.0;
  1759. var deltaTheta = rotateRate * thetaWindowRatio * Math.PI;
  1760. if (!rotateOnlyVertical) {
  1761. camera.rotateRight(deltaPhi);
  1762. }
  1763. if (!rotateOnlyHorizontal) {
  1764. camera.rotateUp(deltaTheta);
  1765. }
  1766. camera.constrainedAxis = oldAxis;
  1767. }
  1768. var pan3DP0 = Cartesian4.clone(Cartesian4.UNIT_W);
  1769. var pan3DP1 = Cartesian4.clone(Cartesian4.UNIT_W);
  1770. var pan3DTemp0 = new Cartesian3();
  1771. var pan3DTemp1 = new Cartesian3();
  1772. var pan3DTemp2 = new Cartesian3();
  1773. var pan3DTemp3 = new Cartesian3();
  1774. var pan3DStartMousePosition = new Cartesian2();
  1775. var pan3DEndMousePosition = new Cartesian2();
  1776. function pan3D(controller, startPosition, movement, ellipsoid) {
  1777. var scene = controller._scene;
  1778. var camera = scene.camera;
  1779. var startMousePosition = Cartesian2.clone(
  1780. movement.startPosition,
  1781. pan3DStartMousePosition
  1782. );
  1783. var endMousePosition = Cartesian2.clone(
  1784. movement.endPosition,
  1785. pan3DEndMousePosition
  1786. );
  1787. var p0 = camera.pickEllipsoid(startMousePosition, ellipsoid, pan3DP0);
  1788. var p1 = camera.pickEllipsoid(endMousePosition, ellipsoid, pan3DP1);
  1789. if (!defined(p0) || !defined(p1)) {
  1790. controller._rotating = true;
  1791. rotate3D(controller, startPosition, movement);
  1792. return;
  1793. }
  1794. p0 = camera.worldToCameraCoordinates(p0, p0);
  1795. p1 = camera.worldToCameraCoordinates(p1, p1);
  1796. if (!defined(camera.constrainedAxis)) {
  1797. Cartesian3.normalize(p0, p0);
  1798. Cartesian3.normalize(p1, p1);
  1799. var dot = Cartesian3.dot(p0, p1);
  1800. var axis = Cartesian3.cross(p0, p1, pan3DTemp0);
  1801. if (
  1802. dot < 1.0 &&
  1803. !Cartesian3.equalsEpsilon(axis, Cartesian3.ZERO, CesiumMath.EPSILON14)
  1804. ) {
  1805. // dot is in [0, 1]
  1806. var angle = Math.acos(dot);
  1807. camera.rotate(axis, angle);
  1808. }
  1809. } else {
  1810. var basis0 = camera.constrainedAxis;
  1811. var basis1 = Cartesian3.mostOrthogonalAxis(basis0, pan3DTemp0);
  1812. Cartesian3.cross(basis1, basis0, basis1);
  1813. Cartesian3.normalize(basis1, basis1);
  1814. var basis2 = Cartesian3.cross(basis0, basis1, pan3DTemp1);
  1815. var startRho = Cartesian3.magnitude(p0);
  1816. var startDot = Cartesian3.dot(basis0, p0);
  1817. var startTheta = Math.acos(startDot / startRho);
  1818. var startRej = Cartesian3.multiplyByScalar(basis0, startDot, pan3DTemp2);
  1819. Cartesian3.subtract(p0, startRej, startRej);
  1820. Cartesian3.normalize(startRej, startRej);
  1821. var endRho = Cartesian3.magnitude(p1);
  1822. var endDot = Cartesian3.dot(basis0, p1);
  1823. var endTheta = Math.acos(endDot / endRho);
  1824. var endRej = Cartesian3.multiplyByScalar(basis0, endDot, pan3DTemp3);
  1825. Cartesian3.subtract(p1, endRej, endRej);
  1826. Cartesian3.normalize(endRej, endRej);
  1827. var startPhi = Math.acos(Cartesian3.dot(startRej, basis1));
  1828. if (Cartesian3.dot(startRej, basis2) < 0) {
  1829. startPhi = CesiumMath.TWO_PI - startPhi;
  1830. }
  1831. var endPhi = Math.acos(Cartesian3.dot(endRej, basis1));
  1832. if (Cartesian3.dot(endRej, basis2) < 0) {
  1833. endPhi = CesiumMath.TWO_PI - endPhi;
  1834. }
  1835. var deltaPhi = startPhi - endPhi;
  1836. var east;
  1837. if (
  1838. Cartesian3.equalsEpsilon(basis0, camera.position, CesiumMath.EPSILON2)
  1839. ) {
  1840. east = camera.right;
  1841. } else {
  1842. east = Cartesian3.cross(basis0, camera.position, pan3DTemp0);
  1843. }
  1844. var planeNormal = Cartesian3.cross(basis0, east, pan3DTemp0);
  1845. var side0 = Cartesian3.dot(
  1846. planeNormal,
  1847. Cartesian3.subtract(p0, basis0, pan3DTemp1)
  1848. );
  1849. var side1 = Cartesian3.dot(
  1850. planeNormal,
  1851. Cartesian3.subtract(p1, basis0, pan3DTemp1)
  1852. );
  1853. var deltaTheta;
  1854. if (side0 > 0 && side1 > 0) {
  1855. deltaTheta = endTheta - startTheta;
  1856. } else if (side0 > 0 && side1 <= 0) {
  1857. if (Cartesian3.dot(camera.position, basis0) > 0) {
  1858. deltaTheta = -startTheta - endTheta;
  1859. } else {
  1860. deltaTheta = startTheta + endTheta;
  1861. }
  1862. } else {
  1863. deltaTheta = startTheta - endTheta;
  1864. }
  1865. camera.rotateRight(deltaPhi);
  1866. camera.rotateUp(deltaTheta);
  1867. }
  1868. }
  1869. var zoom3DUnitPosition = new Cartesian3();
  1870. var zoom3DCartographic = new Cartographic();
  1871. function zoom3D(controller, startPosition, movement) {
  1872. if (defined(movement.distance)) {
  1873. movement = movement.distance;
  1874. }
  1875. var ellipsoid = controller._ellipsoid;
  1876. var scene = controller._scene;
  1877. var camera = scene.camera;
  1878. var canvas = scene.canvas;
  1879. var cameraUnderground = controller._cameraUnderground;
  1880. var windowPosition;
  1881. if (cameraUnderground) {
  1882. windowPosition = startPosition;
  1883. } else {
  1884. windowPosition = zoomCVWindowPos;
  1885. windowPosition.x = canvas.clientWidth / 2;
  1886. windowPosition.y = canvas.clientHeight / 2;
  1887. }
  1888. var ray = camera.getPickRay(windowPosition, zoomCVWindowRay);
  1889. var intersection;
  1890. var height = ellipsoid.cartesianToCartographic(
  1891. camera.position,
  1892. zoom3DCartographic
  1893. ).height;
  1894. if (height < controller._minimumPickingTerrainHeight) {
  1895. intersection = pickGlobe(controller, windowPosition, zoomCVIntersection);
  1896. }
  1897. var distance;
  1898. if (defined(intersection)) {
  1899. distance = Cartesian3.distance(ray.origin, intersection);
  1900. }
  1901. if (cameraUnderground) {
  1902. var distanceUnderground = getZoomDistanceUnderground(
  1903. controller,
  1904. ray,
  1905. height
  1906. );
  1907. if (defined(distance)) {
  1908. distance = Math.min(distance, distanceUnderground);
  1909. } else {
  1910. distance = distanceUnderground;
  1911. }
  1912. }
  1913. if (!defined(distance)) {
  1914. distance = height;
  1915. }
  1916. var unitPosition = Cartesian3.normalize(camera.position, zoom3DUnitPosition);
  1917. handleZoom(
  1918. controller,
  1919. startPosition,
  1920. movement,
  1921. controller._zoomFactor,
  1922. distance,
  1923. Cartesian3.dot(unitPosition, camera.direction)
  1924. );
  1925. }
  1926. var tilt3DWindowPos = new Cartesian2();
  1927. var tilt3DRay = new Ray();
  1928. var tilt3DCenter = new Cartesian3();
  1929. var tilt3DVerticalCenter = new Cartesian3();
  1930. var tilt3DTransform = new Matrix4();
  1931. var tilt3DVerticalTransform = new Matrix4();
  1932. var tilt3DOldTransform = new Matrix4();
  1933. var tilt3DQuaternion = new Quaternion();
  1934. var tilt3DMatrix = new Matrix3();
  1935. var tilt3DCart = new Cartographic();
  1936. var tilt3DLookUp = new Cartesian3();
  1937. function tilt3D(controller, startPosition, movement) {
  1938. var scene = controller._scene;
  1939. var camera = scene.camera;
  1940. if (!Matrix4.equals(camera.transform, Matrix4.IDENTITY)) {
  1941. return;
  1942. }
  1943. if (defined(movement.angleAndHeight)) {
  1944. movement = movement.angleAndHeight;
  1945. }
  1946. if (!Cartesian2.equals(startPosition, controller._tiltCenterMousePosition)) {
  1947. controller._tiltOnEllipsoid = false;
  1948. controller._looking = false;
  1949. }
  1950. if (controller._looking) {
  1951. var up = controller._ellipsoid.geodeticSurfaceNormal(
  1952. camera.position,
  1953. tilt3DLookUp
  1954. );
  1955. look3D(controller, startPosition, movement, up);
  1956. return;
  1957. }
  1958. var ellipsoid = controller._ellipsoid;
  1959. var cartographic = ellipsoid.cartesianToCartographic(
  1960. camera.position,
  1961. tilt3DCart
  1962. );
  1963. if (
  1964. controller._tiltOnEllipsoid ||
  1965. cartographic.height > controller._minimumCollisionTerrainHeight
  1966. ) {
  1967. controller._tiltOnEllipsoid = true;
  1968. tilt3DOnEllipsoid(controller, startPosition, movement);
  1969. } else {
  1970. tilt3DOnTerrain(controller, startPosition, movement);
  1971. }
  1972. }
  1973. var tilt3DOnEllipsoidCartographic = new Cartographic();
  1974. function tilt3DOnEllipsoid(controller, startPosition, movement) {
  1975. var ellipsoid = controller._ellipsoid;
  1976. var scene = controller._scene;
  1977. var camera = scene.camera;
  1978. var minHeight = controller.minimumZoomDistance * 0.25;
  1979. var height = ellipsoid.cartesianToCartographic(
  1980. camera.positionWC,
  1981. tilt3DOnEllipsoidCartographic
  1982. ).height;
  1983. if (
  1984. height - minHeight - 1.0 < CesiumMath.EPSILON3 &&
  1985. movement.endPosition.y - movement.startPosition.y < 0
  1986. ) {
  1987. return;
  1988. }
  1989. var canvas = scene.canvas;
  1990. var windowPosition = tilt3DWindowPos;
  1991. windowPosition.x = canvas.clientWidth / 2;
  1992. windowPosition.y = canvas.clientHeight / 2;
  1993. var ray = camera.getPickRay(windowPosition, tilt3DRay);
  1994. var center;
  1995. var intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid);
  1996. if (defined(intersection)) {
  1997. center = Ray.getPoint(ray, intersection.start, tilt3DCenter);
  1998. } else if (height > controller._minimumTrackBallHeight) {
  1999. var grazingAltitudeLocation = IntersectionTests.grazingAltitudeLocation(
  2000. ray,
  2001. ellipsoid
  2002. );
  2003. if (!defined(grazingAltitudeLocation)) {
  2004. return;
  2005. }
  2006. var grazingAltitudeCart = ellipsoid.cartesianToCartographic(
  2007. grazingAltitudeLocation,
  2008. tilt3DCart
  2009. );
  2010. grazingAltitudeCart.height = 0.0;
  2011. center = ellipsoid.cartographicToCartesian(
  2012. grazingAltitudeCart,
  2013. tilt3DCenter
  2014. );
  2015. } else {
  2016. controller._looking = true;
  2017. var up = controller._ellipsoid.geodeticSurfaceNormal(
  2018. camera.position,
  2019. tilt3DLookUp
  2020. );
  2021. look3D(controller, startPosition, movement, up);
  2022. Cartesian2.clone(startPosition, controller._tiltCenterMousePosition);
  2023. return;
  2024. }
  2025. var transform = Transforms.eastNorthUpToFixedFrame(
  2026. center,
  2027. ellipsoid,
  2028. tilt3DTransform
  2029. );
  2030. var oldGlobe = controller._globe;
  2031. var oldEllipsoid = controller._ellipsoid;
  2032. controller._globe = undefined;
  2033. controller._ellipsoid = Ellipsoid.UNIT_SPHERE;
  2034. controller._rotateFactor = 1.0;
  2035. controller._rotateRateRangeAdjustment = 1.0;
  2036. var oldTransform = Matrix4.clone(camera.transform, tilt3DOldTransform);
  2037. camera._setTransform(transform);
  2038. rotate3D(controller, startPosition, movement, Cartesian3.UNIT_Z);
  2039. camera._setTransform(oldTransform);
  2040. controller._globe = oldGlobe;
  2041. controller._ellipsoid = oldEllipsoid;
  2042. var radius = oldEllipsoid.maximumRadius;
  2043. controller._rotateFactor = 1.0 / radius;
  2044. controller._rotateRateRangeAdjustment = radius;
  2045. }
  2046. function tilt3DOnTerrain(controller, startPosition, movement) {
  2047. var ellipsoid = controller._ellipsoid;
  2048. var scene = controller._scene;
  2049. var camera = scene.camera;
  2050. var cameraUnderground = controller._cameraUnderground;
  2051. var center;
  2052. var ray;
  2053. var intersection;
  2054. if (Cartesian2.equals(startPosition, controller._tiltCenterMousePosition)) {
  2055. center = Cartesian3.clone(controller._tiltCenter, tilt3DCenter);
  2056. } else {
  2057. center = pickGlobe(controller, startPosition, tilt3DCenter);
  2058. if (!defined(center)) {
  2059. ray = camera.getPickRay(startPosition, tilt3DRay);
  2060. intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid);
  2061. if (!defined(intersection)) {
  2062. var cartographic = ellipsoid.cartesianToCartographic(
  2063. camera.position,
  2064. tilt3DCart
  2065. );
  2066. if (cartographic.height <= controller._minimumTrackBallHeight) {
  2067. controller._looking = true;
  2068. var up = controller._ellipsoid.geodeticSurfaceNormal(
  2069. camera.position,
  2070. tilt3DLookUp
  2071. );
  2072. look3D(controller, startPosition, movement, up);
  2073. Cartesian2.clone(startPosition, controller._tiltCenterMousePosition);
  2074. }
  2075. return;
  2076. }
  2077. center = Ray.getPoint(ray, intersection.start, tilt3DCenter);
  2078. }
  2079. if (cameraUnderground) {
  2080. if (!defined(ray)) {
  2081. ray = camera.getPickRay(startPosition, tilt3DRay);
  2082. }
  2083. getTiltCenterUnderground(controller, ray, center, center);
  2084. }
  2085. Cartesian2.clone(startPosition, controller._tiltCenterMousePosition);
  2086. Cartesian3.clone(center, controller._tiltCenter);
  2087. }
  2088. var canvas = scene.canvas;
  2089. var windowPosition = tilt3DWindowPos;
  2090. windowPosition.x = canvas.clientWidth / 2;
  2091. windowPosition.y = controller._tiltCenterMousePosition.y;
  2092. ray = camera.getPickRay(windowPosition, tilt3DRay);
  2093. var mag = Cartesian3.magnitude(center);
  2094. var radii = Cartesian3.fromElements(mag, mag, mag, scratchRadii);
  2095. var newEllipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid);
  2096. intersection = IntersectionTests.rayEllipsoid(ray, newEllipsoid);
  2097. if (!defined(intersection)) {
  2098. return;
  2099. }
  2100. var t =
  2101. Cartesian3.magnitude(ray.origin) > mag
  2102. ? intersection.start
  2103. : intersection.stop;
  2104. var verticalCenter = Ray.getPoint(ray, t, tilt3DVerticalCenter);
  2105. var transform = Transforms.eastNorthUpToFixedFrame(
  2106. center,
  2107. ellipsoid,
  2108. tilt3DTransform
  2109. );
  2110. var verticalTransform = Transforms.eastNorthUpToFixedFrame(
  2111. verticalCenter,
  2112. newEllipsoid,
  2113. tilt3DVerticalTransform
  2114. );
  2115. var oldGlobe = controller._globe;
  2116. var oldEllipsoid = controller._ellipsoid;
  2117. controller._globe = undefined;
  2118. controller._ellipsoid = Ellipsoid.UNIT_SPHERE;
  2119. controller._rotateFactor = 1.0;
  2120. controller._rotateRateRangeAdjustment = 1.0;
  2121. var constrainedAxis = Cartesian3.UNIT_Z;
  2122. var oldTransform = Matrix4.clone(camera.transform, tilt3DOldTransform);
  2123. camera._setTransform(transform);
  2124. var tangent = Cartesian3.cross(
  2125. verticalCenter,
  2126. camera.positionWC,
  2127. tilt3DCartesian3
  2128. );
  2129. var dot = Cartesian3.dot(camera.rightWC, tangent);
  2130. rotate3D(controller, startPosition, movement, constrainedAxis, false, true);
  2131. camera._setTransform(verticalTransform);
  2132. if (dot < 0.0) {
  2133. var movementDelta = movement.startPosition.y - movement.endPosition.y;
  2134. if (
  2135. (cameraUnderground && movementDelta < 0.0) ||
  2136. (!cameraUnderground && movementDelta > 0.0)
  2137. ) {
  2138. // Prevent camera from flipping past the up axis
  2139. constrainedAxis = undefined;
  2140. }
  2141. var oldConstrainedAxis = camera.constrainedAxis;
  2142. camera.constrainedAxis = undefined;
  2143. rotate3D(controller, startPosition, movement, constrainedAxis, true, false);
  2144. camera.constrainedAxis = oldConstrainedAxis;
  2145. } else {
  2146. rotate3D(controller, startPosition, movement, constrainedAxis, true, false);
  2147. }
  2148. if (defined(camera.constrainedAxis)) {
  2149. var right = Cartesian3.cross(
  2150. camera.direction,
  2151. camera.constrainedAxis,
  2152. tilt3DCartesian3
  2153. );
  2154. if (
  2155. !Cartesian3.equalsEpsilon(right, Cartesian3.ZERO, CesiumMath.EPSILON6)
  2156. ) {
  2157. if (Cartesian3.dot(right, camera.right) < 0.0) {
  2158. Cartesian3.negate(right, right);
  2159. }
  2160. Cartesian3.cross(right, camera.direction, camera.up);
  2161. Cartesian3.cross(camera.direction, camera.up, camera.right);
  2162. Cartesian3.normalize(camera.up, camera.up);
  2163. Cartesian3.normalize(camera.right, camera.right);
  2164. }
  2165. }
  2166. camera._setTransform(oldTransform);
  2167. controller._globe = oldGlobe;
  2168. controller._ellipsoid = oldEllipsoid;
  2169. var radius = oldEllipsoid.maximumRadius;
  2170. controller._rotateFactor = 1.0 / radius;
  2171. controller._rotateRateRangeAdjustment = radius;
  2172. var originalPosition = Cartesian3.clone(camera.positionWC, tilt3DCartesian3);
  2173. if (controller.enableCollisionDetection) {
  2174. adjustHeightForTerrain(controller);
  2175. }
  2176. if (!Cartesian3.equals(camera.positionWC, originalPosition)) {
  2177. camera._setTransform(verticalTransform);
  2178. camera.worldToCameraCoordinatesPoint(originalPosition, originalPosition);
  2179. var magSqrd = Cartesian3.magnitudeSquared(originalPosition);
  2180. if (Cartesian3.magnitudeSquared(camera.position) > magSqrd) {
  2181. Cartesian3.normalize(camera.position, camera.position);
  2182. Cartesian3.multiplyByScalar(
  2183. camera.position,
  2184. Math.sqrt(magSqrd),
  2185. camera.position
  2186. );
  2187. }
  2188. var angle = Cartesian3.angleBetween(originalPosition, camera.position);
  2189. var axis = Cartesian3.cross(
  2190. originalPosition,
  2191. camera.position,
  2192. originalPosition
  2193. );
  2194. Cartesian3.normalize(axis, axis);
  2195. var quaternion = Quaternion.fromAxisAngle(axis, angle, tilt3DQuaternion);
  2196. var rotation = Matrix3.fromQuaternion(quaternion, tilt3DMatrix);
  2197. Matrix3.multiplyByVector(rotation, camera.direction, camera.direction);
  2198. Matrix3.multiplyByVector(rotation, camera.up, camera.up);
  2199. Cartesian3.cross(camera.direction, camera.up, camera.right);
  2200. Cartesian3.cross(camera.right, camera.direction, camera.up);
  2201. camera._setTransform(oldTransform);
  2202. }
  2203. }
  2204. var look3DStartPos = new Cartesian2();
  2205. var look3DEndPos = new Cartesian2();
  2206. var look3DStartRay = new Ray();
  2207. var look3DEndRay = new Ray();
  2208. var look3DNegativeRot = new Cartesian3();
  2209. var look3DTan = new Cartesian3();
  2210. function look3D(controller, startPosition, movement, rotationAxis) {
  2211. var scene = controller._scene;
  2212. var camera = scene.camera;
  2213. var startPos = look3DStartPos;
  2214. startPos.x = movement.startPosition.x;
  2215. startPos.y = 0.0;
  2216. var endPos = look3DEndPos;
  2217. endPos.x = movement.endPosition.x;
  2218. endPos.y = 0.0;
  2219. var startRay = camera.getPickRay(startPos, look3DStartRay);
  2220. var endRay = camera.getPickRay(endPos, look3DEndRay);
  2221. var angle = 0.0;
  2222. var start;
  2223. var end;
  2224. if (camera.frustum instanceof OrthographicFrustum) {
  2225. start = startRay.origin;
  2226. end = endRay.origin;
  2227. Cartesian3.add(camera.direction, start, start);
  2228. Cartesian3.add(camera.direction, end, end);
  2229. Cartesian3.subtract(start, camera.position, start);
  2230. Cartesian3.subtract(end, camera.position, end);
  2231. Cartesian3.normalize(start, start);
  2232. Cartesian3.normalize(end, end);
  2233. } else {
  2234. start = startRay.direction;
  2235. end = endRay.direction;
  2236. }
  2237. var dot = Cartesian3.dot(start, end);
  2238. if (dot < 1.0) {
  2239. // dot is in [0, 1]
  2240. angle = Math.acos(dot);
  2241. }
  2242. angle = movement.startPosition.x > movement.endPosition.x ? -angle : angle;
  2243. var horizontalRotationAxis = controller._horizontalRotationAxis;
  2244. if (defined(rotationAxis)) {
  2245. camera.look(rotationAxis, -angle);
  2246. } else if (defined(horizontalRotationAxis)) {
  2247. camera.look(horizontalRotationAxis, -angle);
  2248. } else {
  2249. camera.lookLeft(angle);
  2250. }
  2251. startPos.x = 0.0;
  2252. startPos.y = movement.startPosition.y;
  2253. endPos.x = 0.0;
  2254. endPos.y = movement.endPosition.y;
  2255. startRay = camera.getPickRay(startPos, look3DStartRay);
  2256. endRay = camera.getPickRay(endPos, look3DEndRay);
  2257. angle = 0.0;
  2258. if (camera.frustum instanceof OrthographicFrustum) {
  2259. start = startRay.origin;
  2260. end = endRay.origin;
  2261. Cartesian3.add(camera.direction, start, start);
  2262. Cartesian3.add(camera.direction, end, end);
  2263. Cartesian3.subtract(start, camera.position, start);
  2264. Cartesian3.subtract(end, camera.position, end);
  2265. Cartesian3.normalize(start, start);
  2266. Cartesian3.normalize(end, end);
  2267. } else {
  2268. start = startRay.direction;
  2269. end = endRay.direction;
  2270. }
  2271. dot = Cartesian3.dot(start, end);
  2272. if (dot < 1.0) {
  2273. // dot is in [0, 1]
  2274. angle = Math.acos(dot);
  2275. }
  2276. angle = movement.startPosition.y > movement.endPosition.y ? -angle : angle;
  2277. rotationAxis = defaultValue(rotationAxis, horizontalRotationAxis);
  2278. if (defined(rotationAxis)) {
  2279. var direction = camera.direction;
  2280. var negativeRotationAxis = Cartesian3.negate(
  2281. rotationAxis,
  2282. look3DNegativeRot
  2283. );
  2284. var northParallel = Cartesian3.equalsEpsilon(
  2285. direction,
  2286. rotationAxis,
  2287. CesiumMath.EPSILON2
  2288. );
  2289. var southParallel = Cartesian3.equalsEpsilon(
  2290. direction,
  2291. negativeRotationAxis,
  2292. CesiumMath.EPSILON2
  2293. );
  2294. if (!northParallel && !southParallel) {
  2295. dot = Cartesian3.dot(direction, rotationAxis);
  2296. var angleToAxis = CesiumMath.acosClamped(dot);
  2297. if (angle > 0 && angle > angleToAxis) {
  2298. angle = angleToAxis - CesiumMath.EPSILON4;
  2299. }
  2300. dot = Cartesian3.dot(direction, negativeRotationAxis);
  2301. angleToAxis = CesiumMath.acosClamped(dot);
  2302. if (angle < 0 && -angle > angleToAxis) {
  2303. angle = -angleToAxis + CesiumMath.EPSILON4;
  2304. }
  2305. var tangent = Cartesian3.cross(rotationAxis, direction, look3DTan);
  2306. camera.look(tangent, angle);
  2307. } else if ((northParallel && angle < 0) || (southParallel && angle > 0)) {
  2308. camera.look(camera.right, -angle);
  2309. }
  2310. } else {
  2311. camera.lookUp(angle);
  2312. }
  2313. }
  2314. function update3D(controller) {
  2315. reactToInput(
  2316. controller,
  2317. controller.enableRotate,
  2318. controller.rotateEventTypes,
  2319. spin3D,
  2320. controller.inertiaSpin,
  2321. "_lastInertiaSpinMovement"
  2322. );
  2323. reactToInput(
  2324. controller,
  2325. controller.enableZoom,
  2326. controller.zoomEventTypes,
  2327. zoom3D,
  2328. controller.inertiaZoom,
  2329. "_lastInertiaZoomMovement"
  2330. );
  2331. reactToInput(
  2332. controller,
  2333. controller.enableTilt,
  2334. controller.tiltEventTypes,
  2335. tilt3D,
  2336. controller.inertiaSpin,
  2337. "_lastInertiaTiltMovement"
  2338. );
  2339. reactToInput(
  2340. controller,
  2341. controller.enableLook,
  2342. controller.lookEventTypes,
  2343. look3D
  2344. );
  2345. }
  2346. var scratchAdjustHeightTransform = new Matrix4();
  2347. var scratchAdjustHeightCartographic = new Cartographic();
  2348. function adjustHeightForTerrain(controller) {
  2349. controller._adjustedHeightForTerrain = true;
  2350. var scene = controller._scene;
  2351. var mode = scene.mode;
  2352. var globe = scene.globe;
  2353. if (
  2354. !defined(globe) ||
  2355. mode === SceneMode.SCENE2D ||
  2356. mode === SceneMode.MORPHING
  2357. ) {
  2358. return;
  2359. }
  2360. var camera = scene.camera;
  2361. var ellipsoid = globe.ellipsoid;
  2362. var projection = scene.mapProjection;
  2363. var transform;
  2364. var mag;
  2365. if (!Matrix4.equals(camera.transform, Matrix4.IDENTITY)) {
  2366. transform = Matrix4.clone(camera.transform, scratchAdjustHeightTransform);
  2367. mag = Cartesian3.magnitude(camera.position);
  2368. camera._setTransform(Matrix4.IDENTITY);
  2369. }
  2370. var cartographic = scratchAdjustHeightCartographic;
  2371. if (mode === SceneMode.SCENE3D) {
  2372. ellipsoid.cartesianToCartographic(camera.position, cartographic);
  2373. } else {
  2374. projection.unproject(camera.position, cartographic);
  2375. }
  2376. var heightUpdated = false;
  2377. if (cartographic.height < controller._minimumCollisionTerrainHeight) {
  2378. var globeHeight = controller._scene.globeHeight;
  2379. if (defined(globeHeight)) {
  2380. var height = globeHeight + controller.minimumZoomDistance;
  2381. if (cartographic.height < height) {
  2382. cartographic.height = height;
  2383. if (mode === SceneMode.SCENE3D) {
  2384. ellipsoid.cartographicToCartesian(cartographic, camera.position);
  2385. } else {
  2386. projection.project(cartographic, camera.position);
  2387. }
  2388. heightUpdated = true;
  2389. }
  2390. }
  2391. }
  2392. if (defined(transform)) {
  2393. camera._setTransform(transform);
  2394. if (heightUpdated) {
  2395. Cartesian3.normalize(camera.position, camera.position);
  2396. Cartesian3.negate(camera.position, camera.direction);
  2397. Cartesian3.multiplyByScalar(
  2398. camera.position,
  2399. Math.max(mag, controller.minimumZoomDistance),
  2400. camera.position
  2401. );
  2402. Cartesian3.normalize(camera.direction, camera.direction);
  2403. Cartesian3.cross(camera.direction, camera.up, camera.right);
  2404. Cartesian3.cross(camera.right, camera.direction, camera.up);
  2405. }
  2406. }
  2407. }
  2408. /**
  2409. * @private
  2410. */
  2411. ScreenSpaceCameraController.prototype.onMap = function () {
  2412. var scene = this._scene;
  2413. var mode = scene.mode;
  2414. var camera = scene.camera;
  2415. if (mode === SceneMode.COLUMBUS_VIEW) {
  2416. return (
  2417. Math.abs(camera.position.x) - this._maxCoord.x < 0 &&
  2418. Math.abs(camera.position.y) - this._maxCoord.y < 0
  2419. );
  2420. }
  2421. return true;
  2422. };
  2423. var scratchPreviousPosition = new Cartesian3();
  2424. var scratchPreviousDirection = new Cartesian3();
  2425. /**
  2426. * @private
  2427. */
  2428. ScreenSpaceCameraController.prototype.update = function () {
  2429. var scene = this._scene;
  2430. var camera = scene.camera;
  2431. var globe = scene.globe;
  2432. var mode = scene.mode;
  2433. if (!Matrix4.equals(camera.transform, Matrix4.IDENTITY)) {
  2434. this._globe = undefined;
  2435. this._ellipsoid = Ellipsoid.UNIT_SPHERE;
  2436. } else {
  2437. this._globe = globe;
  2438. this._ellipsoid = defined(this._globe)
  2439. ? this._globe.ellipsoid
  2440. : scene.mapProjection.ellipsoid;
  2441. }
  2442. this._cameraUnderground = scene.cameraUnderground && defined(this._globe);
  2443. this._minimumCollisionTerrainHeight =
  2444. this.minimumCollisionTerrainHeight * scene.terrainExaggeration;
  2445. this._minimumPickingTerrainHeight =
  2446. this.minimumPickingTerrainHeight * scene.terrainExaggeration;
  2447. this._minimumTrackBallHeight =
  2448. this.minimumTrackBallHeight * scene.terrainExaggeration;
  2449. var radius = this._ellipsoid.maximumRadius;
  2450. this._rotateFactor = 1.0 / radius;
  2451. this._rotateRateRangeAdjustment = radius;
  2452. this._adjustedHeightForTerrain = false;
  2453. var previousPosition = Cartesian3.clone(
  2454. camera.positionWC,
  2455. scratchPreviousPosition
  2456. );
  2457. var previousDirection = Cartesian3.clone(
  2458. camera.directionWC,
  2459. scratchPreviousDirection
  2460. );
  2461. if (mode === SceneMode.SCENE2D) {
  2462. update2D(this);
  2463. } else if (mode === SceneMode.COLUMBUS_VIEW) {
  2464. this._horizontalRotationAxis = Cartesian3.UNIT_Z;
  2465. updateCV(this);
  2466. } else if (mode === SceneMode.SCENE3D) {
  2467. this._horizontalRotationAxis = undefined;
  2468. update3D(this);
  2469. }
  2470. if (this.enableCollisionDetection && !this._adjustedHeightForTerrain) {
  2471. // Adjust the camera height if the camera moved at all (user input or inertia) and an action didn't already adjust the camera height
  2472. var cameraChanged =
  2473. !Cartesian3.equals(previousPosition, camera.positionWC) ||
  2474. !Cartesian3.equals(previousDirection, camera.directionWC);
  2475. if (cameraChanged) {
  2476. adjustHeightForTerrain(this);
  2477. }
  2478. }
  2479. this._aggregator.reset();
  2480. };
  2481. /**
  2482. * Returns true if this object was destroyed; otherwise, false.
  2483. * <br /><br />
  2484. * If this object was destroyed, it should not be used; calling any function other than
  2485. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  2486. *
  2487. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  2488. *
  2489. * @see ScreenSpaceCameraController#destroy
  2490. */
  2491. ScreenSpaceCameraController.prototype.isDestroyed = function () {
  2492. return false;
  2493. };
  2494. /**
  2495. * Removes mouse listeners held by this object.
  2496. * <br /><br />
  2497. * Once an object is destroyed, it should not be used; calling any function other than
  2498. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  2499. * assign the return value (<code>undefined</code>) to the object as done in the example.
  2500. *
  2501. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  2502. *
  2503. *
  2504. * @example
  2505. * controller = controller && controller.destroy();
  2506. *
  2507. * @see ScreenSpaceCameraController#isDestroyed
  2508. */
  2509. ScreenSpaceCameraController.prototype.destroy = function () {
  2510. this._tweens.removeAll();
  2511. this._aggregator = this._aggregator && this._aggregator.destroy();
  2512. return destroyObject(this);
  2513. };
  2514. export default ScreenSpaceCameraController;