Camera.js 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097
  1. /*global define*/
  2. define([
  3. '../Core/Cartesian2',
  4. '../Core/Cartesian3',
  5. '../Core/Cartesian4',
  6. '../Core/Cartographic',
  7. '../Core/defaultValue',
  8. '../Core/defined',
  9. '../Core/defineProperties',
  10. '../Core/DeveloperError',
  11. '../Core/EasingFunction',
  12. '../Core/Ellipsoid',
  13. '../Core/IntersectionTests',
  14. '../Core/Math',
  15. '../Core/Matrix3',
  16. '../Core/Matrix4',
  17. '../Core/Quaternion',
  18. '../Core/Ray',
  19. '../Core/Rectangle',
  20. '../Core/Transforms',
  21. './CameraFlightPath',
  22. './PerspectiveFrustum',
  23. './SceneMode'
  24. ], function(
  25. Cartesian2,
  26. Cartesian3,
  27. Cartesian4,
  28. Cartographic,
  29. defaultValue,
  30. defined,
  31. defineProperties,
  32. DeveloperError,
  33. EasingFunction,
  34. Ellipsoid,
  35. IntersectionTests,
  36. CesiumMath,
  37. Matrix3,
  38. Matrix4,
  39. Quaternion,
  40. Ray,
  41. Rectangle,
  42. Transforms,
  43. CameraFlightPath,
  44. PerspectiveFrustum,
  45. SceneMode) {
  46. "use strict";
  47. /**
  48. * The camera is defined by a position, orientation, and view frustum.
  49. * <br /><br />
  50. * The orientation forms an orthonormal basis with a view, up and right = view x up unit vectors.
  51. * <br /><br />
  52. * The viewing frustum is defined by 6 planes.
  53. * Each plane is represented by a {@link Cartesian4} object, where the x, y, and z components
  54. * define the unit vector normal to the plane, and the w component is the distance of the
  55. * plane from the origin/camera position.
  56. *
  57. * @alias Camera
  58. *
  59. * @constructor
  60. *
  61. * @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Camera.html|Cesium Sandcastle Camera Demo}
  62. * @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Camera%20Tutorial.html">Sandcastle Example</a> from the <a href="http://cesiumjs.org/2013/02/13/Cesium-Camera-Tutorial/|Camera Tutorial}
  63. *
  64. * @example
  65. * // Create a camera looking down the negative z-axis, positioned at the origin,
  66. * // with a field of view of 60 degrees, and 1:1 aspect ratio.
  67. * var camera = new Cesium.Camera(scene);
  68. * camera.position = new Cesium.Cartesian3();
  69. * camera.direction = Cesium.Cartesian3.negate(Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3());
  70. * camera.up = Cesium.Cartesian3.clone(Cesium.Cartesian3.UNIT_Y);
  71. * camera.frustum.fov = Cesium.Math.PI_OVER_THREE;
  72. * camera.frustum.near = 1.0;
  73. * camera.frustum.far = 2.0;
  74. */
  75. var Camera = function(scene) {
  76. //>>includeStart('debug', pragmas.debug);
  77. if (!defined(scene)) {
  78. throw new DeveloperError('scene is required.');
  79. }
  80. //>>includeEnd('debug');
  81. this._scene = scene;
  82. /**
  83. * Modifies the camera's reference frame. The inverse of this transformation is appended to the view matrix.
  84. *
  85. * @type {Matrix4}
  86. * @default {@link Matrix4.IDENTITY}
  87. *
  88. * @see Transforms
  89. * @see Camera#inverseTransform
  90. */
  91. this.transform = Matrix4.clone(Matrix4.IDENTITY);
  92. this._transform = Matrix4.clone(Matrix4.IDENTITY);
  93. this._invTransform = Matrix4.clone(Matrix4.IDENTITY);
  94. this._actualTransform = Matrix4.clone(Matrix4.IDENTITY);
  95. this._actualInvTransform = Matrix4.clone(Matrix4.IDENTITY);
  96. /**
  97. * The position of the camera.
  98. *
  99. * @type {Cartesian3}
  100. */
  101. this.position = new Cartesian3();
  102. this._position = new Cartesian3();
  103. this._positionWC = new Cartesian3();
  104. this._positionCartographic = new Cartographic();
  105. /**
  106. * The view direction of the camera.
  107. *
  108. * @type {Cartesian3}
  109. */
  110. this.direction = new Cartesian3();
  111. this._direction = new Cartesian3();
  112. this._directionWC = new Cartesian3();
  113. /**
  114. * The up direction of the camera.
  115. *
  116. * @type {Cartesian3}
  117. */
  118. this.up = new Cartesian3();
  119. this._up = new Cartesian3();
  120. this._upWC = new Cartesian3();
  121. /**
  122. * The right direction of the camera.
  123. *
  124. * @type {Cartesian3}
  125. */
  126. this.right = new Cartesian3();
  127. this._right = new Cartesian3();
  128. this._rightWC = new Cartesian3();
  129. /**
  130. * The region of space in view.
  131. *
  132. * @type {Frustum}
  133. * @default PerspectiveFrustum()
  134. *
  135. * @see PerspectiveFrustum
  136. * @see PerspectiveOffCenterFrustum
  137. * @see OrthographicFrustum
  138. */
  139. this.frustum = new PerspectiveFrustum();
  140. this.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight;
  141. this.frustum.fov = CesiumMath.toRadians(60.0);
  142. /**
  143. * The default amount to move the camera when an argument is not
  144. * provided to the move methods.
  145. * @type {Number}
  146. * @default 100000.0;
  147. */
  148. this.defaultMoveAmount = 100000.0;
  149. /**
  150. * The default amount to rotate the camera when an argument is not
  151. * provided to the look methods.
  152. * @type {Number}
  153. * @default Math.PI / 60.0
  154. */
  155. this.defaultLookAmount = Math.PI / 60.0;
  156. /**
  157. * The default amount to rotate the camera when an argument is not
  158. * provided to the rotate methods.
  159. * @type {Number}
  160. * @default Math.PI / 3600.0
  161. */
  162. this.defaultRotateAmount = Math.PI / 3600.0;
  163. /**
  164. * The default amount to move the camera when an argument is not
  165. * provided to the zoom methods.
  166. * @type {Number}
  167. * @default 100000.0;
  168. */
  169. this.defaultZoomAmount = 100000.0;
  170. /**
  171. * If set, the camera will not be able to rotate past this axis in either direction.
  172. * @type {Cartesian3}
  173. * @default undefined
  174. */
  175. this.constrainedAxis = undefined;
  176. /**
  177. * The factor multiplied by the the map size used to determine where to clamp the camera position
  178. * when translating across the surface. The default is 1.5. Only valid for 2D and Columbus view.
  179. * @type {Number}
  180. * @default 1.5
  181. */
  182. this.maximumTranslateFactor = 1.5;
  183. /**
  184. * The factor multiplied by the the map size used to determine where to clamp the camera position
  185. * when zooming out from the surface. The default is 2.5. Only valid for 2D.
  186. * @type {Number}
  187. * @default 2.5
  188. */
  189. this.maximumZoomFactor = 2.5;
  190. this._viewMatrix = new Matrix4();
  191. this._invViewMatrix = new Matrix4();
  192. updateViewMatrix(this);
  193. this._mode = SceneMode.SCENE3D;
  194. this._modeChanged = true;
  195. var projection = scene.mapProjection;
  196. this._projection = projection;
  197. this._maxCoord = projection.project(new Cartographic(Math.PI, CesiumMath.PI_OVER_TWO));
  198. this._max2Dfrustum = undefined;
  199. // set default view
  200. this.viewRectangle(Camera.DEFAULT_VIEW_RECTANGLE);
  201. var mag = Cartesian3.magnitude(this.position);
  202. mag += mag * Camera.DEFAULT_VIEW_FACTOR;
  203. Cartesian3.normalize(this.position, this.position);
  204. Cartesian3.multiplyByScalar(this.position, mag, this.position);
  205. };
  206. /**
  207. * @private
  208. */
  209. Camera.TRANSFORM_2D = new Matrix4(
  210. 0.0, 0.0, 1.0, 0.0,
  211. 1.0, 0.0, 0.0, 0.0,
  212. 0.0, 1.0, 0.0, 0.0,
  213. 0.0, 0.0, 0.0, 1.0);
  214. /**
  215. * @private
  216. */
  217. Camera.TRANSFORM_2D_INVERSE = Matrix4.inverseTransformation(Camera.TRANSFORM_2D, new Matrix4());
  218. /**
  219. * The default extent the camera will view on creation.
  220. * @type Rectangle
  221. */
  222. Camera.DEFAULT_VIEW_RECTANGLE = Rectangle.fromDegrees(-95.0, -20.0, -70.0, 90.0);
  223. /**
  224. * A scalar to multiply to the camera position and add it back after setting the camera to view the rectangle.
  225. * A value of zero means the camera will view the entire {@link Camera#DEFAULT_VIEW_RECTANGLE}, a value greater than zero
  226. * will move it further away from the extent, and a value less than zero will move it close to the extent.
  227. * @type Number
  228. */
  229. Camera.DEFAULT_VIEW_FACTOR = 0.5;
  230. function updateViewMatrix(camera) {
  231. var r = camera._right;
  232. var u = camera._up;
  233. var d = camera._direction;
  234. var e = camera._position;
  235. var viewMatrix = camera._viewMatrix;
  236. viewMatrix[0] = r.x;
  237. viewMatrix[1] = u.x;
  238. viewMatrix[2] = -d.x;
  239. viewMatrix[3] = 0.0;
  240. viewMatrix[4] = r.y;
  241. viewMatrix[5] = u.y;
  242. viewMatrix[6] = -d.y;
  243. viewMatrix[7] = 0.0;
  244. viewMatrix[8] = r.z;
  245. viewMatrix[9] = u.z;
  246. viewMatrix[10] = -d.z;
  247. viewMatrix[11] = 0.0;
  248. viewMatrix[12] = -Cartesian3.dot(r, e);
  249. viewMatrix[13] = -Cartesian3.dot(u, e);
  250. viewMatrix[14] = Cartesian3.dot(d, e);
  251. viewMatrix[15] = 1.0;
  252. Matrix4.multiply(viewMatrix, camera._actualInvTransform, camera._viewMatrix);
  253. Matrix4.inverseTransformation(camera._viewMatrix, camera._invViewMatrix);
  254. }
  255. var scratchCartographic = new Cartographic();
  256. var scratchCartesian3Projection = new Cartesian3();
  257. var scratchCartesian3 = new Cartesian3();
  258. var scratchCartesian4Origin = new Cartesian4();
  259. var scratchCartesian4NewOrigin = new Cartesian4();
  260. var scratchCartesian4NewXAxis = new Cartesian4();
  261. var scratchCartesian4NewYAxis = new Cartesian4();
  262. var scratchCartesian4NewZAxis = new Cartesian4();
  263. function convertTransformForColumbusView(camera) {
  264. var projection = camera._projection;
  265. var ellipsoid = projection.ellipsoid;
  266. var origin = Matrix4.getColumn(camera._transform, 3, scratchCartesian4Origin);
  267. var cartographic = ellipsoid.cartesianToCartographic(origin, scratchCartographic);
  268. var projectedPosition = projection.project(cartographic, scratchCartesian3Projection);
  269. var newOrigin = scratchCartesian4NewOrigin;
  270. newOrigin.x = projectedPosition.z;
  271. newOrigin.y = projectedPosition.x;
  272. newOrigin.z = projectedPosition.y;
  273. newOrigin.w = 1.0;
  274. var xAxis = Cartesian4.add(Matrix4.getColumn(camera._transform, 0, scratchCartesian3), origin, scratchCartesian3);
  275. ellipsoid.cartesianToCartographic(xAxis, cartographic);
  276. projection.project(cartographic, projectedPosition);
  277. var newXAxis = scratchCartesian4NewXAxis;
  278. newXAxis.x = projectedPosition.z;
  279. newXAxis.y = projectedPosition.x;
  280. newXAxis.z = projectedPosition.y;
  281. newXAxis.w = 0.0;
  282. Cartesian3.subtract(newXAxis, newOrigin, newXAxis);
  283. var yAxis = Cartesian4.add(Matrix4.getColumn(camera._transform, 1, scratchCartesian3), origin, scratchCartesian3);
  284. ellipsoid.cartesianToCartographic(yAxis, cartographic);
  285. projection.project(cartographic, projectedPosition);
  286. var newYAxis = scratchCartesian4NewYAxis;
  287. newYAxis.x = projectedPosition.z;
  288. newYAxis.y = projectedPosition.x;
  289. newYAxis.z = projectedPosition.y;
  290. newYAxis.w = 0.0;
  291. Cartesian3.subtract(newYAxis, newOrigin, newYAxis);
  292. var newZAxis = scratchCartesian4NewZAxis;
  293. Cartesian3.cross(newXAxis, newYAxis, newZAxis);
  294. Cartesian3.normalize(newZAxis, newZAxis);
  295. Cartesian3.cross(newYAxis, newZAxis, newXAxis);
  296. Cartesian3.normalize(newXAxis, newXAxis);
  297. Cartesian3.cross(newZAxis, newXAxis, newYAxis);
  298. Cartesian3.normalize(newYAxis, newYAxis);
  299. Matrix4.setColumn(camera._actualTransform, 0, newXAxis, camera._actualTransform);
  300. Matrix4.setColumn(camera._actualTransform, 1, newYAxis, camera._actualTransform);
  301. Matrix4.setColumn(camera._actualTransform, 2, newZAxis, camera._actualTransform);
  302. Matrix4.setColumn(camera._actualTransform, 3, newOrigin, camera._actualTransform);
  303. }
  304. function convertTransformFor2D(camera) {
  305. var projection = camera._projection;
  306. var ellipsoid = projection.ellipsoid;
  307. var origin = Matrix4.getColumn(camera._transform, 3, scratchCartesian4Origin);
  308. var cartographic = ellipsoid.cartesianToCartographic(origin, scratchCartographic);
  309. var projectedPosition = projection.project(cartographic, scratchCartesian3Projection);
  310. var newOrigin = scratchCartesian4NewOrigin;
  311. newOrigin.x = projectedPosition.z;
  312. newOrigin.y = projectedPosition.x;
  313. newOrigin.z = projectedPosition.y;
  314. newOrigin.w = 1.0;
  315. var newZAxis = Cartesian4.clone(Cartesian4.UNIT_X, scratchCartesian4NewZAxis);
  316. var xAxis = Cartesian4.add(Matrix4.getColumn(camera._transform, 0, scratchCartesian3), origin, scratchCartesian3);
  317. ellipsoid.cartesianToCartographic(xAxis, cartographic);
  318. projection.project(cartographic, projectedPosition);
  319. var newXAxis = scratchCartesian4NewXAxis;
  320. newXAxis.x = projectedPosition.z;
  321. newXAxis.y = projectedPosition.x;
  322. newXAxis.z = projectedPosition.y;
  323. newXAxis.w = 0.0;
  324. Cartesian3.subtract(newXAxis, newOrigin, newXAxis);
  325. newXAxis.x = 0.0;
  326. var newYAxis = scratchCartesian4NewYAxis;
  327. if (Cartesian3.magnitudeSquared(newXAxis) > CesiumMath.EPSILON10) {
  328. Cartesian3.cross(newZAxis, newXAxis, newYAxis);
  329. } else {
  330. var yAxis = Cartesian4.add(Matrix4.getColumn(camera._transform, 1, scratchCartesian3), origin, scratchCartesian3);
  331. ellipsoid.cartesianToCartographic(yAxis, cartographic);
  332. projection.project(cartographic, projectedPosition);
  333. newYAxis.x = projectedPosition.z;
  334. newYAxis.y = projectedPosition.x;
  335. newYAxis.z = projectedPosition.y;
  336. newYAxis.w = 0.0;
  337. Cartesian3.subtract(newYAxis, newOrigin, newYAxis);
  338. newYAxis.x = 0.0;
  339. if (Cartesian3.magnitudeSquared(newYAxis) < CesiumMath.EPSILON10) {
  340. Cartesian4.clone(Cartesian4.UNIT_Y, newXAxis);
  341. Cartesian4.clone(Cartesian4.UNIT_Z, newYAxis);
  342. }
  343. }
  344. Cartesian3.cross(newYAxis, newZAxis, newXAxis);
  345. Cartesian3.normalize(newXAxis, newXAxis);
  346. Cartesian3.cross(newZAxis, newXAxis, newYAxis);
  347. Cartesian3.normalize(newYAxis, newYAxis);
  348. Matrix4.setColumn(camera._actualTransform, 0, newXAxis, camera._actualTransform);
  349. Matrix4.setColumn(camera._actualTransform, 1, newYAxis, camera._actualTransform);
  350. Matrix4.setColumn(camera._actualTransform, 2, newZAxis, camera._actualTransform);
  351. Matrix4.setColumn(camera._actualTransform, 3, newOrigin, camera._actualTransform);
  352. }
  353. var scratchCartesian = new Cartesian3();
  354. function updateMembers(camera) {
  355. var position = camera._position;
  356. var positionChanged = !Cartesian3.equals(position, camera.position);
  357. if (positionChanged) {
  358. position = Cartesian3.clone(camera.position, camera._position);
  359. }
  360. var direction = camera._direction;
  361. var directionChanged = !Cartesian3.equals(direction, camera.direction);
  362. if (directionChanged) {
  363. direction = Cartesian3.clone(camera.direction, camera._direction);
  364. }
  365. var up = camera._up;
  366. var upChanged = !Cartesian3.equals(up, camera.up);
  367. if (upChanged) {
  368. up = Cartesian3.clone(camera.up, camera._up);
  369. }
  370. var right = camera._right;
  371. var rightChanged = !Cartesian3.equals(right, camera.right);
  372. if (rightChanged) {
  373. right = Cartesian3.clone(camera.right, camera._right);
  374. }
  375. var transformChanged = !Matrix4.equals(camera._transform, camera.transform) || camera._modeChanged;
  376. if (transformChanged) {
  377. Matrix4.clone(camera.transform, camera._transform);
  378. Matrix4.inverseTransformation(camera._transform, camera._invTransform);
  379. if (camera._mode === SceneMode.COLUMBUS_VIEW || camera._mode === SceneMode.SCENE2D) {
  380. if (Matrix4.equals(Matrix4.IDENTITY, camera._transform)) {
  381. Matrix4.clone(Camera.TRANSFORM_2D, camera._actualTransform);
  382. } else if (camera._mode === SceneMode.COLUMBUS_VIEW) {
  383. convertTransformForColumbusView(camera);
  384. } else {
  385. convertTransformFor2D(camera);
  386. }
  387. } else {
  388. Matrix4.clone(camera._transform, camera._actualTransform);
  389. }
  390. Matrix4.inverseTransformation(camera._actualTransform, camera._actualInvTransform);
  391. camera._modeChanged = false;
  392. }
  393. var transform = camera._actualTransform;
  394. if (positionChanged || transformChanged) {
  395. camera._positionWC = Matrix4.multiplyByPoint(transform, position, camera._positionWC);
  396. // Compute the Cartographic position of the camera.
  397. var mode = camera._mode;
  398. if (mode === SceneMode.SCENE3D || mode === SceneMode.MORPHING) {
  399. camera._positionCartographic = camera._projection.ellipsoid.cartesianToCartographic(camera._positionWC, camera._positionCartographic);
  400. } else {
  401. // The camera position is expressed in the 2D coordinate system where the Y axis is to the East,
  402. // the Z axis is to the North, and the X axis is out of the map. Express them instead in the ENU axes where
  403. // X is to the East, Y is to the North, and Z is out of the local horizontal plane.
  404. var positionENU = scratchCartesian;
  405. positionENU.x = camera._positionWC.y;
  406. positionENU.y = camera._positionWC.z;
  407. positionENU.z = camera._positionWC.x;
  408. // In 2D, the camera height is always 12.7 million meters.
  409. // The apparent height is equal to half the frustum width.
  410. if (mode === SceneMode.SCENE2D) {
  411. positionENU.z = (camera.frustum.right - camera.frustum.left) * 0.5;
  412. }
  413. camera._projection.unproject(positionENU, camera._positionCartographic);
  414. }
  415. }
  416. if (directionChanged || upChanged || rightChanged) {
  417. var det = Cartesian3.dot(direction, Cartesian3.cross(up, right, scratchCartesian));
  418. if (Math.abs(1.0 - det) > CesiumMath.EPSILON2) {
  419. //orthonormalize axes
  420. direction = Cartesian3.normalize(direction, camera._direction);
  421. Cartesian3.clone(direction, camera.direction);
  422. var invUpMag = 1.0 / Cartesian3.magnitudeSquared(up);
  423. var scalar = Cartesian3.dot(up, direction) * invUpMag;
  424. var w0 = Cartesian3.multiplyByScalar(direction, scalar, scratchCartesian);
  425. up = Cartesian3.normalize(Cartesian3.subtract(up, w0, camera._up), camera._up);
  426. Cartesian3.clone(up, camera.up);
  427. right = Cartesian3.cross(direction, up, camera._right);
  428. Cartesian3.clone(right, camera.right);
  429. }
  430. }
  431. if (directionChanged || transformChanged) {
  432. camera._directionWC = Matrix4.multiplyByPointAsVector(transform, direction, camera._directionWC);
  433. }
  434. if (upChanged || transformChanged) {
  435. camera._upWC = Matrix4.multiplyByPointAsVector(transform, up, camera._upWC);
  436. }
  437. if (rightChanged || transformChanged) {
  438. camera._rightWC = Matrix4.multiplyByPointAsVector(transform, right, camera._rightWC);
  439. }
  440. if (positionChanged || directionChanged || upChanged || rightChanged || transformChanged) {
  441. updateViewMatrix(camera);
  442. }
  443. }
  444. function getHeading2D(camera) {
  445. return Math.atan2(camera.right.y, camera.right.x);
  446. }
  447. var scratchHeadingMatrix4 = new Matrix4();
  448. var scratchHeadingMatrix3 = new Matrix3();
  449. var scratchHeadingCartesian3 = new Cartesian3();
  450. function getHeading3D(camera) {
  451. var ellipsoid = camera._projection.ellipsoid;
  452. var toFixedFrame = Transforms.eastNorthUpToFixedFrame(camera.position, ellipsoid, scratchHeadingMatrix4);
  453. var transform = Matrix4.getRotation(toFixedFrame, scratchHeadingMatrix3);
  454. Matrix3.transpose(transform, transform);
  455. var right = Matrix3.multiplyByVector(transform, camera.right, scratchHeadingCartesian3);
  456. return Math.atan2(right.y, right.x);
  457. }
  458. function setHeading2D(camera, angle) {
  459. var rightAngle = getHeading2D(camera);
  460. angle = rightAngle - angle;
  461. camera.look(Cartesian3.UNIT_Z, angle);
  462. }
  463. var scratchHeadingAxis = new Cartesian3();
  464. function setHeading3D(camera, angle) {
  465. var axis = Cartesian3.normalize(camera.position, scratchHeadingAxis);
  466. var upAngle = getHeading3D(camera);
  467. angle = upAngle - angle;
  468. camera.look(axis, angle);
  469. }
  470. function getTiltCV(camera) {
  471. // CesiumMath.acosClamped(dot(camera.direction, Cartesian3.negate(Cartesian3.UNIT_Z))
  472. return CesiumMath.PI_OVER_TWO - CesiumMath.acosClamped(-camera.direction.z);
  473. }
  474. var scratchTiltCartesian3 = new Cartesian3();
  475. function getTilt3D(camera) {
  476. var direction = Cartesian3.normalize(camera.position, scratchTiltCartesian3);
  477. Cartesian3.negate(direction, direction);
  478. return CesiumMath.PI_OVER_TWO - CesiumMath.acosClamped(Cartesian3.dot(camera.direction, direction));
  479. }
  480. defineProperties(Camera.prototype, {
  481. /**
  482. * Gets the inverse camera transform.
  483. * @memberof Camera.prototype
  484. *
  485. * @type {Matrix4}
  486. * @readonly
  487. *
  488. * @default {@link Matrix4.IDENTITY}
  489. */
  490. inverseTransform : {
  491. get : function() {
  492. updateMembers(this);
  493. return this._invTransform;
  494. }
  495. },
  496. /**
  497. * Gets the view matrix.
  498. * @memberof Camera.prototype
  499. *
  500. * @type {Matrix4}
  501. * @readonly
  502. *
  503. * @see Camera#inverseViewMatrix
  504. */
  505. viewMatrix : {
  506. get : function() {
  507. updateMembers(this);
  508. return this._viewMatrix;
  509. }
  510. },
  511. /**
  512. * Gets the inverse view matrix.
  513. * @memberof Camera.prototype
  514. *
  515. * @type {Matrix4}
  516. * @readonly
  517. *
  518. * @see Camera#viewMatrix
  519. */
  520. inverseViewMatrix : {
  521. get : function() {
  522. updateMembers(this);
  523. return this._invViewMatrix;
  524. }
  525. },
  526. /**
  527. * Gets the {@link Cartographic} position of the camera, with longitude and latitude
  528. * expressed in radians and height in meters. In 2D and Columbus View, it is possible
  529. * for the returned longitude and latitude to be outside the range of valid longitudes
  530. * and latitudes when the camera is outside the map.
  531. * @memberof Camera.prototype
  532. *
  533. * @type {Cartographic}
  534. */
  535. positionCartographic : {
  536. get : function() {
  537. updateMembers(this);
  538. return this._positionCartographic;
  539. }
  540. },
  541. /**
  542. * Gets the position of the camera in world coordinates.
  543. * @memberof Camera.prototype
  544. *
  545. * @type {Cartesian3}
  546. * @readonly
  547. */
  548. positionWC : {
  549. get : function() {
  550. updateMembers(this);
  551. return this._positionWC;
  552. }
  553. },
  554. /**
  555. * Gets the view direction of the camera in world coordinates.
  556. * @memberof Camera.prototype
  557. *
  558. * @type {Cartesian3}
  559. * @readonly
  560. */
  561. directionWC : {
  562. get : function() {
  563. updateMembers(this);
  564. return this._directionWC;
  565. }
  566. },
  567. /**
  568. * Gets the up direction of the camera in world coordinates.
  569. * @memberof Camera.prototype
  570. *
  571. * @type {Cartesian3}
  572. * @readonly
  573. */
  574. upWC : {
  575. get : function() {
  576. updateMembers(this);
  577. return this._upWC;
  578. }
  579. },
  580. /**
  581. * Gets the right direction of the camera in world coordinates.
  582. * @memberof Camera.prototype
  583. *
  584. * @type {Cartesian3}
  585. * @readonly
  586. */
  587. rightWC : {
  588. get : function() {
  589. updateMembers(this);
  590. return this._rightWC;
  591. }
  592. },
  593. /**
  594. * Gets or sets the camera heading in radians.
  595. * @memberof Camera.prototype
  596. *
  597. * @type {Number}
  598. */
  599. heading : {
  600. get : function () {
  601. if (this._mode === SceneMode.SCENE2D || this._mode === SceneMode.COLUMBUS_VIEW) {
  602. return getHeading2D(this);
  603. } else if (this._mode === SceneMode.SCENE3D) {
  604. return getHeading3D(this);
  605. }
  606. return undefined;
  607. },
  608. //TODO See https://github.com/AnalyticalGraphicsInc/cesium/issues/832
  609. set : function (angle) {
  610. //>>includeStart('debug', pragmas.debug);
  611. if (!defined(angle)) {
  612. throw new DeveloperError('angle is required.');
  613. }
  614. //>>includeEnd('debug');
  615. if (this._mode === SceneMode.SCENE2D || this._mode === SceneMode.COLUMBUS_VIEW) {
  616. setHeading2D(this, angle);
  617. } else if (this._mode === SceneMode.SCENE3D) {
  618. setHeading3D(this, angle);
  619. }
  620. }
  621. },
  622. /**
  623. * Gets or sets the camera tilt in radians.
  624. * @memberof Camera.prototype
  625. *
  626. * @type {Number}
  627. */
  628. tilt : {
  629. get : function() {
  630. if (this._mode === SceneMode.COLUMBUS_VIEW) {
  631. return getTiltCV(this);
  632. } else if (this._mode === SceneMode.SCENE3D) {
  633. return getTilt3D(this);
  634. }
  635. return undefined;
  636. },
  637. //TODO See https://github.com/AnalyticalGraphicsInc/cesium/issues/832
  638. set : function(angle) {
  639. //>>includeStart('debug', pragmas.debug);
  640. if (!defined(angle)) {
  641. throw new DeveloperError('angle is required.');
  642. }
  643. //>>includeEnd('debug');
  644. if (this._mode === SceneMode.COLUMBUS_VIEW || this._mode === SceneMode.SCENE3D) {
  645. angle = CesiumMath.clamp(angle, -CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO);
  646. angle = angle - this.tilt;
  647. this.look(this.right, angle);
  648. }
  649. }
  650. }
  651. });
  652. /**
  653. * @private
  654. */
  655. Camera.prototype.update = function(mode) {
  656. //>>includeStart('debug', pragmas.debug);
  657. if (!defined(mode)) {
  658. throw new DeveloperError('mode is required.');
  659. }
  660. //>>includeEnd('debug');
  661. var updateFrustum = false;
  662. if (mode !== this._mode) {
  663. this._mode = mode;
  664. this._modeChanged = mode !== SceneMode.MORPHING;
  665. updateFrustum = this._mode === SceneMode.SCENE2D;
  666. }
  667. if (updateFrustum) {
  668. var frustum = this._max2Dfrustum = this.frustum.clone();
  669. //>>includeStart('debug', pragmas.debug);
  670. if (!defined(frustum.left) || !defined(frustum.right) ||
  671. !defined(frustum.top) || !defined(frustum.bottom)) {
  672. throw new DeveloperError('The camera frustum is expected to be orthographic for 2D camera control.');
  673. }
  674. //>>includeEnd('debug');
  675. var maxZoomOut = 2.0;
  676. var ratio = frustum.top / frustum.right;
  677. frustum.right = this._maxCoord.x * maxZoomOut;
  678. frustum.left = -frustum.right;
  679. frustum.top = ratio * frustum.right;
  680. frustum.bottom = -frustum.top;
  681. }
  682. };
  683. var setTransformPosition = new Cartesian3();
  684. var setTransformUp = new Cartesian3();
  685. var setTransformDirection = new Cartesian3();
  686. /**
  687. * Sets the camera's transform without changing the current view.
  688. *
  689. * @param {Matrix4} transform The camera transform.
  690. */
  691. Camera.prototype.setTransform = function(transform) {
  692. var position = Cartesian3.clone(this.positionWC, setTransformPosition);
  693. var up = Cartesian3.clone(this.upWC, setTransformUp);
  694. var direction = Cartesian3.clone(this.directionWC, setTransformDirection);
  695. Matrix4.clone(transform, this.transform);
  696. updateMembers(this);
  697. var inverse = this._actualInvTransform;
  698. Matrix4.multiplyByPoint(inverse, position, this.position);
  699. Matrix4.multiplyByPointAsVector(inverse, direction, this.direction);
  700. Matrix4.multiplyByPointAsVector(inverse, up, this.up);
  701. Cartesian3.cross(this.direction, this.up, this.right);
  702. };
  703. /**
  704. * Transform a vector or point from world coordinates to the camera's reference frame.
  705. *
  706. * @param {Cartesian4} cartesian The vector or point to transform.
  707. * @param {Cartesian4} [result] The object onto which to store the result.
  708. * @returns {Cartesian4} The transformed vector or point.
  709. */
  710. Camera.prototype.worldToCameraCoordinates = function(cartesian, result) {
  711. //>>includeStart('debug', pragmas.debug);
  712. if (!defined(cartesian)) {
  713. throw new DeveloperError('cartesian is required.');
  714. }
  715. //>>includeEnd('debug');
  716. if (!defined(result)){
  717. result = new Cartesian4();
  718. }
  719. updateMembers(this);
  720. return Matrix4.multiplyByVector(this._actualInvTransform, cartesian, result);
  721. };
  722. /**
  723. * Transform a point from world coordinates to the camera's reference frame.
  724. *
  725. * @param {Cartesian3} cartesian The point to transform.
  726. * @param {Cartesian3} [result] The object onto which to store the result.
  727. * @returns {Cartesian3} The transformed point.
  728. */
  729. Camera.prototype.worldToCameraCoordinatesPoint = function(cartesian, result) {
  730. //>>includeStart('debug', pragmas.debug);
  731. if (!defined(cartesian)) {
  732. throw new DeveloperError('cartesian is required.');
  733. }
  734. //>>includeEnd('debug');
  735. if (!defined(result)){
  736. result = new Cartesian3();
  737. }
  738. updateMembers(this);
  739. return Matrix4.multiplyByPoint(this._actualInvTransform, cartesian, result);
  740. };
  741. /**
  742. * Transform a vector from world coordinates to the camera's reference frame.
  743. *
  744. * @param {Cartesian3} cartesian The vector to transform.
  745. * @param {Cartesian3} [result] The object onto which to store the result.
  746. * @returns {Cartesian3} The transformed vector.
  747. */
  748. Camera.prototype.worldToCameraCoordinatesVector = function(cartesian, result) {
  749. //>>includeStart('debug', pragmas.debug);
  750. if (!defined(cartesian)) {
  751. throw new DeveloperError('cartesian is required.');
  752. }
  753. //>>includeEnd('debug');
  754. if (!defined(result)){
  755. result = new Cartesian3();
  756. }
  757. updateMembers(this);
  758. return Matrix4.multiplyByPointAsVector(this._actualInvTransform, cartesian, result);
  759. };
  760. /**
  761. * Transform a vector or point from the camera's reference frame to world coordinates.
  762. *
  763. * @param {Cartesian4} cartesian The vector or point to transform.
  764. * @param {Cartesian4} [result] The object onto which to store the result.
  765. * @returns {Cartesian4} The transformed vector or point.
  766. */
  767. Camera.prototype.cameraToWorldCoordinates = function(cartesian, result) {
  768. //>>includeStart('debug', pragmas.debug);
  769. if (!defined(cartesian)) {
  770. throw new DeveloperError('cartesian is required.');
  771. }
  772. //>>includeEnd('debug');
  773. if (!defined(result)){
  774. result = new Cartesian4();
  775. }
  776. updateMembers(this);
  777. return Matrix4.multiplyByVector(this._actualTransform, cartesian, result);
  778. };
  779. /**
  780. * Transform a point from the camera's reference frame to world coordinates.
  781. *
  782. * @param {Cartesian3} cartesian The point to transform.
  783. * @param {Cartesian3} [result] The object onto which to store the result.
  784. * @returns {Cartesian3} The transformed point.
  785. */
  786. Camera.prototype.cameraToWorldCoordinatesPoint = function(cartesian, result) {
  787. //>>includeStart('debug', pragmas.debug);
  788. if (!defined(cartesian)) {
  789. throw new DeveloperError('cartesian is required.');
  790. }
  791. //>>includeEnd('debug');
  792. if (!defined(result)){
  793. result = new Cartesian3();
  794. }
  795. updateMembers(this);
  796. return Matrix4.multiplyByPoint(this._actualTransform, cartesian, result);
  797. };
  798. /**
  799. * Transform a vector from the camera's reference frame to world coordinates.
  800. *
  801. * @param {Cartesian3} cartesian The vector to transform.
  802. * @param {Cartesian3} [result] The object onto which to store the result.
  803. * @returns {Cartesian3} The transformed vector.
  804. */
  805. Camera.prototype.cameraToWorldCoordinatesVector = function(cartesian, result) {
  806. //>>includeStart('debug', pragmas.debug);
  807. if (!defined(cartesian)) {
  808. throw new DeveloperError('cartesian is required.');
  809. }
  810. //>>includeEnd('debug');
  811. if (!defined(result)){
  812. result = new Cartesian3();
  813. }
  814. updateMembers(this);
  815. return Matrix4.multiplyByPointAsVector(this._actualTransform, cartesian, result);
  816. };
  817. function clampMove2D(camera, position) {
  818. var maxX = camera._maxCoord.x * camera.maximumTranslateFactor;
  819. if (position.x > maxX) {
  820. position.x = maxX;
  821. }
  822. if (position.x < -maxX) {
  823. position.x = -maxX;
  824. }
  825. var maxY = camera._maxCoord.y * camera.maximumTranslateFactor;
  826. if (position.y > maxY) {
  827. position.y = maxY;
  828. }
  829. if (position.y < -maxY) {
  830. position.y = -maxY;
  831. }
  832. }
  833. var moveScratch = new Cartesian3();
  834. /**
  835. * Translates the camera's position by <code>amount</code> along <code>direction</code>.
  836. *
  837. * @param {Cartesian3} direction The direction to move.
  838. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  839. *
  840. * @see Camera#moveBackward
  841. * @see Camera#moveForward
  842. * @see Camera#moveLeft
  843. * @see Camera#moveRight
  844. * @see Camera#moveUp
  845. * @see Camera#moveDown
  846. */
  847. Camera.prototype.move = function(direction, amount) {
  848. //>>includeStart('debug', pragmas.debug);
  849. if (!defined(direction)) {
  850. throw new DeveloperError('direction is required.');
  851. }
  852. //>>includeEnd('debug');
  853. var cameraPosition = this.position;
  854. Cartesian3.multiplyByScalar(direction, amount, moveScratch);
  855. Cartesian3.add(cameraPosition, moveScratch, cameraPosition);
  856. if (this._mode === SceneMode.SCENE2D) {
  857. clampMove2D(this, cameraPosition);
  858. }
  859. };
  860. /**
  861. * Translates the camera's position by <code>amount</code> along the camera's view vector.
  862. *
  863. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  864. *
  865. * @see Camera#moveBackward
  866. */
  867. Camera.prototype.moveForward = function(amount) {
  868. amount = defaultValue(amount, this.defaultMoveAmount);
  869. this.move(this.direction, amount);
  870. };
  871. /**
  872. * Translates the camera's position by <code>amount</code> along the opposite direction
  873. * of the camera's view vector.
  874. *
  875. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  876. *
  877. * @see Camera#moveForward
  878. */
  879. Camera.prototype.moveBackward = function(amount) {
  880. amount = defaultValue(amount, this.defaultMoveAmount);
  881. this.move(this.direction, -amount);
  882. };
  883. /**
  884. * Translates the camera's position by <code>amount</code> along the camera's up vector.
  885. *
  886. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  887. *
  888. * @see Camera#moveDown
  889. */
  890. Camera.prototype.moveUp = function(amount) {
  891. amount = defaultValue(amount, this.defaultMoveAmount);
  892. this.move(this.up, amount);
  893. };
  894. /**
  895. * Translates the camera's position by <code>amount</code> along the opposite direction
  896. * of the camera's up vector.
  897. *
  898. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  899. *
  900. * @see Camera#moveUp
  901. */
  902. Camera.prototype.moveDown = function(amount) {
  903. amount = defaultValue(amount, this.defaultMoveAmount);
  904. this.move(this.up, -amount);
  905. };
  906. /**
  907. * Translates the camera's position by <code>amount</code> along the camera's right vector.
  908. *
  909. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  910. *
  911. * @see Camera#moveLeft
  912. */
  913. Camera.prototype.moveRight = function(amount) {
  914. amount = defaultValue(amount, this.defaultMoveAmount);
  915. this.move(this.right, amount);
  916. };
  917. /**
  918. * Translates the camera's position by <code>amount</code> along the opposite direction
  919. * of the camera's right vector.
  920. *
  921. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  922. *
  923. * @see Camera#moveRight
  924. */
  925. Camera.prototype.moveLeft = function(amount) {
  926. amount = defaultValue(amount, this.defaultMoveAmount);
  927. this.move(this.right, -amount);
  928. };
  929. /**
  930. * Rotates the camera around its up vector by amount, in radians, in the opposite direction
  931. * of its right vector.
  932. *
  933. * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  934. *
  935. * @see Camera#lookRight
  936. */
  937. Camera.prototype.lookLeft = function(amount) {
  938. amount = defaultValue(amount, this.defaultLookAmount);
  939. this.look(this.up, -amount);
  940. };
  941. /**
  942. * Rotates the camera around its up vector by amount, in radians, in the direction
  943. * of its right vector.
  944. *
  945. * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  946. *
  947. * @see Camera#lookLeft
  948. */
  949. Camera.prototype.lookRight = function(amount) {
  950. amount = defaultValue(amount, this.defaultLookAmount);
  951. this.look(this.up, amount);
  952. };
  953. /**
  954. * Rotates the camera around its right vector by amount, in radians, in the direction
  955. * of its up vector.
  956. *
  957. * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  958. *
  959. * @see Camera#lookDown
  960. */
  961. Camera.prototype.lookUp = function(amount) {
  962. amount = defaultValue(amount, this.defaultLookAmount);
  963. this.look(this.right, -amount);
  964. };
  965. /**
  966. * Rotates the camera around its right vector by amount, in radians, in the opposite direction
  967. * of its up vector.
  968. *
  969. * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  970. *
  971. * @see Camera#lookUp
  972. */
  973. Camera.prototype.lookDown = function(amount) {
  974. amount = defaultValue(amount, this.defaultLookAmount);
  975. this.look(this.right, amount);
  976. };
  977. var lookScratchQuaternion = new Quaternion();
  978. var lookScratchMatrix = new Matrix3();
  979. /**
  980. * Rotate each of the camera's orientation vectors around <code>axis</code> by <code>angle</code>
  981. *
  982. * @param {Cartesian3} axis The axis to rotate around.
  983. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  984. *
  985. * @see Camera#lookUp
  986. * @see Camera#lookDown
  987. * @see Camera#lookLeft
  988. * @see Camera#lookRight
  989. */
  990. Camera.prototype.look = function(axis, angle) {
  991. //>>includeStart('debug', pragmas.debug);
  992. if (!defined(axis)) {
  993. throw new DeveloperError('axis is required.');
  994. }
  995. //>>includeEnd('debug');
  996. var turnAngle = defaultValue(angle, this.defaultLookAmount);
  997. var quaternion = Quaternion.fromAxisAngle(axis, -turnAngle, lookScratchQuaternion);
  998. var rotation = Matrix3.fromQuaternion(quaternion, lookScratchMatrix);
  999. var direction = this.direction;
  1000. var up = this.up;
  1001. var right = this.right;
  1002. Matrix3.multiplyByVector(rotation, direction, direction);
  1003. Matrix3.multiplyByVector(rotation, up, up);
  1004. Matrix3.multiplyByVector(rotation, right, right);
  1005. };
  1006. /**
  1007. * Rotate the camera counter-clockwise around its direction vector by amount, in radians.
  1008. *
  1009. * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  1010. *
  1011. * @see Camera#twistRight
  1012. */
  1013. Camera.prototype.twistLeft = function(amount) {
  1014. amount = defaultValue(amount, this.defaultLookAmount);
  1015. this.look(this.direction, amount);
  1016. };
  1017. /**
  1018. * Rotate the camera clockwise around its direction vector by amount, in radians.
  1019. *
  1020. * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  1021. *
  1022. * @see Camera#twistLeft
  1023. */
  1024. Camera.prototype.twistRight = function(amount) {
  1025. amount = defaultValue(amount, this.defaultLookAmount);
  1026. this.look(this.direction, -amount);
  1027. };
  1028. var rotateScratchQuaternion = new Quaternion();
  1029. var rotateScratchMatrix = new Matrix3();
  1030. /**
  1031. * Rotates the camera around <code>axis</code> by <code>angle</code>. The distance
  1032. * of the camera's position to the center of the camera's reference frame remains the same.
  1033. *
  1034. * @param {Cartesian3} axis The axis to rotate around given in world coordinates.
  1035. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>.
  1036. *
  1037. * @see Camera#rotateUp
  1038. * @see Camera#rotateDown
  1039. * @see Camera#rotateLeft
  1040. * @see Camera#rotateRight
  1041. */
  1042. Camera.prototype.rotate = function(axis, angle) {
  1043. //>>includeStart('debug', pragmas.debug);
  1044. if (!defined(axis)) {
  1045. throw new DeveloperError('axis is required.');
  1046. }
  1047. //>>includeEnd('debug');
  1048. var turnAngle = defaultValue(angle, this.defaultRotateAmount);
  1049. var quaternion = Quaternion.fromAxisAngle(axis, -turnAngle, rotateScratchQuaternion);
  1050. var rotation = Matrix3.fromQuaternion(quaternion, rotateScratchMatrix);
  1051. Matrix3.multiplyByVector(rotation, this.position, this.position);
  1052. Matrix3.multiplyByVector(rotation, this.direction, this.direction);
  1053. Matrix3.multiplyByVector(rotation, this.up, this.up);
  1054. Cartesian3.cross(this.direction, this.up, this.right);
  1055. Cartesian3.cross(this.right, this.direction, this.up);
  1056. };
  1057. /**
  1058. * Rotates the camera around the center of the camera's reference frame by angle downwards.
  1059. *
  1060. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>.
  1061. *
  1062. * @see Camera#rotateUp
  1063. * @see Camera#rotate
  1064. */
  1065. Camera.prototype.rotateDown = function(angle) {
  1066. angle = defaultValue(angle, this.defaultRotateAmount);
  1067. rotateVertical(this, angle);
  1068. };
  1069. /**
  1070. * Rotates the camera around the center of the camera's reference frame by angle upwards.
  1071. *
  1072. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>.
  1073. *
  1074. * @see Camera#rotateDown
  1075. * @see Camera#rotate
  1076. */
  1077. Camera.prototype.rotateUp = function(angle) {
  1078. angle = defaultValue(angle, this.defaultRotateAmount);
  1079. rotateVertical(this, -angle);
  1080. };
  1081. var rotateVertScratchP = new Cartesian3();
  1082. var rotateVertScratchA = new Cartesian3();
  1083. var rotateVertScratchTan = new Cartesian3();
  1084. var rotateVertScratchNegate = new Cartesian3();
  1085. function rotateVertical(camera, angle) {
  1086. var position = camera.position;
  1087. var p = Cartesian3.normalize(position, rotateVertScratchP);
  1088. if (defined(camera.constrainedAxis)) {
  1089. var northParallel = Cartesian3.equalsEpsilon(p, camera.constrainedAxis, CesiumMath.EPSILON2);
  1090. var southParallel = Cartesian3.equalsEpsilon(p, Cartesian3.negate(camera.constrainedAxis, rotateVertScratchNegate), CesiumMath.EPSILON2);
  1091. if ((!northParallel && !southParallel)) {
  1092. var constrainedAxis = Cartesian3.normalize(camera.constrainedAxis, rotateVertScratchA);
  1093. var dot = Cartesian3.dot(p, constrainedAxis);
  1094. var angleToAxis = CesiumMath.acosClamped(dot);
  1095. if (angle > 0 && angle > angleToAxis) {
  1096. angle = angleToAxis - CesiumMath.EPSILON4;
  1097. }
  1098. dot = Cartesian3.dot(p, Cartesian3.negate(constrainedAxis, rotateVertScratchNegate));
  1099. angleToAxis = CesiumMath.acosClamped(dot);
  1100. if (angle < 0 && -angle > angleToAxis) {
  1101. angle = -angleToAxis + CesiumMath.EPSILON4;
  1102. }
  1103. var tangent = Cartesian3.cross(constrainedAxis, p, rotateVertScratchTan);
  1104. camera.rotate(tangent, angle);
  1105. } else if ((northParallel && angle < 0) || (southParallel && angle > 0)) {
  1106. camera.rotate(camera.right, angle);
  1107. }
  1108. } else {
  1109. camera.rotate(camera.right, angle);
  1110. }
  1111. }
  1112. /**
  1113. * Rotates the camera around the center of the camera's reference frame by angle to the right.
  1114. *
  1115. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>.
  1116. *
  1117. * @see Camera#rotateLeft
  1118. * @see Camera#rotate
  1119. */
  1120. Camera.prototype.rotateRight = function(angle) {
  1121. angle = defaultValue(angle, this.defaultRotateAmount);
  1122. rotateHorizontal(this, -angle);
  1123. };
  1124. /**
  1125. * Rotates the camera around the center of the camera's reference frame by angle to the left.
  1126. *
  1127. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>.
  1128. *
  1129. * @see Camera#rotateRight
  1130. * @see Camera#rotate
  1131. */
  1132. Camera.prototype.rotateLeft = function(angle) {
  1133. angle = defaultValue(angle, this.defaultRotateAmount);
  1134. rotateHorizontal(this, angle);
  1135. };
  1136. function rotateHorizontal(camera, angle) {
  1137. if (defined(camera.constrainedAxis)) {
  1138. camera.rotate(camera.constrainedAxis, angle);
  1139. } else {
  1140. camera.rotate(camera.up, angle);
  1141. }
  1142. }
  1143. function zoom2D(camera, amount) {
  1144. var frustum = camera.frustum;
  1145. //>>includeStart('debug', pragmas.debug);
  1146. if (!defined(frustum.left) || !defined(frustum.right) || !defined(frustum.top) || !defined(frustum.bottom)) {
  1147. throw new DeveloperError('The camera frustum is expected to be orthographic for 2D camera control.');
  1148. }
  1149. //>>includeEnd('debug');
  1150. amount = amount * 0.5;
  1151. var newRight = frustum.right - amount;
  1152. var newLeft = frustum.left + amount;
  1153. var maxRight = camera._maxCoord.x * camera.maximumZoomFactor;
  1154. if (newRight > maxRight) {
  1155. newRight = maxRight;
  1156. newLeft = -maxRight;
  1157. }
  1158. if (newRight <= newLeft) {
  1159. newRight = 1.0;
  1160. newLeft = -1.0;
  1161. }
  1162. var ratio = frustum.top / frustum.right;
  1163. frustum.right = newRight;
  1164. frustum.left = newLeft;
  1165. frustum.top = frustum.right * ratio;
  1166. frustum.bottom = -frustum.top;
  1167. }
  1168. function zoom3D(camera, amount) {
  1169. camera.move(camera.direction, amount);
  1170. }
  1171. /**
  1172. * Zooms <code>amount</code> along the camera's view vector.
  1173. *
  1174. * @param {Number} [amount] The amount to move. Defaults to <code>defaultZoomAmount</code>.
  1175. *
  1176. * @see Camera#zoomOut
  1177. */
  1178. Camera.prototype.zoomIn = function(amount) {
  1179. amount = defaultValue(amount, this.defaultZoomAmount);
  1180. if (this._mode === SceneMode.SCENE2D) {
  1181. zoom2D(this, amount);
  1182. } else {
  1183. zoom3D(this, amount);
  1184. }
  1185. };
  1186. /**
  1187. * Zooms <code>amount</code> along the opposite direction of
  1188. * the camera's view vector.
  1189. *
  1190. * @param {Number} [amount] The amount to move. Defaults to <code>defaultZoomAmount</code>.
  1191. *
  1192. * @see Camera#zoomIn
  1193. */
  1194. Camera.prototype.zoomOut = function(amount) {
  1195. amount = defaultValue(amount, this.defaultZoomAmount);
  1196. if (this._mode === SceneMode.SCENE2D) {
  1197. zoom2D(this, -amount);
  1198. } else {
  1199. zoom3D(this, -amount);
  1200. }
  1201. };
  1202. /**
  1203. * Gets the magnitude of the camera position. In 3D, this is the vector magnitude. In 2D and
  1204. * Columbus view, this is the distance to the map.
  1205. *
  1206. * @returns {Number} The magnitude of the position.
  1207. */
  1208. Camera.prototype.getMagnitude = function() {
  1209. if (this._mode === SceneMode.SCENE3D) {
  1210. return Cartesian3.magnitude(this.position);
  1211. } else if (this._mode === SceneMode.COLUMBUS_VIEW) {
  1212. return Math.abs(this.position.z);
  1213. } else if (this._mode === SceneMode.SCENE2D) {
  1214. return Math.max(this.frustum.right - this.frustum.left, this.frustum.top - this.frustum.bottom);
  1215. }
  1216. };
  1217. function setPositionCartographic2D(camera, cartographic) {
  1218. var newLeft = -cartographic.height * 0.5;
  1219. var newRight = -newLeft;
  1220. var frustum = camera.frustum;
  1221. if (newRight > newLeft) {
  1222. var ratio = frustum.top / frustum.right;
  1223. frustum.right = newRight;
  1224. frustum.left = newLeft;
  1225. frustum.top = frustum.right * ratio;
  1226. frustum.bottom = -frustum.top;
  1227. }
  1228. //We use Cartesian2 instead of 3 here because Z must be constant in 2D mode.
  1229. Cartesian2.clone(camera._projection.project(cartographic), camera.position);
  1230. Cartesian3.negate(Cartesian3.UNIT_Z, camera.direction);
  1231. Cartesian3.clone(Cartesian3.UNIT_Y, camera.up);
  1232. Cartesian3.clone(Cartesian3.UNIT_X, camera.right);
  1233. }
  1234. function setPositionCartographicCV(camera, cartographic) {
  1235. var projection = camera._projection;
  1236. camera.position = projection.project(cartographic);
  1237. Cartesian3.negate(Cartesian3.UNIT_Z, camera.direction);
  1238. Cartesian3.clone(Cartesian3.UNIT_Y, camera.up);
  1239. Cartesian3.clone(Cartesian3.UNIT_X, camera.right);
  1240. }
  1241. function setPositionCartographic3D(camera, cartographic) {
  1242. var ellipsoid = camera._projection.ellipsoid;
  1243. ellipsoid.cartographicToCartesian(cartographic, camera.position);
  1244. Cartesian3.negate(camera.position, camera.direction);
  1245. Cartesian3.normalize(camera.direction, camera.direction);
  1246. Cartesian3.cross(camera.direction, Cartesian3.UNIT_Z, camera.right);
  1247. Cartesian3.cross(camera.right, camera.direction, camera.up);
  1248. Cartesian3.cross(camera.direction, camera.up, camera.right);
  1249. }
  1250. /**
  1251. * Moves the camera to the provided cartographic position.
  1252. *
  1253. * @param {Cartographic} cartographic The new camera position.
  1254. */
  1255. Camera.prototype.setPositionCartographic = function(cartographic) {
  1256. //>>includeStart('debug', pragmas.debug);
  1257. if (!defined(cartographic)) {
  1258. throw new DeveloperError('cartographic is required.');
  1259. }
  1260. //>>includeEnd('debug');
  1261. if (this._mode === SceneMode.SCENE2D) {
  1262. setPositionCartographic2D(this, cartographic);
  1263. } else if (this._mode === SceneMode.COLUMBUS_VIEW) {
  1264. setPositionCartographicCV(this, cartographic);
  1265. } else if (this._mode === SceneMode.SCENE3D) {
  1266. setPositionCartographic3D(this, cartographic);
  1267. }
  1268. };
  1269. /**
  1270. * Sets the camera position and orientation with an eye position, target, and up vector.
  1271. * This method is not supported in 2D mode because there is only one direction to look.
  1272. *
  1273. * @param {Cartesian3} eye The position of the camera.
  1274. * @param {Cartesian3} target The position to look at.
  1275. * @param {Cartesian3} up The up vector.
  1276. *
  1277. * @exception {DeveloperError} lookAt is not supported while morphing.
  1278. */
  1279. Camera.prototype.lookAt = function(eye, target, up) {
  1280. //>>includeStart('debug', pragmas.debug);
  1281. if (!defined(eye)) {
  1282. throw new DeveloperError('eye is required');
  1283. }
  1284. if (!defined(target)) {
  1285. throw new DeveloperError('target is required');
  1286. }
  1287. if (!defined(up)) {
  1288. throw new DeveloperError('up is required');
  1289. }
  1290. if (this._mode === SceneMode.MORPHING) {
  1291. throw new DeveloperError('lookAt is not supported while morphing.');
  1292. }
  1293. //>>includeEnd('debug');
  1294. if (this._mode === SceneMode.SCENE2D) {
  1295. Cartesian2.clone(target, this.position);
  1296. Cartesian3.negate(Cartesian3.UNIT_Z, this.direction);
  1297. Cartesian3.clone(up, this.up);
  1298. this.up.z = 0.0;
  1299. if (Cartesian3.magnitudeSquared(this.up) < CesiumMath.EPSILON10) {
  1300. Cartesian3.clone(Cartesian3.UNIT_Y, this.up);
  1301. }
  1302. Cartesian3.cross(this.direction, this.up, this.right);
  1303. var frustum = this.frustum;
  1304. var ratio = frustum.top / frustum.right;
  1305. frustum.right = eye.z;
  1306. frustum.left = -frustum.right;
  1307. frustum.top = ratio * frustum.right;
  1308. frustum.bottom = -frustum.top;
  1309. return;
  1310. }
  1311. this.position = Cartesian3.clone(eye, this.position);
  1312. this.direction = Cartesian3.normalize(Cartesian3.subtract(target, eye, this.direction), this.direction);
  1313. this.right = Cartesian3.normalize(Cartesian3.cross(this.direction, up, this.right), this.right);
  1314. this.up = Cartesian3.cross(this.right, this.direction, this.up);
  1315. };
  1316. var viewRectangle3DCartographic = new Cartographic();
  1317. var viewRectangle3DNorthEast = new Cartesian3();
  1318. var viewRectangle3DSouthWest = new Cartesian3();
  1319. var viewRectangle3DNorthWest = new Cartesian3();
  1320. var viewRectangle3DSouthEast = new Cartesian3();
  1321. var viewRectangle3DCenter = new Cartesian3();
  1322. var defaultRF = {direction: new Cartesian3(), right: new Cartesian3(), up: new Cartesian3()};
  1323. function rectangleCameraPosition3D (camera, rectangle, ellipsoid, result, positionOnly) {
  1324. if (!defined(result)) {
  1325. result = new Cartesian3();
  1326. }
  1327. var cameraRF = camera;
  1328. if (positionOnly) {
  1329. cameraRF = defaultRF;
  1330. }
  1331. var north = rectangle.north;
  1332. var south = rectangle.south;
  1333. var east = rectangle.east;
  1334. var west = rectangle.west;
  1335. // If we go across the International Date Line
  1336. if (west > east) {
  1337. east += CesiumMath.TWO_PI;
  1338. }
  1339. var cart = viewRectangle3DCartographic;
  1340. cart.longitude = east;
  1341. cart.latitude = north;
  1342. var northEast = ellipsoid.cartographicToCartesian(cart, viewRectangle3DNorthEast);
  1343. cart.latitude = south;
  1344. var southEast = ellipsoid.cartographicToCartesian(cart, viewRectangle3DSouthEast);
  1345. cart.longitude = west;
  1346. var southWest = ellipsoid.cartographicToCartesian(cart, viewRectangle3DSouthWest);
  1347. cart.latitude = north;
  1348. var northWest = ellipsoid.cartographicToCartesian(cart, viewRectangle3DNorthWest);
  1349. var center = Cartesian3.subtract(northEast, southWest, viewRectangle3DCenter);
  1350. Cartesian3.multiplyByScalar(center, 0.5, center);
  1351. Cartesian3.add(southWest, center, center);
  1352. var mag = Cartesian3.magnitude(center);
  1353. if (mag < CesiumMath.EPSILON6) {
  1354. cart.longitude = (east + west) * 0.5;
  1355. cart.latitude = (north + south) * 0.5;
  1356. ellipsoid.cartographicToCartesian(cart, center);
  1357. }
  1358. Cartesian3.subtract(northWest, center, northWest);
  1359. Cartesian3.subtract(southEast, center, southEast);
  1360. Cartesian3.subtract(northEast, center, northEast);
  1361. Cartesian3.subtract(southWest, center, southWest);
  1362. var direction = Cartesian3.negate(center, cameraRF.direction);
  1363. Cartesian3.normalize(direction, direction);
  1364. var right = Cartesian3.cross(direction, Cartesian3.UNIT_Z, cameraRF.right);
  1365. Cartesian3.normalize(right, right);
  1366. var up = Cartesian3.cross(right, direction, cameraRF.up);
  1367. var height = Math.max(
  1368. Math.abs(Cartesian3.dot(up, northWest)),
  1369. Math.abs(Cartesian3.dot(up, southEast)),
  1370. Math.abs(Cartesian3.dot(up, northEast)),
  1371. Math.abs(Cartesian3.dot(up, southWest))
  1372. );
  1373. var width = Math.max(
  1374. Math.abs(Cartesian3.dot(right, northWest)),
  1375. Math.abs(Cartesian3.dot(right, southEast)),
  1376. Math.abs(Cartesian3.dot(right, northEast)),
  1377. Math.abs(Cartesian3.dot(right, southWest))
  1378. );
  1379. var tanPhi = Math.tan(camera.frustum.fovy * 0.5);
  1380. var tanTheta = camera.frustum.aspectRatio * tanPhi;
  1381. var d = Math.max(width / tanTheta, height / tanPhi);
  1382. var scalar = mag + d;
  1383. Cartesian3.normalize(center, center);
  1384. return Cartesian3.multiplyByScalar(center, scalar, result);
  1385. }
  1386. var viewRectangleCVCartographic = new Cartographic();
  1387. var viewRectangleCVNorthEast = new Cartesian3();
  1388. var viewRectangleCVSouthWest = new Cartesian3();
  1389. function rectangleCameraPositionColumbusView(camera, rectangle, projection, result, positionOnly) {
  1390. var north = rectangle.north;
  1391. var south = rectangle.south;
  1392. var east = rectangle.east;
  1393. var west = rectangle.west;
  1394. var transform = camera._actualTransform;
  1395. var invTransform = camera._actualInvTransform;
  1396. var cart = viewRectangleCVCartographic;
  1397. cart.longitude = east;
  1398. cart.latitude = north;
  1399. var northEast = projection.project(cart, viewRectangleCVNorthEast);
  1400. Matrix4.multiplyByPoint(transform, northEast, northEast);
  1401. Matrix4.multiplyByPoint(invTransform, northEast, northEast);
  1402. cart.longitude = west;
  1403. cart.latitude = south;
  1404. var southWest = projection.project(cart, viewRectangleCVSouthWest);
  1405. Matrix4.multiplyByPoint(transform, southWest, southWest);
  1406. Matrix4.multiplyByPoint(invTransform, southWest, southWest);
  1407. var tanPhi = Math.tan(camera.frustum.fovy * 0.5);
  1408. var tanTheta = camera.frustum.aspectRatio * tanPhi;
  1409. if (!defined(result)) {
  1410. result = new Cartesian3();
  1411. }
  1412. result.x = (northEast.x - southWest.x) * 0.5 + southWest.x;
  1413. result.y = (northEast.y - southWest.y) * 0.5 + southWest.y;
  1414. result.z = Math.max((northEast.x - southWest.x) / tanTheta, (northEast.y - southWest.y) / tanPhi) * 0.5;
  1415. if (!positionOnly) {
  1416. var direction = Cartesian3.clone(Cartesian3.UNIT_Z, camera.direction);
  1417. Cartesian3.negate(direction, direction);
  1418. Cartesian3.clone(Cartesian3.UNIT_X, camera.right);
  1419. Cartesian3.clone(Cartesian3.UNIT_Y, camera.up);
  1420. }
  1421. return result;
  1422. }
  1423. var viewRectangle2DCartographic = new Cartographic();
  1424. var viewRectangle2DNorthEast = new Cartesian3();
  1425. var viewRectangle2DSouthWest = new Cartesian3();
  1426. function rectangleCameraPosition2D (camera, rectangle, projection, result, positionOnly) {
  1427. var north = rectangle.north;
  1428. var south = rectangle.south;
  1429. var east = rectangle.east;
  1430. var west = rectangle.west;
  1431. var cart = viewRectangle2DCartographic;
  1432. cart.longitude = east;
  1433. cart.latitude = north;
  1434. var northEast = projection.project(cart, viewRectangle2DNorthEast);
  1435. cart.longitude = west;
  1436. cart.latitude = south;
  1437. var southWest = projection.project(cart, viewRectangle2DSouthWest);
  1438. var width = Math.abs(northEast.x - southWest.x) * 0.5;
  1439. var height = Math.abs(northEast.y - southWest.y) * 0.5;
  1440. var right, top;
  1441. var ratio = camera.frustum.right / camera.frustum.top;
  1442. var heightRatio = height * ratio;
  1443. if (width > heightRatio) {
  1444. right = width;
  1445. top = right / ratio;
  1446. } else {
  1447. top = height;
  1448. right = heightRatio;
  1449. }
  1450. height = Math.max(2.0 * right, 2.0 * top);
  1451. if (!defined(result)) {
  1452. result = new Cartesian3();
  1453. }
  1454. result.x = (northEast.x - southWest.x) * 0.5 + southWest.x;
  1455. result.y = (northEast.y - southWest.y) * 0.5 + southWest.y;
  1456. if (positionOnly) {
  1457. cart = projection.unproject(result, cart);
  1458. cart.height = height;
  1459. result = projection.project(cart, result);
  1460. } else {
  1461. var frustum = camera.frustum;
  1462. frustum.right = right;
  1463. frustum.left = -right;
  1464. frustum.top = top;
  1465. frustum.bottom = -top;
  1466. var direction = Cartesian3.clone(Cartesian3.UNIT_Z, camera.direction);
  1467. Cartesian3.negate(direction, direction);
  1468. Cartesian3.clone(Cartesian3.UNIT_X, camera.right);
  1469. Cartesian3.clone(Cartesian3.UNIT_Y, camera.up);
  1470. }
  1471. return result;
  1472. }
  1473. /**
  1474. * Get the camera position needed to view an rectangle on an ellipsoid or map
  1475. *
  1476. * @param {Rectangle} rectangle The rectangle to view.
  1477. * @param {Cartesian3} [result] The camera position needed to view the rectangle
  1478. * @returns {Cartesian3} The camera position needed to view the rectangle
  1479. */
  1480. Camera.prototype.getRectangleCameraCoordinates = function(rectangle, result) {
  1481. //>>includeStart('debug', pragmas.debug);
  1482. if (!defined(rectangle)) {
  1483. throw new DeveloperError('rectangle is required');
  1484. }
  1485. //>>includeEnd('debug');
  1486. if (this._mode === SceneMode.SCENE3D) {
  1487. return rectangleCameraPosition3D(this, rectangle, this._projection.ellipsoid, result, true);
  1488. } else if (this._mode === SceneMode.COLUMBUS_VIEW) {
  1489. return rectangleCameraPositionColumbusView(this, rectangle, this._projection, result, true);
  1490. } else if (this._mode === SceneMode.SCENE2D) {
  1491. return rectangleCameraPosition2D(this, rectangle, this._projection, result, true);
  1492. }
  1493. return undefined;
  1494. };
  1495. /**
  1496. * View an rectangle on an ellipsoid or map.
  1497. *
  1498. * @param {Rectangle} rectangle The rectangle to view.
  1499. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to view.
  1500. */
  1501. Camera.prototype.viewRectangle = function(rectangle, ellipsoid) {
  1502. //>>includeStart('debug', pragmas.debug);
  1503. if (!defined(rectangle)) {
  1504. throw new DeveloperError('rectangle is required.');
  1505. }
  1506. //>>includeEnd('debug');
  1507. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  1508. if (this._mode === SceneMode.SCENE3D) {
  1509. rectangleCameraPosition3D(this, rectangle, ellipsoid, this.position);
  1510. } else if (this._mode === SceneMode.COLUMBUS_VIEW) {
  1511. rectangleCameraPositionColumbusView(this, rectangle, this._projection, this.position);
  1512. } else if (this._mode === SceneMode.SCENE2D) {
  1513. rectangleCameraPosition2D(this, rectangle, this._projection, this.position);
  1514. }
  1515. };
  1516. var pickEllipsoid3DRay = new Ray();
  1517. function pickEllipsoid3D(camera, windowPosition, ellipsoid, result) {
  1518. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  1519. var ray = camera.getPickRay(windowPosition, pickEllipsoid3DRay);
  1520. var intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid);
  1521. if (!intersection) {
  1522. return undefined;
  1523. }
  1524. var t = intersection.start > 0.0 ? intersection.start : intersection.stop;
  1525. return Ray.getPoint(ray, t, result);
  1526. }
  1527. var pickEllipsoid2DRay = new Ray();
  1528. function pickMap2D(camera, windowPosition, projection, result) {
  1529. var ray = camera.getPickRay(windowPosition, pickEllipsoid2DRay);
  1530. var position = ray.origin;
  1531. position.z = 0.0;
  1532. var cart = projection.unproject(position);
  1533. if (cart.latitude < -CesiumMath.PI_OVER_TWO || cart.latitude > CesiumMath.PI_OVER_TWO ||
  1534. cart.longitude < - Math.PI || cart.longitude > Math.PI) {
  1535. return undefined;
  1536. }
  1537. return projection.ellipsoid.cartographicToCartesian(cart, result);
  1538. }
  1539. var pickEllipsoidCVRay = new Ray();
  1540. function pickMapColumbusView(camera, windowPosition, projection, result) {
  1541. var ray = camera.getPickRay(windowPosition, pickEllipsoidCVRay);
  1542. var scalar = -ray.origin.x / ray.direction.x;
  1543. Ray.getPoint(ray, scalar, result);
  1544. var cart = projection.unproject(new Cartesian3(result.y, result.z, 0.0));
  1545. if (cart.latitude < -CesiumMath.PI_OVER_TWO || cart.latitude > CesiumMath.PI_OVER_TWO ||
  1546. cart.longitude < - Math.PI || cart.longitude > Math.PI) {
  1547. return undefined;
  1548. }
  1549. return projection.ellipsoid.cartographicToCartesian(cart, result);
  1550. }
  1551. /**
  1552. * Pick an ellipsoid or map.
  1553. *
  1554. * @param {Cartesian2} windowPosition The x and y coordinates of a pixel.
  1555. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to pick.
  1556. * @param {Cartesian3} [result] The object onto which to store the result.
  1557. * @returns {Cartesian3} If the ellipsoid or map was picked, returns the point on the surface of the ellipsoid or map
  1558. * in world coordinates. If the ellipsoid or map was not picked, returns undefined.
  1559. */
  1560. Camera.prototype.pickEllipsoid = function(windowPosition, ellipsoid, result) {
  1561. //>>includeStart('debug', pragmas.debug);
  1562. if (!defined(windowPosition)) {
  1563. throw new DeveloperError('windowPosition is required.');
  1564. }
  1565. //>>includeEnd('debug');
  1566. if (!defined(result)) {
  1567. result = new Cartesian3();
  1568. }
  1569. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  1570. if (this._mode === SceneMode.SCENE3D) {
  1571. result = pickEllipsoid3D(this, windowPosition, ellipsoid, result);
  1572. } else if (this._mode === SceneMode.SCENE2D) {
  1573. result = pickMap2D(this, windowPosition, this._projection, result);
  1574. } else if (this._mode === SceneMode.COLUMBUS_VIEW) {
  1575. result = pickMapColumbusView(this, windowPosition, this._projection, result);
  1576. } else {
  1577. return undefined;
  1578. }
  1579. return result;
  1580. };
  1581. var pickPerspCenter = new Cartesian3();
  1582. var pickPerspXDir = new Cartesian3();
  1583. var pickPerspYDir = new Cartesian3();
  1584. function getPickRayPerspective(camera, windowPosition, result) {
  1585. var canvas = camera._scene.canvas;
  1586. var width = canvas.clientWidth;
  1587. var height = canvas.clientHeight;
  1588. var tanPhi = Math.tan(camera.frustum.fovy * 0.5);
  1589. var tanTheta = camera.frustum.aspectRatio * tanPhi;
  1590. var near = camera.frustum.near;
  1591. var x = (2.0 / width) * windowPosition.x - 1.0;
  1592. var y = (2.0 / height) * (height - windowPosition.y) - 1.0;
  1593. var position = camera.positionWC;
  1594. Cartesian3.clone(position, result.origin);
  1595. var nearCenter = Cartesian3.multiplyByScalar(camera.directionWC, near, pickPerspCenter);
  1596. Cartesian3.add(position, nearCenter, nearCenter);
  1597. var xDir = Cartesian3.multiplyByScalar(camera.rightWC, x * near * tanTheta, pickPerspXDir);
  1598. var yDir = Cartesian3.multiplyByScalar(camera.upWC, y * near * tanPhi, pickPerspYDir);
  1599. var direction = Cartesian3.add(nearCenter, xDir, result.direction);
  1600. Cartesian3.add(direction, yDir, direction);
  1601. Cartesian3.subtract(direction, position, direction);
  1602. Cartesian3.normalize(direction, direction);
  1603. return result;
  1604. }
  1605. var scratchDirection = new Cartesian3();
  1606. function getPickRayOrthographic(camera, windowPosition, result) {
  1607. var canvas = camera._scene.canvas;
  1608. var width = canvas.clientWidth;
  1609. var height = canvas.clientHeight;
  1610. var x = (2.0 / width) * windowPosition.x - 1.0;
  1611. x *= (camera.frustum.right - camera.frustum.left) * 0.5;
  1612. var y = (2.0 / height) * (height - windowPosition.y) - 1.0;
  1613. y *= (camera.frustum.top - camera.frustum.bottom) * 0.5;
  1614. var origin = result.origin;
  1615. Cartesian3.clone(camera.position, origin);
  1616. Cartesian3.multiplyByScalar(camera.right, x, scratchDirection);
  1617. Cartesian3.add(scratchDirection, origin, origin);
  1618. Cartesian3.multiplyByScalar(camera.up, y, scratchDirection);
  1619. Cartesian3.add(scratchDirection, origin, origin);
  1620. Cartesian3.clone(camera.directionWC, result.direction);
  1621. return result;
  1622. }
  1623. /**
  1624. * Create a ray from the camera position through the pixel at <code>windowPosition</code>
  1625. * in world coordinates.
  1626. *
  1627. * @param {Cartesian2} windowPosition The x and y coordinates of a pixel.
  1628. * @param {Ray} [result] The object onto which to store the result.
  1629. * @returns {Object} Returns the {@link Cartesian3} position and direction of the ray.
  1630. */
  1631. Camera.prototype.getPickRay = function(windowPosition, result) {
  1632. //>>includeStart('debug', pragmas.debug);
  1633. if (!defined(windowPosition)) {
  1634. throw new DeveloperError('windowPosition is required.');
  1635. }
  1636. //>>includeEnd('debug');
  1637. if (!defined(result)) {
  1638. result = new Ray();
  1639. }
  1640. var frustum = this.frustum;
  1641. if (defined(frustum.aspectRatio) && defined(frustum.fov) && defined(frustum.near)) {
  1642. return getPickRayPerspective(this, windowPosition, result);
  1643. }
  1644. return getPickRayOrthographic(this, windowPosition, result);
  1645. };
  1646. function createAnimation2D(camera, duration) {
  1647. var position = camera.position;
  1648. var translateX = position.x < -camera._maxCoord.x || position.x > camera._maxCoord.x;
  1649. var translateY = position.y < -camera._maxCoord.y || position.y > camera._maxCoord.y;
  1650. var animatePosition = translateX || translateY;
  1651. var frustum = camera.frustum;
  1652. var top = frustum.top;
  1653. var bottom = frustum.bottom;
  1654. var right = frustum.right;
  1655. var left = frustum.left;
  1656. var startFrustum = camera._max2Dfrustum;
  1657. var animateFrustum = right > camera._max2Dfrustum.right;
  1658. if (animatePosition || animateFrustum) {
  1659. var translatedPosition = Cartesian3.clone(position);
  1660. if (translatedPosition.x > camera._maxCoord.x) {
  1661. translatedPosition.x = camera._maxCoord.x;
  1662. } else if (translatedPosition.x < -camera._maxCoord.x) {
  1663. translatedPosition.x = -camera._maxCoord.x;
  1664. }
  1665. if (translatedPosition.y > camera._maxCoord.y) {
  1666. translatedPosition.y = camera._maxCoord.y;
  1667. } else if (translatedPosition.y < -camera._maxCoord.y) {
  1668. translatedPosition.y = -camera._maxCoord.y;
  1669. }
  1670. var update2D = function(value) {
  1671. if (animatePosition) {
  1672. camera.position = Cartesian3.lerp(position, translatedPosition, value.time, camera.position);
  1673. }
  1674. if (animateFrustum) {
  1675. camera.frustum.top = CesiumMath.lerp(top, startFrustum.top, value.time);
  1676. camera.frustum.bottom = CesiumMath.lerp(bottom, startFrustum.bottom, value.time);
  1677. camera.frustum.right = CesiumMath.lerp(right, startFrustum.right, value.time);
  1678. camera.frustum.left = CesiumMath.lerp(left, startFrustum.left, value.time);
  1679. }
  1680. };
  1681. return {
  1682. easingFunction : EasingFunction.EXPONENTIAL_OUT,
  1683. startObject : {
  1684. time : 0.0
  1685. },
  1686. stopObject : {
  1687. time : 1.0
  1688. },
  1689. duration : duration,
  1690. update : update2D
  1691. };
  1692. }
  1693. return undefined;
  1694. }
  1695. function createAnimationTemplateCV(camera, position, center, maxX, maxY, duration) {
  1696. var newPosition = Cartesian3.clone(position);
  1697. if (center.y > maxX) {
  1698. newPosition.y -= center.y - maxX;
  1699. } else if (center.y < -maxX) {
  1700. newPosition.y += -maxX - center.y;
  1701. }
  1702. if (center.z > maxY) {
  1703. newPosition.z -= center.z - maxY;
  1704. } else if (center.z < -maxY) {
  1705. newPosition.z += -maxY - center.z;
  1706. }
  1707. var updateCV = function(value) {
  1708. var interp = Cartesian3.lerp(position, newPosition, value.time, new Cartesian3());
  1709. camera.worldToCameraCoordinatesPoint(interp, camera.position);
  1710. };
  1711. return {
  1712. easingFunction : EasingFunction.EXPONENTIAL_OUT,
  1713. startObject : {
  1714. time : 0.0
  1715. },
  1716. stopObject : {
  1717. time : 1.0
  1718. },
  1719. duration : duration,
  1720. update : updateCV
  1721. };
  1722. }
  1723. var normalScratch = new Cartesian3();
  1724. var centerScratch = new Cartesian3();
  1725. var posScratch = new Cartesian3();
  1726. var scratchCartesian3Subtract = new Cartesian3();
  1727. function createAnimationCV(camera, duration) {
  1728. var position = camera.position;
  1729. var direction = camera.direction;
  1730. var normal = camera.worldToCameraCoordinatesVector(Cartesian3.UNIT_X, normalScratch);
  1731. var scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction);
  1732. var center = Cartesian3.add(position, Cartesian3.multiplyByScalar(direction, scalar, centerScratch), centerScratch);
  1733. camera.cameraToWorldCoordinatesPoint(center, center);
  1734. position = camera.cameraToWorldCoordinatesPoint(camera.position, posScratch);
  1735. var tanPhi = Math.tan(camera.frustum.fovy * 0.5);
  1736. var tanTheta = camera.frustum.aspectRatio * tanPhi;
  1737. var distToC = Cartesian3.magnitude(Cartesian3.subtract(position, center, scratchCartesian3Subtract));
  1738. var dWidth = tanTheta * distToC;
  1739. var dHeight = tanPhi * distToC;
  1740. var mapWidth = camera._maxCoord.x;
  1741. var mapHeight = camera._maxCoord.y;
  1742. var maxX = Math.max(dWidth - mapWidth, mapWidth);
  1743. var maxY = Math.max(dHeight - mapHeight, mapHeight);
  1744. if (position.z < -maxX || position.z > maxX || position.y < -maxY || position.y > maxY) {
  1745. var translateX = center.y < -maxX || center.y > maxX;
  1746. var translateY = center.z < -maxY || center.z > maxY;
  1747. if (translateX || translateY) {
  1748. return createAnimationTemplateCV(camera, position, center, maxX, maxY, duration);
  1749. }
  1750. }
  1751. return undefined;
  1752. }
  1753. /**
  1754. * Create an animation to move the map into view. This method is only valid for 2D and Columbus modes.
  1755. *
  1756. * @param {Number} duration The duration, in seconds, of the animation.
  1757. * @returns {Object} The animation or undefined if the scene mode is 3D or the map is already ion view.
  1758. *
  1759. * @exception {DeveloperException} duration is required.
  1760. *
  1761. * @private
  1762. */
  1763. Camera.prototype.createCorrectPositionTween = function(duration) {
  1764. //>>includeStart('debug', pragmas.debug);
  1765. if (!defined(duration)) {
  1766. throw new DeveloperError('duration is required.');
  1767. }
  1768. //>>includeEnd('debug');
  1769. if (this._mode === SceneMode.SCENE2D) {
  1770. return createAnimation2D(this, duration);
  1771. } else if (this._mode === SceneMode.COLUMBUS_VIEW) {
  1772. return createAnimationCV(this, duration);
  1773. }
  1774. return undefined;
  1775. };
  1776. /**
  1777. * Flies the camera from its current position to a new position.
  1778. *
  1779. * @param {Object} options Object with the following properties:
  1780. * @param {Cartesian3} options.destination The final position of the camera in WGS84 (world) coordinates.
  1781. * @param {Cartesian3} [options.direction] The final direction of the camera in WGS84 (world) coordinates. By default, the direction will point towards the center of the frame in 3D and in the negative z direction in Columbus view or 2D.
  1782. * @param {Cartesian3} [options.up] The final up direction in WGS84 (world) coordinates. By default, the up direction will point towards local north in 3D and in the positive y direction in Columbus view or 2D.
  1783. * @param {Number} [options.duration=3.0] The duration of the flight in seconds.
  1784. * @param {Camera~FlightCompleteCallback} [options.complete] The function to execute when the flight is complete.
  1785. * @param {Camera~FlightCancelledCallback} [options.cancel] The function to execute if the flight is cancelled.
  1786. * @param {Matrix4} [options.endTransform] Transform matrix representing the reference frame the camera will be in when the flight is completed.
  1787. * @param {Boolean} [options.convert=true] When <code>true</code>, the destination is converted to the correct coordinate system for each scene mode. When <code>false</code>, the destination is expected
  1788. * to be in the correct coordinate system.
  1789. *
  1790. * @exception {DeveloperError} If either direction or up is given, then both are required.
  1791. */
  1792. Camera.prototype.flyTo = function(options) {
  1793. var scene = this._scene;
  1794. scene.tweens.add(CameraFlightPath.createTween(scene, options));
  1795. };
  1796. /**
  1797. * Flies the camera from its current position to a position where the entire rectangle is visible.
  1798. *
  1799. * @param {Object} options Object with the following properties:
  1800. * @param {Rectangle} options.destination The rectangle to view, in WGS84 (world) coordinates, which determines the final position of the camera.
  1801. * @param {Number} [options.duration=3.0] The duration of the flight in seconds.
  1802. * @param {Camera~FlightCompleteCallback} [options.complete] The function to execute when the flight is complete.
  1803. * @param {Camera~FlightCancelledCallback} [options.cancel] The function to execute if the flight is cancelled.
  1804. * @param {Matrix4} [endTransform] Transform matrix representing the reference frame the camera will be in when the flight is completed.
  1805. */
  1806. Camera.prototype.flyToRectangle = function(options) {
  1807. var scene = this._scene;
  1808. scene.tweens.add(CameraFlightPath.createTweenRectangle(scene, options));
  1809. };
  1810. /**
  1811. * Returns a duplicate of a Camera instance.
  1812. *
  1813. * @returns {Camera} A new copy of the Camera instance.
  1814. */
  1815. Camera.prototype.clone = function() {
  1816. var camera = new Camera(this._scene);
  1817. camera.position = Cartesian3.clone(this.position);
  1818. camera.direction = Cartesian3.clone(this.direction);
  1819. camera.up = Cartesian3.clone(this.up);
  1820. camera.right = Cartesian3.clone(this.right);
  1821. camera.transform = Matrix4.clone(this.transform);
  1822. camera.frustum = this.frustum.clone();
  1823. return camera;
  1824. };
  1825. /**
  1826. * A function that will execute when a flight completes.
  1827. * @callback Camera~FlightCompleteCallback
  1828. */
  1829. /**
  1830. * A function that will execute when a flight is cancelled.
  1831. * @callback Camera~FlightCancelledCallback
  1832. */
  1833. return Camera;
  1834. });