Camera.js 117 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882
  1. import BoundingSphere from "../Core/BoundingSphere.js";
  2. import Cartesian2 from "../Core/Cartesian2.js";
  3. import Cartesian3 from "../Core/Cartesian3.js";
  4. import Cartesian4 from "../Core/Cartesian4.js";
  5. import Cartographic from "../Core/Cartographic.js";
  6. import defaultValue from "../Core/defaultValue.js";
  7. import defined from "../Core/defined.js";
  8. import DeveloperError from "../Core/DeveloperError.js";
  9. import EasingFunction from "../Core/EasingFunction.js";
  10. import Ellipsoid from "../Core/Ellipsoid.js";
  11. import EllipsoidGeodesic from "../Core/EllipsoidGeodesic.js";
  12. import Event from "../Core/Event.js";
  13. import getTimestamp from "../Core/getTimestamp.js";
  14. import HeadingPitchRange from "../Core/HeadingPitchRange.js";
  15. import HeadingPitchRoll from "../Core/HeadingPitchRoll.js";
  16. import Intersect from "../Core/Intersect.js";
  17. import IntersectionTests from "../Core/IntersectionTests.js";
  18. import CesiumMath from "../Core/Math.js";
  19. import Matrix3 from "../Core/Matrix3.js";
  20. import Matrix4 from "../Core/Matrix4.js";
  21. import OrthographicFrustum from "../Core/OrthographicFrustum.js";
  22. import OrthographicOffCenterFrustum from "../Core/OrthographicOffCenterFrustum.js";
  23. import PerspectiveFrustum from "../Core/PerspectiveFrustum.js";
  24. import Quaternion from "../Core/Quaternion.js";
  25. import Ray from "../Core/Ray.js";
  26. import Rectangle from "../Core/Rectangle.js";
  27. import Transforms from "../Core/Transforms.js";
  28. import CameraFlightPath from "./CameraFlightPath.js";
  29. import MapMode2D from "./MapMode2D.js";
  30. import SceneMode from "./SceneMode.js";
  31. /**
  32. * The camera is defined by a position, orientation, and view frustum.
  33. * <br /><br />
  34. * The orientation forms an orthonormal basis with a view, up and right = view x up unit vectors.
  35. * <br /><br />
  36. * The viewing frustum is defined by 6 planes.
  37. * Each plane is represented by a {@link Cartesian4} object, where the x, y, and z components
  38. * define the unit vector normal to the plane, and the w component is the distance of the
  39. * plane from the origin/camera position.
  40. *
  41. * @alias Camera
  42. *
  43. * @constructor
  44. *
  45. * @param {Scene} scene The scene.
  46. *
  47. * @demo {@link https://sandcastle.cesium.com/index.html?src=Camera.html|Cesium Sandcastle Camera Demo}
  48. * @demo {@link https://sandcastle.cesium.com/index.html?src=Camera%20Tutorial.html|Cesium Sandcastle Camera Tutorial Example}
  49. * @demo {@link https://cesium.com/docs/tutorials/camera/|Camera Tutorial}
  50. *
  51. * @example
  52. * // Create a camera looking down the negative z-axis, positioned at the origin,
  53. * // with a field of view of 60 degrees, and 1:1 aspect ratio.
  54. * var camera = new Cesium.Camera(scene);
  55. * camera.position = new Cesium.Cartesian3();
  56. * camera.direction = Cesium.Cartesian3.negate(Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3());
  57. * camera.up = Cesium.Cartesian3.clone(Cesium.Cartesian3.UNIT_Y);
  58. * camera.frustum.fov = Cesium.Math.PI_OVER_THREE;
  59. * camera.frustum.near = 1.0;
  60. * camera.frustum.far = 2.0;
  61. */
  62. function Camera(scene) {
  63. //>>includeStart('debug', pragmas.debug);
  64. if (!defined(scene)) {
  65. throw new DeveloperError("scene is required.");
  66. }
  67. //>>includeEnd('debug');
  68. this._scene = scene;
  69. this._transform = Matrix4.clone(Matrix4.IDENTITY);
  70. this._invTransform = Matrix4.clone(Matrix4.IDENTITY);
  71. this._actualTransform = Matrix4.clone(Matrix4.IDENTITY);
  72. this._actualInvTransform = Matrix4.clone(Matrix4.IDENTITY);
  73. this._transformChanged = false;
  74. /**
  75. * The position of the camera.
  76. *
  77. * @type {Cartesian3}
  78. */
  79. this.position = new Cartesian3();
  80. this._position = new Cartesian3();
  81. this._positionWC = new Cartesian3();
  82. this._positionCartographic = new Cartographic();
  83. this._oldPositionWC = undefined;
  84. /**
  85. * The position delta magnitude.
  86. *
  87. * @private
  88. */
  89. this.positionWCDeltaMagnitude = 0.0;
  90. /**
  91. * The position delta magnitude last frame.
  92. *
  93. * @private
  94. */
  95. this.positionWCDeltaMagnitudeLastFrame = 0.0;
  96. /**
  97. * How long in seconds since the camera has stopped moving
  98. *
  99. * @private
  100. */
  101. this.timeSinceMoved = 0.0;
  102. this._lastMovedTimestamp = 0.0;
  103. /**
  104. * The view direction of the camera.
  105. *
  106. * @type {Cartesian3}
  107. */
  108. this.direction = new Cartesian3();
  109. this._direction = new Cartesian3();
  110. this._directionWC = new Cartesian3();
  111. /**
  112. * The up direction of the camera.
  113. *
  114. * @type {Cartesian3}
  115. */
  116. this.up = new Cartesian3();
  117. this._up = new Cartesian3();
  118. this._upWC = new Cartesian3();
  119. /**
  120. * The right direction of the camera.
  121. *
  122. * @type {Cartesian3}
  123. */
  124. this.right = new Cartesian3();
  125. this._right = new Cartesian3();
  126. this._rightWC = new Cartesian3();
  127. /**
  128. * The region of space in view.
  129. *
  130. * @type {PerspectiveFrustum|PerspectiveOffCenterFrustum|OrthographicFrustum}
  131. * @default PerspectiveFrustum()
  132. *
  133. * @see PerspectiveFrustum
  134. * @see PerspectiveOffCenterFrustum
  135. * @see OrthographicFrustum
  136. */
  137. this.frustum = new PerspectiveFrustum();
  138. this.frustum.aspectRatio =
  139. scene.drawingBufferWidth / scene.drawingBufferHeight;
  140. this.frustum.fov = CesiumMath.toRadians(60.0);
  141. /**
  142. * The default amount to move the camera when an argument is not
  143. * provided to the move methods.
  144. * @type {Number}
  145. * @default 100000.0;
  146. */
  147. this.defaultMoveAmount = 100000.0;
  148. /**
  149. * The default amount to rotate the camera when an argument is not
  150. * provided to the look methods.
  151. * @type {Number}
  152. * @default Math.PI / 60.0
  153. */
  154. this.defaultLookAmount = Math.PI / 60.0;
  155. /**
  156. * The default amount to rotate the camera when an argument is not
  157. * provided to the rotate methods.
  158. * @type {Number}
  159. * @default Math.PI / 3600.0
  160. */
  161. this.defaultRotateAmount = Math.PI / 3600.0;
  162. /**
  163. * The default amount to move the camera when an argument is not
  164. * provided to the zoom methods.
  165. * @type {Number}
  166. * @default 100000.0;
  167. */
  168. this.defaultZoomAmount = 100000.0;
  169. /**
  170. * If set, the camera will not be able to rotate past this axis in either direction.
  171. * @type {Cartesian3}
  172. * @default undefined
  173. */
  174. this.constrainedAxis = undefined;
  175. /**
  176. * The factor multiplied by the the map size used to determine where to clamp the camera position
  177. * when zooming out from the surface. The default is 1.5. Only valid for 2D and the map is rotatable.
  178. * @type {Number}
  179. * @default 1.5
  180. */
  181. this.maximumZoomFactor = 1.5;
  182. this._moveStart = new Event();
  183. this._moveEnd = new Event();
  184. this._changed = new Event();
  185. this._changedPosition = undefined;
  186. this._changedDirection = undefined;
  187. this._changedFrustum = undefined;
  188. /**
  189. * The amount the camera has to change before the <code>changed</code> event is raised. The value is a percentage in the [0, 1] range.
  190. * @type {number}
  191. * @default 0.5
  192. */
  193. this.percentageChanged = 0.5;
  194. this._viewMatrix = new Matrix4();
  195. this._invViewMatrix = new Matrix4();
  196. updateViewMatrix(this);
  197. this._mode = SceneMode.SCENE3D;
  198. this._modeChanged = true;
  199. var projection = scene.mapProjection;
  200. this._projection = projection;
  201. this._maxCoord = projection.project(
  202. new Cartographic(Math.PI, CesiumMath.PI_OVER_TWO)
  203. );
  204. this._max2Dfrustum = undefined;
  205. // set default view
  206. rectangleCameraPosition3D(
  207. this,
  208. Camera.DEFAULT_VIEW_RECTANGLE,
  209. this.position,
  210. true
  211. );
  212. var mag = Cartesian3.magnitude(this.position);
  213. mag += mag * Camera.DEFAULT_VIEW_FACTOR;
  214. Cartesian3.normalize(this.position, this.position);
  215. Cartesian3.multiplyByScalar(this.position, mag, this.position);
  216. }
  217. /**
  218. * @private
  219. */
  220. Camera.TRANSFORM_2D = new Matrix4(
  221. 0.0,
  222. 0.0,
  223. 1.0,
  224. 0.0,
  225. 1.0,
  226. 0.0,
  227. 0.0,
  228. 0.0,
  229. 0.0,
  230. 1.0,
  231. 0.0,
  232. 0.0,
  233. 0.0,
  234. 0.0,
  235. 0.0,
  236. 1.0
  237. );
  238. /**
  239. * @private
  240. */
  241. Camera.TRANSFORM_2D_INVERSE = Matrix4.inverseTransformation(
  242. Camera.TRANSFORM_2D,
  243. new Matrix4()
  244. );
  245. /**
  246. * The default rectangle the camera will view on creation.
  247. * @type Rectangle
  248. */
  249. Camera.DEFAULT_VIEW_RECTANGLE = Rectangle.fromDegrees(
  250. -95.0,
  251. -20.0,
  252. -70.0,
  253. 90.0
  254. );
  255. /**
  256. * A scalar to multiply to the camera position and add it back after setting the camera to view the rectangle.
  257. * A value of zero means the camera will view the entire {@link Camera#DEFAULT_VIEW_RECTANGLE}, a value greater than zero
  258. * will move it further away from the extent, and a value less than zero will move it close to the extent.
  259. * @type Number
  260. */
  261. Camera.DEFAULT_VIEW_FACTOR = 0.5;
  262. /**
  263. * The default heading/pitch/range that is used when the camera flies to a location that contains a bounding sphere.
  264. * @type HeadingPitchRange
  265. */
  266. Camera.DEFAULT_OFFSET = new HeadingPitchRange(
  267. 0.0,
  268. -CesiumMath.PI_OVER_FOUR,
  269. 0.0
  270. );
  271. function updateViewMatrix(camera) {
  272. Matrix4.computeView(
  273. camera._position,
  274. camera._direction,
  275. camera._up,
  276. camera._right,
  277. camera._viewMatrix
  278. );
  279. Matrix4.multiply(
  280. camera._viewMatrix,
  281. camera._actualInvTransform,
  282. camera._viewMatrix
  283. );
  284. Matrix4.inverseTransformation(camera._viewMatrix, camera._invViewMatrix);
  285. }
  286. function updateCameraDeltas(camera) {
  287. if (!defined(camera._oldPositionWC)) {
  288. camera._oldPositionWC = Cartesian3.clone(
  289. camera.positionWC,
  290. camera._oldPositionWC
  291. );
  292. } else {
  293. camera.positionWCDeltaMagnitudeLastFrame = camera.positionWCDeltaMagnitude;
  294. var delta = Cartesian3.subtract(
  295. camera.positionWC,
  296. camera._oldPositionWC,
  297. camera._oldPositionWC
  298. );
  299. camera.positionWCDeltaMagnitude = Cartesian3.magnitude(delta);
  300. camera._oldPositionWC = Cartesian3.clone(
  301. camera.positionWC,
  302. camera._oldPositionWC
  303. );
  304. // Update move timers
  305. if (camera.positionWCDeltaMagnitude > 0.0) {
  306. camera.timeSinceMoved = 0.0;
  307. camera._lastMovedTimestamp = getTimestamp();
  308. } else {
  309. camera.timeSinceMoved =
  310. Math.max(getTimestamp() - camera._lastMovedTimestamp, 0.0) / 1000.0;
  311. }
  312. }
  313. }
  314. /**
  315. * Checks if there's a camera flight with preload for this camera.
  316. *
  317. * @returns {Boolean} Whether or not this camera has a current flight with a valid preloadFlightCamera in scene.
  318. *
  319. * @private
  320. *
  321. */
  322. Camera.prototype.canPreloadFlight = function () {
  323. return defined(this._currentFlight) && this._mode !== SceneMode.SCENE2D;
  324. };
  325. Camera.prototype._updateCameraChanged = function () {
  326. var camera = this;
  327. updateCameraDeltas(camera);
  328. if (camera._changed.numberOfListeners === 0) {
  329. return;
  330. }
  331. var percentageChanged = camera.percentageChanged;
  332. if (camera._mode === SceneMode.SCENE2D) {
  333. if (!defined(camera._changedFrustum)) {
  334. camera._changedPosition = Cartesian3.clone(
  335. camera.position,
  336. camera._changedPosition
  337. );
  338. camera._changedFrustum = camera.frustum.clone();
  339. return;
  340. }
  341. var position = camera.position;
  342. var lastPosition = camera._changedPosition;
  343. var frustum = camera.frustum;
  344. var lastFrustum = camera._changedFrustum;
  345. var x0 = position.x + frustum.left;
  346. var x1 = position.x + frustum.right;
  347. var x2 = lastPosition.x + lastFrustum.left;
  348. var x3 = lastPosition.x + lastFrustum.right;
  349. var y0 = position.y + frustum.bottom;
  350. var y1 = position.y + frustum.top;
  351. var y2 = lastPosition.y + lastFrustum.bottom;
  352. var y3 = lastPosition.y + lastFrustum.top;
  353. var leftX = Math.max(x0, x2);
  354. var rightX = Math.min(x1, x3);
  355. var bottomY = Math.max(y0, y2);
  356. var topY = Math.min(y1, y3);
  357. var areaPercentage;
  358. if (leftX >= rightX || bottomY >= y1) {
  359. areaPercentage = 1.0;
  360. } else {
  361. var areaRef = lastFrustum;
  362. if (x0 < x2 && x1 > x3 && y0 < y2 && y1 > y3) {
  363. areaRef = frustum;
  364. }
  365. areaPercentage =
  366. 1.0 -
  367. ((rightX - leftX) * (topY - bottomY)) /
  368. ((areaRef.right - areaRef.left) * (areaRef.top - areaRef.bottom));
  369. }
  370. if (areaPercentage > percentageChanged) {
  371. camera._changed.raiseEvent(areaPercentage);
  372. camera._changedPosition = Cartesian3.clone(
  373. camera.position,
  374. camera._changedPosition
  375. );
  376. camera._changedFrustum = camera.frustum.clone(camera._changedFrustum);
  377. }
  378. return;
  379. }
  380. if (!defined(camera._changedDirection)) {
  381. camera._changedPosition = Cartesian3.clone(
  382. camera.positionWC,
  383. camera._changedPosition
  384. );
  385. camera._changedDirection = Cartesian3.clone(
  386. camera.directionWC,
  387. camera._changedDirection
  388. );
  389. return;
  390. }
  391. var dirAngle = CesiumMath.acosClamped(
  392. Cartesian3.dot(camera.directionWC, camera._changedDirection)
  393. );
  394. var dirPercentage;
  395. if (defined(camera.frustum.fovy)) {
  396. dirPercentage = dirAngle / (camera.frustum.fovy * 0.5);
  397. } else {
  398. dirPercentage = dirAngle;
  399. }
  400. var distance = Cartesian3.distance(
  401. camera.positionWC,
  402. camera._changedPosition
  403. );
  404. var heightPercentage = distance / camera.positionCartographic.height;
  405. if (
  406. dirPercentage > percentageChanged ||
  407. heightPercentage > percentageChanged
  408. ) {
  409. camera._changed.raiseEvent(Math.max(dirPercentage, heightPercentage));
  410. camera._changedPosition = Cartesian3.clone(
  411. camera.positionWC,
  412. camera._changedPosition
  413. );
  414. camera._changedDirection = Cartesian3.clone(
  415. camera.directionWC,
  416. camera._changedDirection
  417. );
  418. }
  419. };
  420. function convertTransformForColumbusView(camera) {
  421. Transforms.basisTo2D(
  422. camera._projection,
  423. camera._transform,
  424. camera._actualTransform
  425. );
  426. }
  427. var scratchCartographic = new Cartographic();
  428. var scratchCartesian3Projection = new Cartesian3();
  429. var scratchCartesian3 = new Cartesian3();
  430. var scratchCartesian4Origin = new Cartesian4();
  431. var scratchCartesian4NewOrigin = new Cartesian4();
  432. var scratchCartesian4NewXAxis = new Cartesian4();
  433. var scratchCartesian4NewYAxis = new Cartesian4();
  434. var scratchCartesian4NewZAxis = new Cartesian4();
  435. function convertTransformFor2D(camera) {
  436. var projection = camera._projection;
  437. var ellipsoid = projection.ellipsoid;
  438. var origin = Matrix4.getColumn(camera._transform, 3, scratchCartesian4Origin);
  439. var cartographic = ellipsoid.cartesianToCartographic(
  440. origin,
  441. scratchCartographic
  442. );
  443. var projectedPosition = projection.project(
  444. cartographic,
  445. scratchCartesian3Projection
  446. );
  447. var newOrigin = scratchCartesian4NewOrigin;
  448. newOrigin.x = projectedPosition.z;
  449. newOrigin.y = projectedPosition.x;
  450. newOrigin.z = projectedPosition.y;
  451. newOrigin.w = 1.0;
  452. var newZAxis = Cartesian4.clone(Cartesian4.UNIT_X, scratchCartesian4NewZAxis);
  453. var xAxis = Cartesian4.add(
  454. Matrix4.getColumn(camera._transform, 0, scratchCartesian3),
  455. origin,
  456. scratchCartesian3
  457. );
  458. ellipsoid.cartesianToCartographic(xAxis, cartographic);
  459. projection.project(cartographic, projectedPosition);
  460. var newXAxis = scratchCartesian4NewXAxis;
  461. newXAxis.x = projectedPosition.z;
  462. newXAxis.y = projectedPosition.x;
  463. newXAxis.z = projectedPosition.y;
  464. newXAxis.w = 0.0;
  465. Cartesian3.subtract(newXAxis, newOrigin, newXAxis);
  466. newXAxis.x = 0.0;
  467. var newYAxis = scratchCartesian4NewYAxis;
  468. if (Cartesian3.magnitudeSquared(newXAxis) > CesiumMath.EPSILON10) {
  469. Cartesian3.cross(newZAxis, newXAxis, newYAxis);
  470. } else {
  471. var yAxis = Cartesian4.add(
  472. Matrix4.getColumn(camera._transform, 1, scratchCartesian3),
  473. origin,
  474. scratchCartesian3
  475. );
  476. ellipsoid.cartesianToCartographic(yAxis, cartographic);
  477. projection.project(cartographic, projectedPosition);
  478. newYAxis.x = projectedPosition.z;
  479. newYAxis.y = projectedPosition.x;
  480. newYAxis.z = projectedPosition.y;
  481. newYAxis.w = 0.0;
  482. Cartesian3.subtract(newYAxis, newOrigin, newYAxis);
  483. newYAxis.x = 0.0;
  484. if (Cartesian3.magnitudeSquared(newYAxis) < CesiumMath.EPSILON10) {
  485. Cartesian4.clone(Cartesian4.UNIT_Y, newXAxis);
  486. Cartesian4.clone(Cartesian4.UNIT_Z, newYAxis);
  487. }
  488. }
  489. Cartesian3.cross(newYAxis, newZAxis, newXAxis);
  490. Cartesian3.normalize(newXAxis, newXAxis);
  491. Cartesian3.cross(newZAxis, newXAxis, newYAxis);
  492. Cartesian3.normalize(newYAxis, newYAxis);
  493. Matrix4.setColumn(
  494. camera._actualTransform,
  495. 0,
  496. newXAxis,
  497. camera._actualTransform
  498. );
  499. Matrix4.setColumn(
  500. camera._actualTransform,
  501. 1,
  502. newYAxis,
  503. camera._actualTransform
  504. );
  505. Matrix4.setColumn(
  506. camera._actualTransform,
  507. 2,
  508. newZAxis,
  509. camera._actualTransform
  510. );
  511. Matrix4.setColumn(
  512. camera._actualTransform,
  513. 3,
  514. newOrigin,
  515. camera._actualTransform
  516. );
  517. }
  518. var scratchCartesian = new Cartesian3();
  519. function updateMembers(camera) {
  520. var mode = camera._mode;
  521. var heightChanged = false;
  522. var height = 0.0;
  523. if (mode === SceneMode.SCENE2D) {
  524. height = camera.frustum.right - camera.frustum.left;
  525. heightChanged = height !== camera._positionCartographic.height;
  526. }
  527. var position = camera._position;
  528. var positionChanged =
  529. !Cartesian3.equals(position, camera.position) || heightChanged;
  530. if (positionChanged) {
  531. position = Cartesian3.clone(camera.position, camera._position);
  532. }
  533. var direction = camera._direction;
  534. var directionChanged = !Cartesian3.equals(direction, camera.direction);
  535. if (directionChanged) {
  536. Cartesian3.normalize(camera.direction, camera.direction);
  537. direction = Cartesian3.clone(camera.direction, camera._direction);
  538. }
  539. var up = camera._up;
  540. var upChanged = !Cartesian3.equals(up, camera.up);
  541. if (upChanged) {
  542. Cartesian3.normalize(camera.up, camera.up);
  543. up = Cartesian3.clone(camera.up, camera._up);
  544. }
  545. var right = camera._right;
  546. var rightChanged = !Cartesian3.equals(right, camera.right);
  547. if (rightChanged) {
  548. Cartesian3.normalize(camera.right, camera.right);
  549. right = Cartesian3.clone(camera.right, camera._right);
  550. }
  551. var transformChanged = camera._transformChanged || camera._modeChanged;
  552. camera._transformChanged = false;
  553. if (transformChanged) {
  554. Matrix4.inverseTransformation(camera._transform, camera._invTransform);
  555. if (
  556. camera._mode === SceneMode.COLUMBUS_VIEW ||
  557. camera._mode === SceneMode.SCENE2D
  558. ) {
  559. if (Matrix4.equals(Matrix4.IDENTITY, camera._transform)) {
  560. Matrix4.clone(Camera.TRANSFORM_2D, camera._actualTransform);
  561. } else if (camera._mode === SceneMode.COLUMBUS_VIEW) {
  562. convertTransformForColumbusView(camera);
  563. } else {
  564. convertTransformFor2D(camera);
  565. }
  566. } else {
  567. Matrix4.clone(camera._transform, camera._actualTransform);
  568. }
  569. Matrix4.inverseTransformation(
  570. camera._actualTransform,
  571. camera._actualInvTransform
  572. );
  573. camera._modeChanged = false;
  574. }
  575. var transform = camera._actualTransform;
  576. if (positionChanged || transformChanged) {
  577. camera._positionWC = Matrix4.multiplyByPoint(
  578. transform,
  579. position,
  580. camera._positionWC
  581. );
  582. // Compute the Cartographic position of the camera.
  583. if (mode === SceneMode.SCENE3D || mode === SceneMode.MORPHING) {
  584. camera._positionCartographic = camera._projection.ellipsoid.cartesianToCartographic(
  585. camera._positionWC,
  586. camera._positionCartographic
  587. );
  588. } else {
  589. // The camera position is expressed in the 2D coordinate system where the Y axis is to the East,
  590. // the Z axis is to the North, and the X axis is out of the map. Express them instead in the ENU axes where
  591. // X is to the East, Y is to the North, and Z is out of the local horizontal plane.
  592. var positionENU = scratchCartesian;
  593. positionENU.x = camera._positionWC.y;
  594. positionENU.y = camera._positionWC.z;
  595. positionENU.z = camera._positionWC.x;
  596. // In 2D, the camera height is always 12.7 million meters.
  597. // The apparent height is equal to half the frustum width.
  598. if (mode === SceneMode.SCENE2D) {
  599. positionENU.z = height;
  600. }
  601. camera._projection.unproject(positionENU, camera._positionCartographic);
  602. }
  603. }
  604. if (directionChanged || upChanged || rightChanged) {
  605. var det = Cartesian3.dot(
  606. direction,
  607. Cartesian3.cross(up, right, scratchCartesian)
  608. );
  609. if (Math.abs(1.0 - det) > CesiumMath.EPSILON2) {
  610. //orthonormalize axes
  611. var invUpMag = 1.0 / Cartesian3.magnitudeSquared(up);
  612. var scalar = Cartesian3.dot(up, direction) * invUpMag;
  613. var w0 = Cartesian3.multiplyByScalar(direction, scalar, scratchCartesian);
  614. up = Cartesian3.normalize(
  615. Cartesian3.subtract(up, w0, camera._up),
  616. camera._up
  617. );
  618. Cartesian3.clone(up, camera.up);
  619. right = Cartesian3.cross(direction, up, camera._right);
  620. Cartesian3.clone(right, camera.right);
  621. }
  622. }
  623. if (directionChanged || transformChanged) {
  624. camera._directionWC = Matrix4.multiplyByPointAsVector(
  625. transform,
  626. direction,
  627. camera._directionWC
  628. );
  629. Cartesian3.normalize(camera._directionWC, camera._directionWC);
  630. }
  631. if (upChanged || transformChanged) {
  632. camera._upWC = Matrix4.multiplyByPointAsVector(transform, up, camera._upWC);
  633. Cartesian3.normalize(camera._upWC, camera._upWC);
  634. }
  635. if (rightChanged || transformChanged) {
  636. camera._rightWC = Matrix4.multiplyByPointAsVector(
  637. transform,
  638. right,
  639. camera._rightWC
  640. );
  641. Cartesian3.normalize(camera._rightWC, camera._rightWC);
  642. }
  643. if (
  644. positionChanged ||
  645. directionChanged ||
  646. upChanged ||
  647. rightChanged ||
  648. transformChanged
  649. ) {
  650. updateViewMatrix(camera);
  651. }
  652. }
  653. function getHeading(direction, up) {
  654. var heading;
  655. if (
  656. !CesiumMath.equalsEpsilon(Math.abs(direction.z), 1.0, CesiumMath.EPSILON3)
  657. ) {
  658. heading = Math.atan2(direction.y, direction.x) - CesiumMath.PI_OVER_TWO;
  659. } else {
  660. heading = Math.atan2(up.y, up.x) - CesiumMath.PI_OVER_TWO;
  661. }
  662. return CesiumMath.TWO_PI - CesiumMath.zeroToTwoPi(heading);
  663. }
  664. function getPitch(direction) {
  665. return CesiumMath.PI_OVER_TWO - CesiumMath.acosClamped(direction.z);
  666. }
  667. function getRoll(direction, up, right) {
  668. var roll = 0.0;
  669. if (
  670. !CesiumMath.equalsEpsilon(Math.abs(direction.z), 1.0, CesiumMath.EPSILON3)
  671. ) {
  672. roll = Math.atan2(-right.z, up.z);
  673. roll = CesiumMath.zeroToTwoPi(roll + CesiumMath.TWO_PI);
  674. }
  675. return roll;
  676. }
  677. var scratchHPRMatrix1 = new Matrix4();
  678. var scratchHPRMatrix2 = new Matrix4();
  679. Object.defineProperties(Camera.prototype, {
  680. /**
  681. * Gets the camera's reference frame. The inverse of this transformation is appended to the view matrix.
  682. * @memberof Camera.prototype
  683. *
  684. * @type {Matrix4}
  685. * @readonly
  686. *
  687. * @default {@link Matrix4.IDENTITY}
  688. */
  689. transform: {
  690. get: function () {
  691. return this._transform;
  692. },
  693. },
  694. /**
  695. * Gets the inverse camera transform.
  696. * @memberof Camera.prototype
  697. *
  698. * @type {Matrix4}
  699. * @readonly
  700. *
  701. * @default {@link Matrix4.IDENTITY}
  702. */
  703. inverseTransform: {
  704. get: function () {
  705. updateMembers(this);
  706. return this._invTransform;
  707. },
  708. },
  709. /**
  710. * Gets the view matrix.
  711. * @memberof Camera.prototype
  712. *
  713. * @type {Matrix4}
  714. * @readonly
  715. *
  716. * @see Camera#inverseViewMatrix
  717. */
  718. viewMatrix: {
  719. get: function () {
  720. updateMembers(this);
  721. return this._viewMatrix;
  722. },
  723. },
  724. /**
  725. * Gets the inverse view matrix.
  726. * @memberof Camera.prototype
  727. *
  728. * @type {Matrix4}
  729. * @readonly
  730. *
  731. * @see Camera#viewMatrix
  732. */
  733. inverseViewMatrix: {
  734. get: function () {
  735. updateMembers(this);
  736. return this._invViewMatrix;
  737. },
  738. },
  739. /**
  740. * Gets the {@link Cartographic} position of the camera, with longitude and latitude
  741. * expressed in radians and height in meters. In 2D and Columbus View, it is possible
  742. * for the returned longitude and latitude to be outside the range of valid longitudes
  743. * and latitudes when the camera is outside the map.
  744. * @memberof Camera.prototype
  745. *
  746. * @type {Cartographic}
  747. * @readonly
  748. */
  749. positionCartographic: {
  750. get: function () {
  751. updateMembers(this);
  752. return this._positionCartographic;
  753. },
  754. },
  755. /**
  756. * Gets the position of the camera in world coordinates.
  757. * @memberof Camera.prototype
  758. *
  759. * @type {Cartesian3}
  760. * @readonly
  761. */
  762. positionWC: {
  763. get: function () {
  764. updateMembers(this);
  765. return this._positionWC;
  766. },
  767. },
  768. /**
  769. * Gets the view direction of the camera in world coordinates.
  770. * @memberof Camera.prototype
  771. *
  772. * @type {Cartesian3}
  773. * @readonly
  774. */
  775. directionWC: {
  776. get: function () {
  777. updateMembers(this);
  778. return this._directionWC;
  779. },
  780. },
  781. /**
  782. * Gets the up direction of the camera in world coordinates.
  783. * @memberof Camera.prototype
  784. *
  785. * @type {Cartesian3}
  786. * @readonly
  787. */
  788. upWC: {
  789. get: function () {
  790. updateMembers(this);
  791. return this._upWC;
  792. },
  793. },
  794. /**
  795. * Gets the right direction of the camera in world coordinates.
  796. * @memberof Camera.prototype
  797. *
  798. * @type {Cartesian3}
  799. * @readonly
  800. */
  801. rightWC: {
  802. get: function () {
  803. updateMembers(this);
  804. return this._rightWC;
  805. },
  806. },
  807. /**
  808. * Gets the camera heading in radians.
  809. * @memberof Camera.prototype
  810. *
  811. * @type {Number}
  812. * @readonly
  813. */
  814. heading: {
  815. get: function () {
  816. if (this._mode !== SceneMode.MORPHING) {
  817. var ellipsoid = this._projection.ellipsoid;
  818. var oldTransform = Matrix4.clone(this._transform, scratchHPRMatrix1);
  819. var transform = Transforms.eastNorthUpToFixedFrame(
  820. this.positionWC,
  821. ellipsoid,
  822. scratchHPRMatrix2
  823. );
  824. this._setTransform(transform);
  825. var heading = getHeading(this.direction, this.up);
  826. this._setTransform(oldTransform);
  827. return heading;
  828. }
  829. return undefined;
  830. },
  831. },
  832. /**
  833. * Gets the camera pitch in radians.
  834. * @memberof Camera.prototype
  835. *
  836. * @type {Number}
  837. * @readonly
  838. */
  839. pitch: {
  840. get: function () {
  841. if (this._mode !== SceneMode.MORPHING) {
  842. var ellipsoid = this._projection.ellipsoid;
  843. var oldTransform = Matrix4.clone(this._transform, scratchHPRMatrix1);
  844. var transform = Transforms.eastNorthUpToFixedFrame(
  845. this.positionWC,
  846. ellipsoid,
  847. scratchHPRMatrix2
  848. );
  849. this._setTransform(transform);
  850. var pitch = getPitch(this.direction);
  851. this._setTransform(oldTransform);
  852. return pitch;
  853. }
  854. return undefined;
  855. },
  856. },
  857. /**
  858. * Gets the camera roll in radians.
  859. * @memberof Camera.prototype
  860. *
  861. * @type {Number}
  862. * @readonly
  863. */
  864. roll: {
  865. get: function () {
  866. if (this._mode !== SceneMode.MORPHING) {
  867. var ellipsoid = this._projection.ellipsoid;
  868. var oldTransform = Matrix4.clone(this._transform, scratchHPRMatrix1);
  869. var transform = Transforms.eastNorthUpToFixedFrame(
  870. this.positionWC,
  871. ellipsoid,
  872. scratchHPRMatrix2
  873. );
  874. this._setTransform(transform);
  875. var roll = getRoll(this.direction, this.up, this.right);
  876. this._setTransform(oldTransform);
  877. return roll;
  878. }
  879. return undefined;
  880. },
  881. },
  882. /**
  883. * Gets the event that will be raised at when the camera starts to move.
  884. * @memberof Camera.prototype
  885. * @type {Event}
  886. * @readonly
  887. */
  888. moveStart: {
  889. get: function () {
  890. return this._moveStart;
  891. },
  892. },
  893. /**
  894. * Gets the event that will be raised when the camera has stopped moving.
  895. * @memberof Camera.prototype
  896. * @type {Event}
  897. * @readonly
  898. */
  899. moveEnd: {
  900. get: function () {
  901. return this._moveEnd;
  902. },
  903. },
  904. /**
  905. * Gets the event that will be raised when the camera has changed by <code>percentageChanged</code>.
  906. * @memberof Camera.prototype
  907. * @type {Event}
  908. * @readonly
  909. */
  910. changed: {
  911. get: function () {
  912. return this._changed;
  913. },
  914. },
  915. });
  916. /**
  917. * @private
  918. */
  919. Camera.prototype.update = function (mode) {
  920. //>>includeStart('debug', pragmas.debug);
  921. if (!defined(mode)) {
  922. throw new DeveloperError("mode is required.");
  923. }
  924. if (
  925. mode === SceneMode.SCENE2D &&
  926. !(this.frustum instanceof OrthographicOffCenterFrustum)
  927. ) {
  928. throw new DeveloperError(
  929. "An OrthographicOffCenterFrustum is required in 2D."
  930. );
  931. }
  932. if (
  933. (mode === SceneMode.SCENE3D || mode === SceneMode.COLUMBUS_VIEW) &&
  934. !(this.frustum instanceof PerspectiveFrustum) &&
  935. !(this.frustum instanceof OrthographicFrustum)
  936. ) {
  937. throw new DeveloperError(
  938. "A PerspectiveFrustum or OrthographicFrustum is required in 3D and Columbus view"
  939. );
  940. }
  941. //>>includeEnd('debug');
  942. var updateFrustum = false;
  943. if (mode !== this._mode) {
  944. this._mode = mode;
  945. this._modeChanged = mode !== SceneMode.MORPHING;
  946. updateFrustum = this._mode === SceneMode.SCENE2D;
  947. }
  948. if (updateFrustum) {
  949. var frustum = (this._max2Dfrustum = this.frustum.clone());
  950. //>>includeStart('debug', pragmas.debug);
  951. if (!(frustum instanceof OrthographicOffCenterFrustum)) {
  952. throw new DeveloperError(
  953. "The camera frustum is expected to be orthographic for 2D camera control."
  954. );
  955. }
  956. //>>includeEnd('debug');
  957. var maxZoomOut = 2.0;
  958. var ratio = frustum.top / frustum.right;
  959. frustum.right = this._maxCoord.x * maxZoomOut;
  960. frustum.left = -frustum.right;
  961. frustum.top = ratio * frustum.right;
  962. frustum.bottom = -frustum.top;
  963. }
  964. if (this._mode === SceneMode.SCENE2D) {
  965. clampMove2D(this, this.position);
  966. }
  967. };
  968. var setTransformPosition = new Cartesian3();
  969. var setTransformUp = new Cartesian3();
  970. var setTransformDirection = new Cartesian3();
  971. Camera.prototype._setTransform = function (transform) {
  972. var position = Cartesian3.clone(this.positionWC, setTransformPosition);
  973. var up = Cartesian3.clone(this.upWC, setTransformUp);
  974. var direction = Cartesian3.clone(this.directionWC, setTransformDirection);
  975. Matrix4.clone(transform, this._transform);
  976. this._transformChanged = true;
  977. updateMembers(this);
  978. var inverse = this._actualInvTransform;
  979. Matrix4.multiplyByPoint(inverse, position, this.position);
  980. Matrix4.multiplyByPointAsVector(inverse, direction, this.direction);
  981. Matrix4.multiplyByPointAsVector(inverse, up, this.up);
  982. Cartesian3.cross(this.direction, this.up, this.right);
  983. updateMembers(this);
  984. };
  985. var scratchAdjustOrthographicFrustumMousePosition = new Cartesian2();
  986. var scratchPickRay = new Ray();
  987. var scratchRayIntersection = new Cartesian3();
  988. var scratchDepthIntersection = new Cartesian3();
  989. function calculateOrthographicFrustumWidth(camera) {
  990. // Camera is fixed to an object, so keep frustum width constant.
  991. if (!Matrix4.equals(Matrix4.IDENTITY, camera.transform)) {
  992. return Cartesian3.magnitude(camera.position);
  993. }
  994. var scene = camera._scene;
  995. var globe = scene.globe;
  996. var mousePosition = scratchAdjustOrthographicFrustumMousePosition;
  997. mousePosition.x = scene.drawingBufferWidth / 2.0;
  998. mousePosition.y = scene.drawingBufferHeight / 2.0;
  999. var rayIntersection;
  1000. if (defined(globe)) {
  1001. var ray = camera.getPickRay(mousePosition, scratchPickRay);
  1002. rayIntersection = globe.pickWorldCoordinates(
  1003. ray,
  1004. scene,
  1005. true,
  1006. scratchRayIntersection
  1007. );
  1008. }
  1009. var depthIntersection;
  1010. if (scene.pickPositionSupported) {
  1011. depthIntersection = scene.pickPositionWorldCoordinates(
  1012. mousePosition,
  1013. scratchDepthIntersection
  1014. );
  1015. }
  1016. var distance;
  1017. if (defined(rayIntersection) || defined(depthIntersection)) {
  1018. var depthDistance = defined(depthIntersection)
  1019. ? Cartesian3.distance(depthIntersection, camera.positionWC)
  1020. : Number.POSITIVE_INFINITY;
  1021. var rayDistance = defined(rayIntersection)
  1022. ? Cartesian3.distance(rayIntersection, camera.positionWC)
  1023. : Number.POSITIVE_INFINITY;
  1024. distance = Math.min(depthDistance, rayDistance);
  1025. } else {
  1026. distance = Math.max(camera.positionCartographic.height, 0.0);
  1027. }
  1028. return distance;
  1029. }
  1030. Camera.prototype._adjustOrthographicFrustum = function (zooming) {
  1031. if (!(this.frustum instanceof OrthographicFrustum)) {
  1032. return;
  1033. }
  1034. if (!zooming && this._positionCartographic.height < 150000.0) {
  1035. return;
  1036. }
  1037. this.frustum.width = calculateOrthographicFrustumWidth(this);
  1038. };
  1039. var scratchSetViewCartesian = new Cartesian3();
  1040. var scratchSetViewTransform1 = new Matrix4();
  1041. var scratchSetViewTransform2 = new Matrix4();
  1042. var scratchSetViewQuaternion = new Quaternion();
  1043. var scratchSetViewMatrix3 = new Matrix3();
  1044. var scratchSetViewCartographic = new Cartographic();
  1045. function setView3D(camera, position, hpr) {
  1046. var currentTransform = Matrix4.clone(
  1047. camera.transform,
  1048. scratchSetViewTransform1
  1049. );
  1050. var localTransform = Transforms.eastNorthUpToFixedFrame(
  1051. position,
  1052. camera._projection.ellipsoid,
  1053. scratchSetViewTransform2
  1054. );
  1055. camera._setTransform(localTransform);
  1056. Cartesian3.clone(Cartesian3.ZERO, camera.position);
  1057. hpr.heading = hpr.heading - CesiumMath.PI_OVER_TWO;
  1058. var rotQuat = Quaternion.fromHeadingPitchRoll(hpr, scratchSetViewQuaternion);
  1059. var rotMat = Matrix3.fromQuaternion(rotQuat, scratchSetViewMatrix3);
  1060. Matrix3.getColumn(rotMat, 0, camera.direction);
  1061. Matrix3.getColumn(rotMat, 2, camera.up);
  1062. Cartesian3.cross(camera.direction, camera.up, camera.right);
  1063. camera._setTransform(currentTransform);
  1064. camera._adjustOrthographicFrustum(true);
  1065. }
  1066. function setViewCV(camera, position, hpr, convert) {
  1067. var currentTransform = Matrix4.clone(
  1068. camera.transform,
  1069. scratchSetViewTransform1
  1070. );
  1071. camera._setTransform(Matrix4.IDENTITY);
  1072. if (!Cartesian3.equals(position, camera.positionWC)) {
  1073. if (convert) {
  1074. var projection = camera._projection;
  1075. var cartographic = projection.ellipsoid.cartesianToCartographic(
  1076. position,
  1077. scratchSetViewCartographic
  1078. );
  1079. position = projection.project(cartographic, scratchSetViewCartesian);
  1080. }
  1081. Cartesian3.clone(position, camera.position);
  1082. }
  1083. hpr.heading = hpr.heading - CesiumMath.PI_OVER_TWO;
  1084. var rotQuat = Quaternion.fromHeadingPitchRoll(hpr, scratchSetViewQuaternion);
  1085. var rotMat = Matrix3.fromQuaternion(rotQuat, scratchSetViewMatrix3);
  1086. Matrix3.getColumn(rotMat, 0, camera.direction);
  1087. Matrix3.getColumn(rotMat, 2, camera.up);
  1088. Cartesian3.cross(camera.direction, camera.up, camera.right);
  1089. camera._setTransform(currentTransform);
  1090. camera._adjustOrthographicFrustum(true);
  1091. }
  1092. function setView2D(camera, position, hpr, convert) {
  1093. var currentTransform = Matrix4.clone(
  1094. camera.transform,
  1095. scratchSetViewTransform1
  1096. );
  1097. camera._setTransform(Matrix4.IDENTITY);
  1098. if (!Cartesian3.equals(position, camera.positionWC)) {
  1099. if (convert) {
  1100. var projection = camera._projection;
  1101. var cartographic = projection.ellipsoid.cartesianToCartographic(
  1102. position,
  1103. scratchSetViewCartographic
  1104. );
  1105. position = projection.project(cartographic, scratchSetViewCartesian);
  1106. }
  1107. Cartesian2.clone(position, camera.position);
  1108. var newLeft = -position.z * 0.5;
  1109. var newRight = -newLeft;
  1110. var frustum = camera.frustum;
  1111. if (newRight > newLeft) {
  1112. var ratio = frustum.top / frustum.right;
  1113. frustum.right = newRight;
  1114. frustum.left = newLeft;
  1115. frustum.top = frustum.right * ratio;
  1116. frustum.bottom = -frustum.top;
  1117. }
  1118. }
  1119. if (camera._scene.mapMode2D === MapMode2D.ROTATE) {
  1120. hpr.heading = hpr.heading - CesiumMath.PI_OVER_TWO;
  1121. hpr.pitch = -CesiumMath.PI_OVER_TWO;
  1122. hpr.roll = 0.0;
  1123. var rotQuat = Quaternion.fromHeadingPitchRoll(
  1124. hpr,
  1125. scratchSetViewQuaternion
  1126. );
  1127. var rotMat = Matrix3.fromQuaternion(rotQuat, scratchSetViewMatrix3);
  1128. Matrix3.getColumn(rotMat, 2, camera.up);
  1129. Cartesian3.cross(camera.direction, camera.up, camera.right);
  1130. }
  1131. camera._setTransform(currentTransform);
  1132. }
  1133. var scratchToHPRDirection = new Cartesian3();
  1134. var scratchToHPRUp = new Cartesian3();
  1135. var scratchToHPRRight = new Cartesian3();
  1136. function directionUpToHeadingPitchRoll(camera, position, orientation, result) {
  1137. var direction = Cartesian3.clone(
  1138. orientation.direction,
  1139. scratchToHPRDirection
  1140. );
  1141. var up = Cartesian3.clone(orientation.up, scratchToHPRUp);
  1142. if (camera._scene.mode === SceneMode.SCENE3D) {
  1143. var ellipsoid = camera._projection.ellipsoid;
  1144. var transform = Transforms.eastNorthUpToFixedFrame(
  1145. position,
  1146. ellipsoid,
  1147. scratchHPRMatrix1
  1148. );
  1149. var invTransform = Matrix4.inverseTransformation(
  1150. transform,
  1151. scratchHPRMatrix2
  1152. );
  1153. Matrix4.multiplyByPointAsVector(invTransform, direction, direction);
  1154. Matrix4.multiplyByPointAsVector(invTransform, up, up);
  1155. }
  1156. var right = Cartesian3.cross(direction, up, scratchToHPRRight);
  1157. result.heading = getHeading(direction, up);
  1158. result.pitch = getPitch(direction);
  1159. result.roll = getRoll(direction, up, right);
  1160. return result;
  1161. }
  1162. var scratchSetViewOptions = {
  1163. destination: undefined,
  1164. orientation: {
  1165. direction: undefined,
  1166. up: undefined,
  1167. heading: undefined,
  1168. pitch: undefined,
  1169. roll: undefined,
  1170. },
  1171. convert: undefined,
  1172. endTransform: undefined,
  1173. };
  1174. var scratchHpr = new HeadingPitchRoll();
  1175. /**
  1176. * Sets the camera position, orientation and transform.
  1177. *
  1178. * @param {Object} options Object with the following properties:
  1179. * @param {Cartesian3|Rectangle} [options.destination] The final position of the camera in WGS84 (world) coordinates or a rectangle that would be visible from a top-down view.
  1180. * @param {Object} [options.orientation] An object that contains either direction and up properties or heading, pitch and roll properties. By default, the direction will point
  1181. * towards the center of the frame in 3D and in the negative z direction in Columbus view. The up direction will point towards local north in 3D and in the positive
  1182. * y direction in Columbus view. Orientation is not used in 2D when in infinite scrolling mode.
  1183. * @param {Matrix4} [options.endTransform] Transform matrix representing the reference frame of the camera.
  1184. * @param {Boolean} [options.convert] Whether to convert the destination from world coordinates to scene coordinates (only relevant when not using 3D). Defaults to <code>true</code>.
  1185. *
  1186. * @example
  1187. * // 1. Set position with a top-down view
  1188. * viewer.camera.setView({
  1189. * destination : Cesium.Cartesian3.fromDegrees(-117.16, 32.71, 15000.0)
  1190. * });
  1191. *
  1192. * // 2 Set view with heading, pitch and roll
  1193. * viewer.camera.setView({
  1194. * destination : cartesianPosition,
  1195. * orientation: {
  1196. * heading : Cesium.Math.toRadians(90.0), // east, default value is 0.0 (north)
  1197. * pitch : Cesium.Math.toRadians(-90), // default value (looking down)
  1198. * roll : 0.0 // default value
  1199. * }
  1200. * });
  1201. *
  1202. * // 3. Change heading, pitch and roll with the camera position remaining the same.
  1203. * viewer.camera.setView({
  1204. * orientation: {
  1205. * heading : Cesium.Math.toRadians(90.0), // east, default value is 0.0 (north)
  1206. * pitch : Cesium.Math.toRadians(-90), // default value (looking down)
  1207. * roll : 0.0 // default value
  1208. * }
  1209. * });
  1210. *
  1211. *
  1212. * // 4. View rectangle with a top-down view
  1213. * viewer.camera.setView({
  1214. * destination : Cesium.Rectangle.fromDegrees(west, south, east, north)
  1215. * });
  1216. *
  1217. * // 5. Set position with an orientation using unit vectors.
  1218. * viewer.camera.setView({
  1219. * destination : Cesium.Cartesian3.fromDegrees(-122.19, 46.25, 5000.0),
  1220. * orientation : {
  1221. * direction : new Cesium.Cartesian3(-0.04231243104240401, -0.20123236049443421, -0.97862924300734),
  1222. * up : new Cesium.Cartesian3(-0.47934589305293746, -0.8553216253114552, 0.1966022179118339)
  1223. * }
  1224. * });
  1225. */
  1226. Camera.prototype.setView = function (options) {
  1227. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  1228. var orientation = defaultValue(
  1229. options.orientation,
  1230. defaultValue.EMPTY_OBJECT
  1231. );
  1232. var mode = this._mode;
  1233. if (mode === SceneMode.MORPHING) {
  1234. return;
  1235. }
  1236. if (defined(options.endTransform)) {
  1237. this._setTransform(options.endTransform);
  1238. }
  1239. var convert = defaultValue(options.convert, true);
  1240. var destination = defaultValue(
  1241. options.destination,
  1242. Cartesian3.clone(this.positionWC, scratchSetViewCartesian)
  1243. );
  1244. if (defined(destination) && defined(destination.west)) {
  1245. destination = this.getRectangleCameraCoordinates(
  1246. destination,
  1247. scratchSetViewCartesian
  1248. );
  1249. convert = false;
  1250. }
  1251. if (defined(orientation.direction)) {
  1252. orientation = directionUpToHeadingPitchRoll(
  1253. this,
  1254. destination,
  1255. orientation,
  1256. scratchSetViewOptions.orientation
  1257. );
  1258. }
  1259. scratchHpr.heading = defaultValue(orientation.heading, 0.0);
  1260. scratchHpr.pitch = defaultValue(orientation.pitch, -CesiumMath.PI_OVER_TWO);
  1261. scratchHpr.roll = defaultValue(orientation.roll, 0.0);
  1262. if (mode === SceneMode.SCENE3D) {
  1263. setView3D(this, destination, scratchHpr);
  1264. } else if (mode === SceneMode.SCENE2D) {
  1265. setView2D(this, destination, scratchHpr, convert);
  1266. } else {
  1267. setViewCV(this, destination, scratchHpr, convert);
  1268. }
  1269. };
  1270. var pitchScratch = new Cartesian3();
  1271. /**
  1272. * Fly the camera to the home view. Use {@link Camera#.DEFAULT_VIEW_RECTANGLE} to set
  1273. * the default view for the 3D scene. The home view for 2D and columbus view shows the
  1274. * entire map.
  1275. *
  1276. * @param {Number} [duration] The duration of the flight in seconds. If omitted, Cesium attempts to calculate an ideal duration based on the distance to be traveled by the flight. See {@link Camera#flyTo}
  1277. */
  1278. Camera.prototype.flyHome = function (duration) {
  1279. var mode = this._mode;
  1280. if (mode === SceneMode.MORPHING) {
  1281. this._scene.completeMorph();
  1282. }
  1283. if (mode === SceneMode.SCENE2D) {
  1284. this.flyTo({
  1285. destination: Camera.DEFAULT_VIEW_RECTANGLE,
  1286. duration: duration,
  1287. endTransform: Matrix4.IDENTITY,
  1288. });
  1289. } else if (mode === SceneMode.SCENE3D) {
  1290. var destination = this.getRectangleCameraCoordinates(
  1291. Camera.DEFAULT_VIEW_RECTANGLE
  1292. );
  1293. var mag = Cartesian3.magnitude(destination);
  1294. mag += mag * Camera.DEFAULT_VIEW_FACTOR;
  1295. Cartesian3.normalize(destination, destination);
  1296. Cartesian3.multiplyByScalar(destination, mag, destination);
  1297. this.flyTo({
  1298. destination: destination,
  1299. duration: duration,
  1300. endTransform: Matrix4.IDENTITY,
  1301. });
  1302. } else if (mode === SceneMode.COLUMBUS_VIEW) {
  1303. var maxRadii = this._projection.ellipsoid.maximumRadius;
  1304. var position = new Cartesian3(0.0, -1.0, 1.0);
  1305. position = Cartesian3.multiplyByScalar(
  1306. Cartesian3.normalize(position, position),
  1307. 5.0 * maxRadii,
  1308. position
  1309. );
  1310. this.flyTo({
  1311. destination: position,
  1312. duration: duration,
  1313. orientation: {
  1314. heading: 0.0,
  1315. pitch: -Math.acos(Cartesian3.normalize(position, pitchScratch).z),
  1316. roll: 0.0,
  1317. },
  1318. endTransform: Matrix4.IDENTITY,
  1319. convert: false,
  1320. });
  1321. }
  1322. };
  1323. /**
  1324. * Transform a vector or point from world coordinates to the camera's reference frame.
  1325. *
  1326. * @param {Cartesian4} cartesian The vector or point to transform.
  1327. * @param {Cartesian4} [result] The object onto which to store the result.
  1328. * @returns {Cartesian4} The transformed vector or point.
  1329. */
  1330. Camera.prototype.worldToCameraCoordinates = function (cartesian, result) {
  1331. //>>includeStart('debug', pragmas.debug);
  1332. if (!defined(cartesian)) {
  1333. throw new DeveloperError("cartesian is required.");
  1334. }
  1335. //>>includeEnd('debug');
  1336. if (!defined(result)) {
  1337. result = new Cartesian4();
  1338. }
  1339. updateMembers(this);
  1340. return Matrix4.multiplyByVector(this._actualInvTransform, cartesian, result);
  1341. };
  1342. /**
  1343. * Transform a point from world coordinates to the camera's reference frame.
  1344. *
  1345. * @param {Cartesian3} cartesian The point to transform.
  1346. * @param {Cartesian3} [result] The object onto which to store the result.
  1347. * @returns {Cartesian3} The transformed point.
  1348. */
  1349. Camera.prototype.worldToCameraCoordinatesPoint = function (cartesian, result) {
  1350. //>>includeStart('debug', pragmas.debug);
  1351. if (!defined(cartesian)) {
  1352. throw new DeveloperError("cartesian is required.");
  1353. }
  1354. //>>includeEnd('debug');
  1355. if (!defined(result)) {
  1356. result = new Cartesian3();
  1357. }
  1358. updateMembers(this);
  1359. return Matrix4.multiplyByPoint(this._actualInvTransform, cartesian, result);
  1360. };
  1361. /**
  1362. * Transform a vector from world coordinates to the camera's reference frame.
  1363. *
  1364. * @param {Cartesian3} cartesian The vector to transform.
  1365. * @param {Cartesian3} [result] The object onto which to store the result.
  1366. * @returns {Cartesian3} The transformed vector.
  1367. */
  1368. Camera.prototype.worldToCameraCoordinatesVector = function (cartesian, result) {
  1369. //>>includeStart('debug', pragmas.debug);
  1370. if (!defined(cartesian)) {
  1371. throw new DeveloperError("cartesian is required.");
  1372. }
  1373. //>>includeEnd('debug');
  1374. if (!defined(result)) {
  1375. result = new Cartesian3();
  1376. }
  1377. updateMembers(this);
  1378. return Matrix4.multiplyByPointAsVector(
  1379. this._actualInvTransform,
  1380. cartesian,
  1381. result
  1382. );
  1383. };
  1384. /**
  1385. * Transform a vector or point from the camera's reference frame to world coordinates.
  1386. *
  1387. * @param {Cartesian4} cartesian The vector or point to transform.
  1388. * @param {Cartesian4} [result] The object onto which to store the result.
  1389. * @returns {Cartesian4} The transformed vector or point.
  1390. */
  1391. Camera.prototype.cameraToWorldCoordinates = function (cartesian, result) {
  1392. //>>includeStart('debug', pragmas.debug);
  1393. if (!defined(cartesian)) {
  1394. throw new DeveloperError("cartesian is required.");
  1395. }
  1396. //>>includeEnd('debug');
  1397. if (!defined(result)) {
  1398. result = new Cartesian4();
  1399. }
  1400. updateMembers(this);
  1401. return Matrix4.multiplyByVector(this._actualTransform, cartesian, result);
  1402. };
  1403. /**
  1404. * Transform a point from the camera's reference frame to world coordinates.
  1405. *
  1406. * @param {Cartesian3} cartesian The point to transform.
  1407. * @param {Cartesian3} [result] The object onto which to store the result.
  1408. * @returns {Cartesian3} The transformed point.
  1409. */
  1410. Camera.prototype.cameraToWorldCoordinatesPoint = function (cartesian, result) {
  1411. //>>includeStart('debug', pragmas.debug);
  1412. if (!defined(cartesian)) {
  1413. throw new DeveloperError("cartesian is required.");
  1414. }
  1415. //>>includeEnd('debug');
  1416. if (!defined(result)) {
  1417. result = new Cartesian3();
  1418. }
  1419. updateMembers(this);
  1420. return Matrix4.multiplyByPoint(this._actualTransform, cartesian, result);
  1421. };
  1422. /**
  1423. * Transform a vector from the camera's reference frame to world coordinates.
  1424. *
  1425. * @param {Cartesian3} cartesian The vector to transform.
  1426. * @param {Cartesian3} [result] The object onto which to store the result.
  1427. * @returns {Cartesian3} The transformed vector.
  1428. */
  1429. Camera.prototype.cameraToWorldCoordinatesVector = function (cartesian, result) {
  1430. //>>includeStart('debug', pragmas.debug);
  1431. if (!defined(cartesian)) {
  1432. throw new DeveloperError("cartesian is required.");
  1433. }
  1434. //>>includeEnd('debug');
  1435. if (!defined(result)) {
  1436. result = new Cartesian3();
  1437. }
  1438. updateMembers(this);
  1439. return Matrix4.multiplyByPointAsVector(
  1440. this._actualTransform,
  1441. cartesian,
  1442. result
  1443. );
  1444. };
  1445. function clampMove2D(camera, position) {
  1446. var rotatable2D = camera._scene.mapMode2D === MapMode2D.ROTATE;
  1447. var maxProjectedX = camera._maxCoord.x;
  1448. var maxProjectedY = camera._maxCoord.y;
  1449. var minX;
  1450. var maxX;
  1451. if (rotatable2D) {
  1452. maxX = maxProjectedX;
  1453. minX = -maxX;
  1454. } else {
  1455. maxX = position.x - maxProjectedX * 2.0;
  1456. minX = position.x + maxProjectedX * 2.0;
  1457. }
  1458. if (position.x > maxProjectedX) {
  1459. position.x = maxX;
  1460. }
  1461. if (position.x < -maxProjectedX) {
  1462. position.x = minX;
  1463. }
  1464. if (position.y > maxProjectedY) {
  1465. position.y = maxProjectedY;
  1466. }
  1467. if (position.y < -maxProjectedY) {
  1468. position.y = -maxProjectedY;
  1469. }
  1470. }
  1471. var moveScratch = new Cartesian3();
  1472. /**
  1473. * Translates the camera's position by <code>amount</code> along <code>direction</code>.
  1474. *
  1475. * @param {Cartesian3} direction The direction to move.
  1476. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  1477. *
  1478. * @see Camera#moveBackward
  1479. * @see Camera#moveForward
  1480. * @see Camera#moveLeft
  1481. * @see Camera#moveRight
  1482. * @see Camera#moveUp
  1483. * @see Camera#moveDown
  1484. */
  1485. Camera.prototype.move = function (direction, amount) {
  1486. //>>includeStart('debug', pragmas.debug);
  1487. if (!defined(direction)) {
  1488. throw new DeveloperError("direction is required.");
  1489. }
  1490. //>>includeEnd('debug');
  1491. var cameraPosition = this.position;
  1492. Cartesian3.multiplyByScalar(direction, amount, moveScratch);
  1493. Cartesian3.add(cameraPosition, moveScratch, cameraPosition);
  1494. if (this._mode === SceneMode.SCENE2D) {
  1495. clampMove2D(this, cameraPosition);
  1496. }
  1497. this._adjustOrthographicFrustum(true);
  1498. };
  1499. /**
  1500. * Translates the camera's position by <code>amount</code> along the camera's view vector.
  1501. * When in 2D mode, this will zoom in the camera instead of translating the camera's position.
  1502. *
  1503. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  1504. *
  1505. * @see Camera#moveBackward
  1506. */
  1507. Camera.prototype.moveForward = function (amount) {
  1508. amount = defaultValue(amount, this.defaultMoveAmount);
  1509. if (this._mode === SceneMode.SCENE2D) {
  1510. // 2D mode
  1511. zoom2D(this, amount);
  1512. } else {
  1513. // 3D or Columbus view mode
  1514. this.move(this.direction, amount);
  1515. }
  1516. };
  1517. /**
  1518. * Translates the camera's position by <code>amount</code> along the opposite direction
  1519. * of the camera's view vector.
  1520. * When in 2D mode, this will zoom out the camera instead of translating the camera's position.
  1521. *
  1522. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  1523. *
  1524. * @see Camera#moveForward
  1525. */
  1526. Camera.prototype.moveBackward = function (amount) {
  1527. amount = defaultValue(amount, this.defaultMoveAmount);
  1528. if (this._mode === SceneMode.SCENE2D) {
  1529. // 2D mode
  1530. zoom2D(this, -amount);
  1531. } else {
  1532. // 3D or Columbus view mode
  1533. this.move(this.direction, -amount);
  1534. }
  1535. };
  1536. /**
  1537. * Translates the camera's position by <code>amount</code> along the camera's up vector.
  1538. *
  1539. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  1540. *
  1541. * @see Camera#moveDown
  1542. */
  1543. Camera.prototype.moveUp = function (amount) {
  1544. amount = defaultValue(amount, this.defaultMoveAmount);
  1545. this.move(this.up, amount);
  1546. };
  1547. /**
  1548. * Translates the camera's position by <code>amount</code> along the opposite direction
  1549. * of the camera's up vector.
  1550. *
  1551. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  1552. *
  1553. * @see Camera#moveUp
  1554. */
  1555. Camera.prototype.moveDown = function (amount) {
  1556. amount = defaultValue(amount, this.defaultMoveAmount);
  1557. this.move(this.up, -amount);
  1558. };
  1559. /**
  1560. * Translates the camera's position by <code>amount</code> along the camera's right vector.
  1561. *
  1562. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  1563. *
  1564. * @see Camera#moveLeft
  1565. */
  1566. Camera.prototype.moveRight = function (amount) {
  1567. amount = defaultValue(amount, this.defaultMoveAmount);
  1568. this.move(this.right, amount);
  1569. };
  1570. /**
  1571. * Translates the camera's position by <code>amount</code> along the opposite direction
  1572. * of the camera's right vector.
  1573. *
  1574. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>.
  1575. *
  1576. * @see Camera#moveRight
  1577. */
  1578. Camera.prototype.moveLeft = function (amount) {
  1579. amount = defaultValue(amount, this.defaultMoveAmount);
  1580. this.move(this.right, -amount);
  1581. };
  1582. /**
  1583. * Rotates the camera around its up vector by amount, in radians, in the opposite direction
  1584. * of its right vector if not in 2D mode.
  1585. *
  1586. * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  1587. *
  1588. * @see Camera#lookRight
  1589. */
  1590. Camera.prototype.lookLeft = function (amount) {
  1591. amount = defaultValue(amount, this.defaultLookAmount);
  1592. // only want view of map to change in 3D mode, 2D visual is incorrect when look changes
  1593. if (this._mode !== SceneMode.SCENE2D) {
  1594. this.look(this.up, -amount);
  1595. }
  1596. };
  1597. /**
  1598. * Rotates the camera around its up vector by amount, in radians, in the direction
  1599. * of its right vector if not in 2D mode.
  1600. *
  1601. * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  1602. *
  1603. * @see Camera#lookLeft
  1604. */
  1605. Camera.prototype.lookRight = function (amount) {
  1606. amount = defaultValue(amount, this.defaultLookAmount);
  1607. // only want view of map to change in 3D mode, 2D visual is incorrect when look changes
  1608. if (this._mode !== SceneMode.SCENE2D) {
  1609. this.look(this.up, amount);
  1610. }
  1611. };
  1612. /**
  1613. * Rotates the camera around its right vector by amount, in radians, in the direction
  1614. * of its up vector if not in 2D mode.
  1615. *
  1616. * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  1617. *
  1618. * @see Camera#lookDown
  1619. */
  1620. Camera.prototype.lookUp = function (amount) {
  1621. amount = defaultValue(amount, this.defaultLookAmount);
  1622. // only want view of map to change in 3D mode, 2D visual is incorrect when look changes
  1623. if (this._mode !== SceneMode.SCENE2D) {
  1624. this.look(this.right, -amount);
  1625. }
  1626. };
  1627. /**
  1628. * Rotates the camera around its right vector by amount, in radians, in the opposite direction
  1629. * of its up vector if not in 2D mode.
  1630. *
  1631. * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  1632. *
  1633. * @see Camera#lookUp
  1634. */
  1635. Camera.prototype.lookDown = function (amount) {
  1636. amount = defaultValue(amount, this.defaultLookAmount);
  1637. // only want view of map to change in 3D mode, 2D visual is incorrect when look changes
  1638. if (this._mode !== SceneMode.SCENE2D) {
  1639. this.look(this.right, amount);
  1640. }
  1641. };
  1642. var lookScratchQuaternion = new Quaternion();
  1643. var lookScratchMatrix = new Matrix3();
  1644. /**
  1645. * Rotate each of the camera's orientation vectors around <code>axis</code> by <code>angle</code>
  1646. *
  1647. * @param {Cartesian3} axis The axis to rotate around.
  1648. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  1649. *
  1650. * @see Camera#lookUp
  1651. * @see Camera#lookDown
  1652. * @see Camera#lookLeft
  1653. * @see Camera#lookRight
  1654. */
  1655. Camera.prototype.look = function (axis, angle) {
  1656. //>>includeStart('debug', pragmas.debug);
  1657. if (!defined(axis)) {
  1658. throw new DeveloperError("axis is required.");
  1659. }
  1660. //>>includeEnd('debug');
  1661. var turnAngle = defaultValue(angle, this.defaultLookAmount);
  1662. var quaternion = Quaternion.fromAxisAngle(
  1663. axis,
  1664. -turnAngle,
  1665. lookScratchQuaternion
  1666. );
  1667. var rotation = Matrix3.fromQuaternion(quaternion, lookScratchMatrix);
  1668. var direction = this.direction;
  1669. var up = this.up;
  1670. var right = this.right;
  1671. Matrix3.multiplyByVector(rotation, direction, direction);
  1672. Matrix3.multiplyByVector(rotation, up, up);
  1673. Matrix3.multiplyByVector(rotation, right, right);
  1674. };
  1675. /**
  1676. * Rotate the camera counter-clockwise around its direction vector by amount, in radians.
  1677. *
  1678. * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  1679. *
  1680. * @see Camera#twistRight
  1681. */
  1682. Camera.prototype.twistLeft = function (amount) {
  1683. amount = defaultValue(amount, this.defaultLookAmount);
  1684. this.look(this.direction, amount);
  1685. };
  1686. /**
  1687. * Rotate the camera clockwise around its direction vector by amount, in radians.
  1688. *
  1689. * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>.
  1690. *
  1691. * @see Camera#twistLeft
  1692. */
  1693. Camera.prototype.twistRight = function (amount) {
  1694. amount = defaultValue(amount, this.defaultLookAmount);
  1695. this.look(this.direction, -amount);
  1696. };
  1697. var rotateScratchQuaternion = new Quaternion();
  1698. var rotateScratchMatrix = new Matrix3();
  1699. /**
  1700. * Rotates the camera around <code>axis</code> by <code>angle</code>. The distance
  1701. * of the camera's position to the center of the camera's reference frame remains the same.
  1702. *
  1703. * @param {Cartesian3} axis The axis to rotate around given in world coordinates.
  1704. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>.
  1705. *
  1706. * @see Camera#rotateUp
  1707. * @see Camera#rotateDown
  1708. * @see Camera#rotateLeft
  1709. * @see Camera#rotateRight
  1710. */
  1711. Camera.prototype.rotate = function (axis, angle) {
  1712. //>>includeStart('debug', pragmas.debug);
  1713. if (!defined(axis)) {
  1714. throw new DeveloperError("axis is required.");
  1715. }
  1716. //>>includeEnd('debug');
  1717. var turnAngle = defaultValue(angle, this.defaultRotateAmount);
  1718. var quaternion = Quaternion.fromAxisAngle(
  1719. axis,
  1720. -turnAngle,
  1721. rotateScratchQuaternion
  1722. );
  1723. var rotation = Matrix3.fromQuaternion(quaternion, rotateScratchMatrix);
  1724. Matrix3.multiplyByVector(rotation, this.position, this.position);
  1725. Matrix3.multiplyByVector(rotation, this.direction, this.direction);
  1726. Matrix3.multiplyByVector(rotation, this.up, this.up);
  1727. Cartesian3.cross(this.direction, this.up, this.right);
  1728. Cartesian3.cross(this.right, this.direction, this.up);
  1729. this._adjustOrthographicFrustum(false);
  1730. };
  1731. /**
  1732. * Rotates the camera around the center of the camera's reference frame by angle downwards.
  1733. *
  1734. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>.
  1735. *
  1736. * @see Camera#rotateUp
  1737. * @see Camera#rotate
  1738. */
  1739. Camera.prototype.rotateDown = function (angle) {
  1740. angle = defaultValue(angle, this.defaultRotateAmount);
  1741. rotateVertical(this, angle);
  1742. };
  1743. /**
  1744. * Rotates the camera around the center of the camera's reference frame by angle upwards.
  1745. *
  1746. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>.
  1747. *
  1748. * @see Camera#rotateDown
  1749. * @see Camera#rotate
  1750. */
  1751. Camera.prototype.rotateUp = function (angle) {
  1752. angle = defaultValue(angle, this.defaultRotateAmount);
  1753. rotateVertical(this, -angle);
  1754. };
  1755. var rotateVertScratchP = new Cartesian3();
  1756. var rotateVertScratchA = new Cartesian3();
  1757. var rotateVertScratchTan = new Cartesian3();
  1758. var rotateVertScratchNegate = new Cartesian3();
  1759. function rotateVertical(camera, angle) {
  1760. var position = camera.position;
  1761. if (
  1762. defined(camera.constrainedAxis) &&
  1763. !Cartesian3.equalsEpsilon(
  1764. camera.position,
  1765. Cartesian3.ZERO,
  1766. CesiumMath.EPSILON2
  1767. )
  1768. ) {
  1769. var p = Cartesian3.normalize(position, rotateVertScratchP);
  1770. var northParallel = Cartesian3.equalsEpsilon(
  1771. p,
  1772. camera.constrainedAxis,
  1773. CesiumMath.EPSILON2
  1774. );
  1775. var southParallel = Cartesian3.equalsEpsilon(
  1776. p,
  1777. Cartesian3.negate(camera.constrainedAxis, rotateVertScratchNegate),
  1778. CesiumMath.EPSILON2
  1779. );
  1780. if (!northParallel && !southParallel) {
  1781. var constrainedAxis = Cartesian3.normalize(
  1782. camera.constrainedAxis,
  1783. rotateVertScratchA
  1784. );
  1785. var dot = Cartesian3.dot(p, constrainedAxis);
  1786. var angleToAxis = CesiumMath.acosClamped(dot);
  1787. if (angle > 0 && angle > angleToAxis) {
  1788. angle = angleToAxis - CesiumMath.EPSILON4;
  1789. }
  1790. dot = Cartesian3.dot(
  1791. p,
  1792. Cartesian3.negate(constrainedAxis, rotateVertScratchNegate)
  1793. );
  1794. angleToAxis = CesiumMath.acosClamped(dot);
  1795. if (angle < 0 && -angle > angleToAxis) {
  1796. angle = -angleToAxis + CesiumMath.EPSILON4;
  1797. }
  1798. var tangent = Cartesian3.cross(constrainedAxis, p, rotateVertScratchTan);
  1799. camera.rotate(tangent, angle);
  1800. } else if ((northParallel && angle < 0) || (southParallel && angle > 0)) {
  1801. camera.rotate(camera.right, angle);
  1802. }
  1803. } else {
  1804. camera.rotate(camera.right, angle);
  1805. }
  1806. }
  1807. /**
  1808. * Rotates the camera around the center of the camera's reference frame by angle to the right.
  1809. *
  1810. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>.
  1811. *
  1812. * @see Camera#rotateLeft
  1813. * @see Camera#rotate
  1814. */
  1815. Camera.prototype.rotateRight = function (angle) {
  1816. angle = defaultValue(angle, this.defaultRotateAmount);
  1817. rotateHorizontal(this, -angle);
  1818. };
  1819. /**
  1820. * Rotates the camera around the center of the camera's reference frame by angle to the left.
  1821. *
  1822. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>.
  1823. *
  1824. * @see Camera#rotateRight
  1825. * @see Camera#rotate
  1826. */
  1827. Camera.prototype.rotateLeft = function (angle) {
  1828. angle = defaultValue(angle, this.defaultRotateAmount);
  1829. rotateHorizontal(this, angle);
  1830. };
  1831. function rotateHorizontal(camera, angle) {
  1832. if (defined(camera.constrainedAxis)) {
  1833. camera.rotate(camera.constrainedAxis, angle);
  1834. } else {
  1835. camera.rotate(camera.up, angle);
  1836. }
  1837. }
  1838. function zoom2D(camera, amount) {
  1839. var frustum = camera.frustum;
  1840. //>>includeStart('debug', pragmas.debug);
  1841. if (
  1842. !(frustum instanceof OrthographicOffCenterFrustum) ||
  1843. !defined(frustum.left) ||
  1844. !defined(frustum.right) ||
  1845. !defined(frustum.bottom) ||
  1846. !defined(frustum.top)
  1847. ) {
  1848. throw new DeveloperError(
  1849. "The camera frustum is expected to be orthographic for 2D camera control."
  1850. );
  1851. }
  1852. //>>includeEnd('debug');
  1853. var ratio;
  1854. amount = amount * 0.5;
  1855. if (
  1856. Math.abs(frustum.top) + Math.abs(frustum.bottom) >
  1857. Math.abs(frustum.left) + Math.abs(frustum.right)
  1858. ) {
  1859. var newTop = frustum.top - amount;
  1860. var newBottom = frustum.bottom + amount;
  1861. var maxBottom = camera._maxCoord.y;
  1862. if (camera._scene.mapMode2D === MapMode2D.ROTATE) {
  1863. maxBottom *= camera.maximumZoomFactor;
  1864. }
  1865. if (newBottom > maxBottom) {
  1866. newBottom = maxBottom;
  1867. newTop = -maxBottom;
  1868. }
  1869. if (newTop <= newBottom) {
  1870. newTop = 1.0;
  1871. newBottom = -1.0;
  1872. }
  1873. ratio = frustum.right / frustum.top;
  1874. frustum.top = newTop;
  1875. frustum.bottom = newBottom;
  1876. frustum.right = frustum.top * ratio;
  1877. frustum.left = -frustum.right;
  1878. } else {
  1879. var newRight = frustum.right - amount;
  1880. var newLeft = frustum.left + amount;
  1881. var maxRight = camera._maxCoord.x;
  1882. if (camera._scene.mapMode2D === MapMode2D.ROTATE) {
  1883. maxRight *= camera.maximumZoomFactor;
  1884. }
  1885. if (newRight > maxRight) {
  1886. newRight = maxRight;
  1887. newLeft = -maxRight;
  1888. }
  1889. if (newRight <= newLeft) {
  1890. newRight = 1.0;
  1891. newLeft = -1.0;
  1892. }
  1893. ratio = frustum.top / frustum.right;
  1894. frustum.right = newRight;
  1895. frustum.left = newLeft;
  1896. frustum.top = frustum.right * ratio;
  1897. frustum.bottom = -frustum.top;
  1898. }
  1899. }
  1900. function zoom3D(camera, amount) {
  1901. camera.move(camera.direction, amount);
  1902. }
  1903. /**
  1904. * Zooms <code>amount</code> along the camera's view vector.
  1905. *
  1906. * @param {Number} [amount] The amount to move. Defaults to <code>defaultZoomAmount</code>.
  1907. *
  1908. * @see Camera#zoomOut
  1909. */
  1910. Camera.prototype.zoomIn = function (amount) {
  1911. amount = defaultValue(amount, this.defaultZoomAmount);
  1912. if (this._mode === SceneMode.SCENE2D) {
  1913. zoom2D(this, amount);
  1914. } else {
  1915. zoom3D(this, amount);
  1916. }
  1917. };
  1918. /**
  1919. * Zooms <code>amount</code> along the opposite direction of
  1920. * the camera's view vector.
  1921. *
  1922. * @param {Number} [amount] The amount to move. Defaults to <code>defaultZoomAmount</code>.
  1923. *
  1924. * @see Camera#zoomIn
  1925. */
  1926. Camera.prototype.zoomOut = function (amount) {
  1927. amount = defaultValue(amount, this.defaultZoomAmount);
  1928. if (this._mode === SceneMode.SCENE2D) {
  1929. zoom2D(this, -amount);
  1930. } else {
  1931. zoom3D(this, -amount);
  1932. }
  1933. };
  1934. /**
  1935. * Gets the magnitude of the camera position. In 3D, this is the vector magnitude. In 2D and
  1936. * Columbus view, this is the distance to the map.
  1937. *
  1938. * @returns {Number} The magnitude of the position.
  1939. */
  1940. Camera.prototype.getMagnitude = function () {
  1941. if (this._mode === SceneMode.SCENE3D) {
  1942. return Cartesian3.magnitude(this.position);
  1943. } else if (this._mode === SceneMode.COLUMBUS_VIEW) {
  1944. return Math.abs(this.position.z);
  1945. } else if (this._mode === SceneMode.SCENE2D) {
  1946. return Math.max(
  1947. this.frustum.right - this.frustum.left,
  1948. this.frustum.top - this.frustum.bottom
  1949. );
  1950. }
  1951. };
  1952. var scratchLookAtMatrix4 = new Matrix4();
  1953. /**
  1954. * Sets the camera position and orientation using a target and offset. The target must be given in
  1955. * world coordinates. The offset can be either a cartesian or heading/pitch/range in the local east-north-up reference frame centered at the target.
  1956. * If the offset is a cartesian, then it is an offset from the center of the reference frame defined by the transformation matrix. If the offset
  1957. * is heading/pitch/range, then the heading and the pitch angles are defined in the reference frame defined by the transformation matrix.
  1958. * The heading is the angle from y axis and increasing towards the x axis. Pitch is the rotation from the xy-plane. Positive pitch
  1959. * angles are below the plane. Negative pitch angles are above the plane. The range is the distance from the center.
  1960. *
  1961. * In 2D, there must be a top down view. The camera will be placed above the target looking down. The height above the
  1962. * target will be the magnitude of the offset. The heading will be determined from the offset. If the heading cannot be
  1963. * determined from the offset, the heading will be north.
  1964. *
  1965. * @param {Cartesian3} target The target position in world coordinates.
  1966. * @param {Cartesian3|HeadingPitchRange} offset The offset from the target in the local east-north-up reference frame centered at the target.
  1967. *
  1968. * @exception {DeveloperError} lookAt is not supported while morphing.
  1969. *
  1970. * @example
  1971. * // 1. Using a cartesian offset
  1972. * var center = Cesium.Cartesian3.fromDegrees(-98.0, 40.0);
  1973. * viewer.camera.lookAt(center, new Cesium.Cartesian3(0.0, -4790000.0, 3930000.0));
  1974. *
  1975. * // 2. Using a HeadingPitchRange offset
  1976. * var center = Cesium.Cartesian3.fromDegrees(-72.0, 40.0);
  1977. * var heading = Cesium.Math.toRadians(50.0);
  1978. * var pitch = Cesium.Math.toRadians(-20.0);
  1979. * var range = 5000.0;
  1980. * viewer.camera.lookAt(center, new Cesium.HeadingPitchRange(heading, pitch, range));
  1981. */
  1982. Camera.prototype.lookAt = function (target, offset) {
  1983. //>>includeStart('debug', pragmas.debug);
  1984. if (!defined(target)) {
  1985. throw new DeveloperError("target is required");
  1986. }
  1987. if (!defined(offset)) {
  1988. throw new DeveloperError("offset is required");
  1989. }
  1990. if (this._mode === SceneMode.MORPHING) {
  1991. throw new DeveloperError("lookAt is not supported while morphing.");
  1992. }
  1993. //>>includeEnd('debug');
  1994. var transform = Transforms.eastNorthUpToFixedFrame(
  1995. target,
  1996. Ellipsoid.WGS84,
  1997. scratchLookAtMatrix4
  1998. );
  1999. this.lookAtTransform(transform, offset);
  2000. };
  2001. var scratchLookAtHeadingPitchRangeOffset = new Cartesian3();
  2002. var scratchLookAtHeadingPitchRangeQuaternion1 = new Quaternion();
  2003. var scratchLookAtHeadingPitchRangeQuaternion2 = new Quaternion();
  2004. var scratchHeadingPitchRangeMatrix3 = new Matrix3();
  2005. function offsetFromHeadingPitchRange(heading, pitch, range) {
  2006. pitch = CesiumMath.clamp(
  2007. pitch,
  2008. -CesiumMath.PI_OVER_TWO,
  2009. CesiumMath.PI_OVER_TWO
  2010. );
  2011. heading = CesiumMath.zeroToTwoPi(heading) - CesiumMath.PI_OVER_TWO;
  2012. var pitchQuat = Quaternion.fromAxisAngle(
  2013. Cartesian3.UNIT_Y,
  2014. -pitch,
  2015. scratchLookAtHeadingPitchRangeQuaternion1
  2016. );
  2017. var headingQuat = Quaternion.fromAxisAngle(
  2018. Cartesian3.UNIT_Z,
  2019. -heading,
  2020. scratchLookAtHeadingPitchRangeQuaternion2
  2021. );
  2022. var rotQuat = Quaternion.multiply(headingQuat, pitchQuat, headingQuat);
  2023. var rotMatrix = Matrix3.fromQuaternion(
  2024. rotQuat,
  2025. scratchHeadingPitchRangeMatrix3
  2026. );
  2027. var offset = Cartesian3.clone(
  2028. Cartesian3.UNIT_X,
  2029. scratchLookAtHeadingPitchRangeOffset
  2030. );
  2031. Matrix3.multiplyByVector(rotMatrix, offset, offset);
  2032. Cartesian3.negate(offset, offset);
  2033. Cartesian3.multiplyByScalar(offset, range, offset);
  2034. return offset;
  2035. }
  2036. /**
  2037. * Sets the camera position and orientation using a target and transformation matrix. The offset can be either a cartesian or heading/pitch/range.
  2038. * If the offset is a cartesian, then it is an offset from the center of the reference frame defined by the transformation matrix. If the offset
  2039. * is heading/pitch/range, then the heading and the pitch angles are defined in the reference frame defined by the transformation matrix.
  2040. * The heading is the angle from y axis and increasing towards the x axis. Pitch is the rotation from the xy-plane. Positive pitch
  2041. * angles are below the plane. Negative pitch angles are above the plane. The range is the distance from the center.
  2042. *
  2043. * In 2D, there must be a top down view. The camera will be placed above the center of the reference frame. The height above the
  2044. * target will be the magnitude of the offset. The heading will be determined from the offset. If the heading cannot be
  2045. * determined from the offset, the heading will be north.
  2046. *
  2047. * @param {Matrix4} transform The transformation matrix defining the reference frame.
  2048. * @param {Cartesian3|HeadingPitchRange} [offset] The offset from the target in a reference frame centered at the target.
  2049. *
  2050. * @exception {DeveloperError} lookAtTransform is not supported while morphing.
  2051. *
  2052. * @example
  2053. * // 1. Using a cartesian offset
  2054. * var transform = Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(-98.0, 40.0));
  2055. * viewer.camera.lookAtTransform(transform, new Cesium.Cartesian3(0.0, -4790000.0, 3930000.0));
  2056. *
  2057. * // 2. Using a HeadingPitchRange offset
  2058. * var transform = Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(-72.0, 40.0));
  2059. * var heading = Cesium.Math.toRadians(50.0);
  2060. * var pitch = Cesium.Math.toRadians(-20.0);
  2061. * var range = 5000.0;
  2062. * viewer.camera.lookAtTransform(transform, new Cesium.HeadingPitchRange(heading, pitch, range));
  2063. */
  2064. Camera.prototype.lookAtTransform = function (transform, offset) {
  2065. //>>includeStart('debug', pragmas.debug);
  2066. if (!defined(transform)) {
  2067. throw new DeveloperError("transform is required");
  2068. }
  2069. if (this._mode === SceneMode.MORPHING) {
  2070. throw new DeveloperError(
  2071. "lookAtTransform is not supported while morphing."
  2072. );
  2073. }
  2074. //>>includeEnd('debug');
  2075. this._setTransform(transform);
  2076. if (!defined(offset)) {
  2077. return;
  2078. }
  2079. var cartesianOffset;
  2080. if (defined(offset.heading)) {
  2081. cartesianOffset = offsetFromHeadingPitchRange(
  2082. offset.heading,
  2083. offset.pitch,
  2084. offset.range
  2085. );
  2086. } else {
  2087. cartesianOffset = offset;
  2088. }
  2089. if (this._mode === SceneMode.SCENE2D) {
  2090. Cartesian2.clone(Cartesian2.ZERO, this.position);
  2091. Cartesian3.negate(cartesianOffset, this.up);
  2092. this.up.z = 0.0;
  2093. if (Cartesian3.magnitudeSquared(this.up) < CesiumMath.EPSILON10) {
  2094. Cartesian3.clone(Cartesian3.UNIT_Y, this.up);
  2095. }
  2096. Cartesian3.normalize(this.up, this.up);
  2097. this._setTransform(Matrix4.IDENTITY);
  2098. Cartesian3.negate(Cartesian3.UNIT_Z, this.direction);
  2099. Cartesian3.cross(this.direction, this.up, this.right);
  2100. Cartesian3.normalize(this.right, this.right);
  2101. var frustum = this.frustum;
  2102. var ratio = frustum.top / frustum.right;
  2103. frustum.right = Cartesian3.magnitude(cartesianOffset) * 0.5;
  2104. frustum.left = -frustum.right;
  2105. frustum.top = ratio * frustum.right;
  2106. frustum.bottom = -frustum.top;
  2107. this._setTransform(transform);
  2108. return;
  2109. }
  2110. Cartesian3.clone(cartesianOffset, this.position);
  2111. Cartesian3.negate(this.position, this.direction);
  2112. Cartesian3.normalize(this.direction, this.direction);
  2113. Cartesian3.cross(this.direction, Cartesian3.UNIT_Z, this.right);
  2114. if (Cartesian3.magnitudeSquared(this.right) < CesiumMath.EPSILON10) {
  2115. Cartesian3.clone(Cartesian3.UNIT_X, this.right);
  2116. }
  2117. Cartesian3.normalize(this.right, this.right);
  2118. Cartesian3.cross(this.right, this.direction, this.up);
  2119. Cartesian3.normalize(this.up, this.up);
  2120. this._adjustOrthographicFrustum(true);
  2121. };
  2122. var viewRectangle3DCartographic1 = new Cartographic();
  2123. var viewRectangle3DCartographic2 = new Cartographic();
  2124. var viewRectangle3DNorthEast = new Cartesian3();
  2125. var viewRectangle3DSouthWest = new Cartesian3();
  2126. var viewRectangle3DNorthWest = new Cartesian3();
  2127. var viewRectangle3DSouthEast = new Cartesian3();
  2128. var viewRectangle3DNorthCenter = new Cartesian3();
  2129. var viewRectangle3DSouthCenter = new Cartesian3();
  2130. var viewRectangle3DCenter = new Cartesian3();
  2131. var viewRectangle3DEquator = new Cartesian3();
  2132. var defaultRF = {
  2133. direction: new Cartesian3(),
  2134. right: new Cartesian3(),
  2135. up: new Cartesian3(),
  2136. };
  2137. var viewRectangle3DEllipsoidGeodesic;
  2138. function computeD(direction, upOrRight, corner, tanThetaOrPhi) {
  2139. var opposite = Math.abs(Cartesian3.dot(upOrRight, corner));
  2140. return opposite / tanThetaOrPhi - Cartesian3.dot(direction, corner);
  2141. }
  2142. function rectangleCameraPosition3D(camera, rectangle, result, updateCamera) {
  2143. var ellipsoid = camera._projection.ellipsoid;
  2144. var cameraRF = updateCamera ? camera : defaultRF;
  2145. var north = rectangle.north;
  2146. var south = rectangle.south;
  2147. var east = rectangle.east;
  2148. var west = rectangle.west;
  2149. // If we go across the International Date Line
  2150. if (west > east) {
  2151. east += CesiumMath.TWO_PI;
  2152. }
  2153. // Find the midpoint latitude.
  2154. //
  2155. // EllipsoidGeodesic will fail if the north and south edges are very close to being on opposite sides of the ellipsoid.
  2156. // Ideally we'd just call EllipsoidGeodesic.setEndPoints and let it throw when it detects this case, but sadly it doesn't
  2157. // even look for this case in optimized builds, so we have to test for it here instead.
  2158. //
  2159. // Fortunately, this case can only happen (here) when north is very close to the north pole and south is very close to the south pole,
  2160. // so handle it just by using 0 latitude as the center. It's certainliy possible to use a smaller tolerance
  2161. // than one degree here, but one degree is safe and putting the center at 0 latitude should be good enough for any
  2162. // rectangle that spans 178+ of the 180 degrees of latitude.
  2163. var longitude = (west + east) * 0.5;
  2164. var latitude;
  2165. if (
  2166. south < -CesiumMath.PI_OVER_TWO + CesiumMath.RADIANS_PER_DEGREE &&
  2167. north > CesiumMath.PI_OVER_TWO - CesiumMath.RADIANS_PER_DEGREE
  2168. ) {
  2169. latitude = 0.0;
  2170. } else {
  2171. var northCartographic = viewRectangle3DCartographic1;
  2172. northCartographic.longitude = longitude;
  2173. northCartographic.latitude = north;
  2174. northCartographic.height = 0.0;
  2175. var southCartographic = viewRectangle3DCartographic2;
  2176. southCartographic.longitude = longitude;
  2177. southCartographic.latitude = south;
  2178. southCartographic.height = 0.0;
  2179. var ellipsoidGeodesic = viewRectangle3DEllipsoidGeodesic;
  2180. if (
  2181. !defined(ellipsoidGeodesic) ||
  2182. ellipsoidGeodesic.ellipsoid !== ellipsoid
  2183. ) {
  2184. viewRectangle3DEllipsoidGeodesic = ellipsoidGeodesic = new EllipsoidGeodesic(
  2185. undefined,
  2186. undefined,
  2187. ellipsoid
  2188. );
  2189. }
  2190. ellipsoidGeodesic.setEndPoints(northCartographic, southCartographic);
  2191. latitude = ellipsoidGeodesic.interpolateUsingFraction(
  2192. 0.5,
  2193. viewRectangle3DCartographic1
  2194. ).latitude;
  2195. }
  2196. var centerCartographic = viewRectangle3DCartographic1;
  2197. centerCartographic.longitude = longitude;
  2198. centerCartographic.latitude = latitude;
  2199. centerCartographic.height = 0.0;
  2200. var center = ellipsoid.cartographicToCartesian(
  2201. centerCartographic,
  2202. viewRectangle3DCenter
  2203. );
  2204. var cart = viewRectangle3DCartographic1;
  2205. cart.longitude = east;
  2206. cart.latitude = north;
  2207. var northEast = ellipsoid.cartographicToCartesian(
  2208. cart,
  2209. viewRectangle3DNorthEast
  2210. );
  2211. cart.longitude = west;
  2212. var northWest = ellipsoid.cartographicToCartesian(
  2213. cart,
  2214. viewRectangle3DNorthWest
  2215. );
  2216. cart.longitude = longitude;
  2217. var northCenter = ellipsoid.cartographicToCartesian(
  2218. cart,
  2219. viewRectangle3DNorthCenter
  2220. );
  2221. cart.latitude = south;
  2222. var southCenter = ellipsoid.cartographicToCartesian(
  2223. cart,
  2224. viewRectangle3DSouthCenter
  2225. );
  2226. cart.longitude = east;
  2227. var southEast = ellipsoid.cartographicToCartesian(
  2228. cart,
  2229. viewRectangle3DSouthEast
  2230. );
  2231. cart.longitude = west;
  2232. var southWest = ellipsoid.cartographicToCartesian(
  2233. cart,
  2234. viewRectangle3DSouthWest
  2235. );
  2236. Cartesian3.subtract(northWest, center, northWest);
  2237. Cartesian3.subtract(southEast, center, southEast);
  2238. Cartesian3.subtract(northEast, center, northEast);
  2239. Cartesian3.subtract(southWest, center, southWest);
  2240. Cartesian3.subtract(northCenter, center, northCenter);
  2241. Cartesian3.subtract(southCenter, center, southCenter);
  2242. var direction = ellipsoid.geodeticSurfaceNormal(center, cameraRF.direction);
  2243. Cartesian3.negate(direction, direction);
  2244. var right = Cartesian3.cross(direction, Cartesian3.UNIT_Z, cameraRF.right);
  2245. Cartesian3.normalize(right, right);
  2246. var up = Cartesian3.cross(right, direction, cameraRF.up);
  2247. var d;
  2248. if (camera.frustum instanceof OrthographicFrustum) {
  2249. var width = Math.max(
  2250. Cartesian3.distance(northEast, northWest),
  2251. Cartesian3.distance(southEast, southWest)
  2252. );
  2253. var height = Math.max(
  2254. Cartesian3.distance(northEast, southEast),
  2255. Cartesian3.distance(northWest, southWest)
  2256. );
  2257. var rightScalar;
  2258. var topScalar;
  2259. var ratio =
  2260. camera.frustum._offCenterFrustum.right /
  2261. camera.frustum._offCenterFrustum.top;
  2262. var heightRatio = height * ratio;
  2263. if (width > heightRatio) {
  2264. rightScalar = width;
  2265. topScalar = rightScalar / ratio;
  2266. } else {
  2267. topScalar = height;
  2268. rightScalar = heightRatio;
  2269. }
  2270. d = Math.max(rightScalar, topScalar);
  2271. } else {
  2272. var tanPhi = Math.tan(camera.frustum.fovy * 0.5);
  2273. var tanTheta = camera.frustum.aspectRatio * tanPhi;
  2274. d = Math.max(
  2275. computeD(direction, up, northWest, tanPhi),
  2276. computeD(direction, up, southEast, tanPhi),
  2277. computeD(direction, up, northEast, tanPhi),
  2278. computeD(direction, up, southWest, tanPhi),
  2279. computeD(direction, up, northCenter, tanPhi),
  2280. computeD(direction, up, southCenter, tanPhi),
  2281. computeD(direction, right, northWest, tanTheta),
  2282. computeD(direction, right, southEast, tanTheta),
  2283. computeD(direction, right, northEast, tanTheta),
  2284. computeD(direction, right, southWest, tanTheta),
  2285. computeD(direction, right, northCenter, tanTheta),
  2286. computeD(direction, right, southCenter, tanTheta)
  2287. );
  2288. // If the rectangle crosses the equator, compute D at the equator, too, because that's the
  2289. // widest part of the rectangle when projected onto the globe.
  2290. if (south < 0 && north > 0) {
  2291. var equatorCartographic = viewRectangle3DCartographic1;
  2292. equatorCartographic.longitude = west;
  2293. equatorCartographic.latitude = 0.0;
  2294. equatorCartographic.height = 0.0;
  2295. var equatorPosition = ellipsoid.cartographicToCartesian(
  2296. equatorCartographic,
  2297. viewRectangle3DEquator
  2298. );
  2299. Cartesian3.subtract(equatorPosition, center, equatorPosition);
  2300. d = Math.max(
  2301. d,
  2302. computeD(direction, up, equatorPosition, tanPhi),
  2303. computeD(direction, right, equatorPosition, tanTheta)
  2304. );
  2305. equatorCartographic.longitude = east;
  2306. equatorPosition = ellipsoid.cartographicToCartesian(
  2307. equatorCartographic,
  2308. viewRectangle3DEquator
  2309. );
  2310. Cartesian3.subtract(equatorPosition, center, equatorPosition);
  2311. d = Math.max(
  2312. d,
  2313. computeD(direction, up, equatorPosition, tanPhi),
  2314. computeD(direction, right, equatorPosition, tanTheta)
  2315. );
  2316. }
  2317. }
  2318. return Cartesian3.add(
  2319. center,
  2320. Cartesian3.multiplyByScalar(direction, -d, viewRectangle3DEquator),
  2321. result
  2322. );
  2323. }
  2324. var viewRectangleCVCartographic = new Cartographic();
  2325. var viewRectangleCVNorthEast = new Cartesian3();
  2326. var viewRectangleCVSouthWest = new Cartesian3();
  2327. function rectangleCameraPositionColumbusView(camera, rectangle, result) {
  2328. var projection = camera._projection;
  2329. if (rectangle.west > rectangle.east) {
  2330. rectangle = Rectangle.MAX_VALUE;
  2331. }
  2332. var transform = camera._actualTransform;
  2333. var invTransform = camera._actualInvTransform;
  2334. var cart = viewRectangleCVCartographic;
  2335. cart.longitude = rectangle.east;
  2336. cart.latitude = rectangle.north;
  2337. var northEast = projection.project(cart, viewRectangleCVNorthEast);
  2338. Matrix4.multiplyByPoint(transform, northEast, northEast);
  2339. Matrix4.multiplyByPoint(invTransform, northEast, northEast);
  2340. cart.longitude = rectangle.west;
  2341. cart.latitude = rectangle.south;
  2342. var southWest = projection.project(cart, viewRectangleCVSouthWest);
  2343. Matrix4.multiplyByPoint(transform, southWest, southWest);
  2344. Matrix4.multiplyByPoint(invTransform, southWest, southWest);
  2345. result.x = (northEast.x - southWest.x) * 0.5 + southWest.x;
  2346. result.y = (northEast.y - southWest.y) * 0.5 + southWest.y;
  2347. if (defined(camera.frustum.fovy)) {
  2348. var tanPhi = Math.tan(camera.frustum.fovy * 0.5);
  2349. var tanTheta = camera.frustum.aspectRatio * tanPhi;
  2350. result.z =
  2351. Math.max(
  2352. (northEast.x - southWest.x) / tanTheta,
  2353. (northEast.y - southWest.y) / tanPhi
  2354. ) * 0.5;
  2355. } else {
  2356. var width = northEast.x - southWest.x;
  2357. var height = northEast.y - southWest.y;
  2358. result.z = Math.max(width, height);
  2359. }
  2360. return result;
  2361. }
  2362. var viewRectangle2DCartographic = new Cartographic();
  2363. var viewRectangle2DNorthEast = new Cartesian3();
  2364. var viewRectangle2DSouthWest = new Cartesian3();
  2365. function rectangleCameraPosition2D(camera, rectangle, result) {
  2366. var projection = camera._projection;
  2367. // Account for the rectangle crossing the International Date Line in 2D mode
  2368. var east = rectangle.east;
  2369. if (rectangle.west > rectangle.east) {
  2370. if (camera._scene.mapMode2D === MapMode2D.INFINITE_SCROLL) {
  2371. east += CesiumMath.TWO_PI;
  2372. } else {
  2373. rectangle = Rectangle.MAX_VALUE;
  2374. east = rectangle.east;
  2375. }
  2376. }
  2377. var cart = viewRectangle2DCartographic;
  2378. cart.longitude = east;
  2379. cart.latitude = rectangle.north;
  2380. var northEast = projection.project(cart, viewRectangle2DNorthEast);
  2381. cart.longitude = rectangle.west;
  2382. cart.latitude = rectangle.south;
  2383. var southWest = projection.project(cart, viewRectangle2DSouthWest);
  2384. var width = Math.abs(northEast.x - southWest.x) * 0.5;
  2385. var height = Math.abs(northEast.y - southWest.y) * 0.5;
  2386. var right, top;
  2387. var ratio = camera.frustum.right / camera.frustum.top;
  2388. var heightRatio = height * ratio;
  2389. if (width > heightRatio) {
  2390. right = width;
  2391. top = right / ratio;
  2392. } else {
  2393. top = height;
  2394. right = heightRatio;
  2395. }
  2396. height = Math.max(2.0 * right, 2.0 * top);
  2397. result.x = (northEast.x - southWest.x) * 0.5 + southWest.x;
  2398. result.y = (northEast.y - southWest.y) * 0.5 + southWest.y;
  2399. cart = projection.unproject(result, cart);
  2400. cart.height = height;
  2401. result = projection.project(cart, result);
  2402. return result;
  2403. }
  2404. /**
  2405. * Get the camera position needed to view a rectangle on an ellipsoid or map
  2406. *
  2407. * @param {Rectangle} rectangle The rectangle to view.
  2408. * @param {Cartesian3} [result] The camera position needed to view the rectangle
  2409. * @returns {Cartesian3} The camera position needed to view the rectangle
  2410. */
  2411. Camera.prototype.getRectangleCameraCoordinates = function (rectangle, result) {
  2412. //>>includeStart('debug', pragmas.debug);
  2413. if (!defined(rectangle)) {
  2414. throw new DeveloperError("rectangle is required");
  2415. }
  2416. //>>includeEnd('debug');
  2417. var mode = this._mode;
  2418. if (!defined(result)) {
  2419. result = new Cartesian3();
  2420. }
  2421. if (mode === SceneMode.SCENE3D) {
  2422. return rectangleCameraPosition3D(this, rectangle, result);
  2423. } else if (mode === SceneMode.COLUMBUS_VIEW) {
  2424. return rectangleCameraPositionColumbusView(this, rectangle, result);
  2425. } else if (mode === SceneMode.SCENE2D) {
  2426. return rectangleCameraPosition2D(this, rectangle, result);
  2427. }
  2428. return undefined;
  2429. };
  2430. var pickEllipsoid3DRay = new Ray();
  2431. function pickEllipsoid3D(camera, windowPosition, ellipsoid, result) {
  2432. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  2433. var ray = camera.getPickRay(windowPosition, pickEllipsoid3DRay);
  2434. var intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid);
  2435. if (!intersection) {
  2436. return undefined;
  2437. }
  2438. var t = intersection.start > 0.0 ? intersection.start : intersection.stop;
  2439. return Ray.getPoint(ray, t, result);
  2440. }
  2441. var pickEllipsoid2DRay = new Ray();
  2442. function pickMap2D(camera, windowPosition, projection, result) {
  2443. var ray = camera.getPickRay(windowPosition, pickEllipsoid2DRay);
  2444. var position = ray.origin;
  2445. position = Cartesian3.fromElements(position.y, position.z, 0.0, position);
  2446. var cart = projection.unproject(position);
  2447. if (
  2448. cart.latitude < -CesiumMath.PI_OVER_TWO ||
  2449. cart.latitude > CesiumMath.PI_OVER_TWO
  2450. ) {
  2451. return undefined;
  2452. }
  2453. return projection.ellipsoid.cartographicToCartesian(cart, result);
  2454. }
  2455. var pickEllipsoidCVRay = new Ray();
  2456. function pickMapColumbusView(camera, windowPosition, projection, result) {
  2457. var ray = camera.getPickRay(windowPosition, pickEllipsoidCVRay);
  2458. var scalar = -ray.origin.x / ray.direction.x;
  2459. Ray.getPoint(ray, scalar, result);
  2460. var cart = projection.unproject(new Cartesian3(result.y, result.z, 0.0));
  2461. if (
  2462. cart.latitude < -CesiumMath.PI_OVER_TWO ||
  2463. cart.latitude > CesiumMath.PI_OVER_TWO ||
  2464. cart.longitude < -Math.PI ||
  2465. cart.longitude > Math.PI
  2466. ) {
  2467. return undefined;
  2468. }
  2469. return projection.ellipsoid.cartographicToCartesian(cart, result);
  2470. }
  2471. /**
  2472. * Pick an ellipsoid or map.
  2473. *
  2474. * @param {Cartesian2} windowPosition The x and y coordinates of a pixel.
  2475. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to pick.
  2476. * @param {Cartesian3} [result] The object onto which to store the result.
  2477. * @returns {Cartesian3 | undefined} If the ellipsoid or map was picked,
  2478. * returns the point on the surface of the ellipsoid or map in world
  2479. * coordinates. If the ellipsoid or map was not picked, returns undefined.
  2480. *
  2481. * @example
  2482. * var canvas = viewer.scene.canvas;
  2483. * var center = new Cesium.Cartesian2(canvas.clientWidth / 2.0, canvas.clientHeight / 2.0);
  2484. * var ellipsoid = viewer.scene.globe.ellipsoid;
  2485. * var result = viewer.camera.pickEllipsoid(center, ellipsoid);
  2486. */
  2487. Camera.prototype.pickEllipsoid = function (windowPosition, ellipsoid, result) {
  2488. //>>includeStart('debug', pragmas.debug);
  2489. if (!defined(windowPosition)) {
  2490. throw new DeveloperError("windowPosition is required.");
  2491. }
  2492. //>>includeEnd('debug');
  2493. var canvas = this._scene.canvas;
  2494. if (canvas.clientWidth === 0 || canvas.clientHeight === 0) {
  2495. return undefined;
  2496. }
  2497. if (!defined(result)) {
  2498. result = new Cartesian3();
  2499. }
  2500. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  2501. if (this._mode === SceneMode.SCENE3D) {
  2502. result = pickEllipsoid3D(this, windowPosition, ellipsoid, result);
  2503. } else if (this._mode === SceneMode.SCENE2D) {
  2504. result = pickMap2D(this, windowPosition, this._projection, result);
  2505. } else if (this._mode === SceneMode.COLUMBUS_VIEW) {
  2506. result = pickMapColumbusView(
  2507. this,
  2508. windowPosition,
  2509. this._projection,
  2510. result
  2511. );
  2512. } else {
  2513. return undefined;
  2514. }
  2515. return result;
  2516. };
  2517. var pickPerspCenter = new Cartesian3();
  2518. var pickPerspXDir = new Cartesian3();
  2519. var pickPerspYDir = new Cartesian3();
  2520. function getPickRayPerspective(camera, windowPosition, result) {
  2521. var canvas = camera._scene.canvas;
  2522. var width = canvas.clientWidth;
  2523. var height = canvas.clientHeight;
  2524. var tanPhi = Math.tan(camera.frustum.fovy * 0.5);
  2525. var tanTheta = camera.frustum.aspectRatio * tanPhi;
  2526. var near = camera.frustum.near;
  2527. var x = (2.0 / width) * windowPosition.x - 1.0;
  2528. var y = (2.0 / height) * (height - windowPosition.y) - 1.0;
  2529. var position = camera.positionWC;
  2530. Cartesian3.clone(position, result.origin);
  2531. var nearCenter = Cartesian3.multiplyByScalar(
  2532. camera.directionWC,
  2533. near,
  2534. pickPerspCenter
  2535. );
  2536. Cartesian3.add(position, nearCenter, nearCenter);
  2537. var xDir = Cartesian3.multiplyByScalar(
  2538. camera.rightWC,
  2539. x * near * tanTheta,
  2540. pickPerspXDir
  2541. );
  2542. var yDir = Cartesian3.multiplyByScalar(
  2543. camera.upWC,
  2544. y * near * tanPhi,
  2545. pickPerspYDir
  2546. );
  2547. var direction = Cartesian3.add(nearCenter, xDir, result.direction);
  2548. Cartesian3.add(direction, yDir, direction);
  2549. Cartesian3.subtract(direction, position, direction);
  2550. Cartesian3.normalize(direction, direction);
  2551. return result;
  2552. }
  2553. var scratchDirection = new Cartesian3();
  2554. function getPickRayOrthographic(camera, windowPosition, result) {
  2555. var canvas = camera._scene.canvas;
  2556. var width = canvas.clientWidth;
  2557. var height = canvas.clientHeight;
  2558. var frustum = camera.frustum;
  2559. if (defined(frustum._offCenterFrustum)) {
  2560. frustum = frustum._offCenterFrustum;
  2561. }
  2562. var x = (2.0 / width) * windowPosition.x - 1.0;
  2563. x *= (frustum.right - frustum.left) * 0.5;
  2564. var y = (2.0 / height) * (height - windowPosition.y) - 1.0;
  2565. y *= (frustum.top - frustum.bottom) * 0.5;
  2566. var origin = result.origin;
  2567. Cartesian3.clone(camera.position, origin);
  2568. Cartesian3.multiplyByScalar(camera.right, x, scratchDirection);
  2569. Cartesian3.add(scratchDirection, origin, origin);
  2570. Cartesian3.multiplyByScalar(camera.up, y, scratchDirection);
  2571. Cartesian3.add(scratchDirection, origin, origin);
  2572. Cartesian3.clone(camera.directionWC, result.direction);
  2573. if (
  2574. camera._mode === SceneMode.COLUMBUS_VIEW ||
  2575. camera._mode === SceneMode.SCENE2D
  2576. ) {
  2577. Cartesian3.fromElements(
  2578. result.origin.z,
  2579. result.origin.x,
  2580. result.origin.y,
  2581. result.origin
  2582. );
  2583. }
  2584. return result;
  2585. }
  2586. /**
  2587. * Create a ray from the camera position through the pixel at <code>windowPosition</code>
  2588. * in world coordinates.
  2589. *
  2590. * @param {Cartesian2} windowPosition The x and y coordinates of a pixel.
  2591. * @param {Ray} [result] The object onto which to store the result.
  2592. * @returns {Ray} Returns the {@link Cartesian3} position and direction of the ray.
  2593. */
  2594. Camera.prototype.getPickRay = function (windowPosition, result) {
  2595. //>>includeStart('debug', pragmas.debug);
  2596. if (!defined(windowPosition)) {
  2597. throw new DeveloperError("windowPosition is required.");
  2598. }
  2599. //>>includeEnd('debug');
  2600. if (!defined(result)) {
  2601. result = new Ray();
  2602. }
  2603. var frustum = this.frustum;
  2604. if (
  2605. defined(frustum.aspectRatio) &&
  2606. defined(frustum.fov) &&
  2607. defined(frustum.near)
  2608. ) {
  2609. return getPickRayPerspective(this, windowPosition, result);
  2610. }
  2611. return getPickRayOrthographic(this, windowPosition, result);
  2612. };
  2613. var scratchToCenter = new Cartesian3();
  2614. var scratchProj = new Cartesian3();
  2615. /**
  2616. * Return the distance from the camera to the front of the bounding sphere.
  2617. *
  2618. * @param {BoundingSphere} boundingSphere The bounding sphere in world coordinates.
  2619. * @returns {Number} The distance to the bounding sphere.
  2620. */
  2621. Camera.prototype.distanceToBoundingSphere = function (boundingSphere) {
  2622. //>>includeStart('debug', pragmas.debug);
  2623. if (!defined(boundingSphere)) {
  2624. throw new DeveloperError("boundingSphere is required.");
  2625. }
  2626. //>>includeEnd('debug');
  2627. var toCenter = Cartesian3.subtract(
  2628. this.positionWC,
  2629. boundingSphere.center,
  2630. scratchToCenter
  2631. );
  2632. var proj = Cartesian3.multiplyByScalar(
  2633. this.directionWC,
  2634. Cartesian3.dot(toCenter, this.directionWC),
  2635. scratchProj
  2636. );
  2637. return Math.max(0.0, Cartesian3.magnitude(proj) - boundingSphere.radius);
  2638. };
  2639. var scratchPixelSize = new Cartesian2();
  2640. /**
  2641. * Return the pixel size in meters.
  2642. *
  2643. * @param {BoundingSphere} boundingSphere The bounding sphere in world coordinates.
  2644. * @param {Number} drawingBufferWidth The drawing buffer width.
  2645. * @param {Number} drawingBufferHeight The drawing buffer height.
  2646. * @returns {Number} The pixel size in meters.
  2647. */
  2648. Camera.prototype.getPixelSize = function (
  2649. boundingSphere,
  2650. drawingBufferWidth,
  2651. drawingBufferHeight
  2652. ) {
  2653. //>>includeStart('debug', pragmas.debug);
  2654. if (!defined(boundingSphere)) {
  2655. throw new DeveloperError("boundingSphere is required.");
  2656. }
  2657. if (!defined(drawingBufferWidth)) {
  2658. throw new DeveloperError("drawingBufferWidth is required.");
  2659. }
  2660. if (!defined(drawingBufferHeight)) {
  2661. throw new DeveloperError("drawingBufferHeight is required.");
  2662. }
  2663. //>>includeEnd('debug');
  2664. var distance = this.distanceToBoundingSphere(boundingSphere);
  2665. var pixelSize = this.frustum.getPixelDimensions(
  2666. drawingBufferWidth,
  2667. drawingBufferHeight,
  2668. distance,
  2669. this._scene.pixelRatio,
  2670. scratchPixelSize
  2671. );
  2672. return Math.max(pixelSize.x, pixelSize.y);
  2673. };
  2674. function createAnimationTemplateCV(
  2675. camera,
  2676. position,
  2677. center,
  2678. maxX,
  2679. maxY,
  2680. duration
  2681. ) {
  2682. var newPosition = Cartesian3.clone(position);
  2683. if (center.y > maxX) {
  2684. newPosition.y -= center.y - maxX;
  2685. } else if (center.y < -maxX) {
  2686. newPosition.y += -maxX - center.y;
  2687. }
  2688. if (center.z > maxY) {
  2689. newPosition.z -= center.z - maxY;
  2690. } else if (center.z < -maxY) {
  2691. newPosition.z += -maxY - center.z;
  2692. }
  2693. function updateCV(value) {
  2694. var interp = Cartesian3.lerp(
  2695. position,
  2696. newPosition,
  2697. value.time,
  2698. new Cartesian3()
  2699. );
  2700. camera.worldToCameraCoordinatesPoint(interp, camera.position);
  2701. }
  2702. return {
  2703. easingFunction: EasingFunction.EXPONENTIAL_OUT,
  2704. startObject: {
  2705. time: 0.0,
  2706. },
  2707. stopObject: {
  2708. time: 1.0,
  2709. },
  2710. duration: duration,
  2711. update: updateCV,
  2712. };
  2713. }
  2714. var normalScratch = new Cartesian3();
  2715. var centerScratch = new Cartesian3();
  2716. var posScratch = new Cartesian3();
  2717. var scratchCartesian3Subtract = new Cartesian3();
  2718. function createAnimationCV(camera, duration) {
  2719. var position = camera.position;
  2720. var direction = camera.direction;
  2721. var normal = camera.worldToCameraCoordinatesVector(
  2722. Cartesian3.UNIT_X,
  2723. normalScratch
  2724. );
  2725. var scalar =
  2726. -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction);
  2727. var center = Cartesian3.add(
  2728. position,
  2729. Cartesian3.multiplyByScalar(direction, scalar, centerScratch),
  2730. centerScratch
  2731. );
  2732. camera.cameraToWorldCoordinatesPoint(center, center);
  2733. position = camera.cameraToWorldCoordinatesPoint(camera.position, posScratch);
  2734. var tanPhi = Math.tan(camera.frustum.fovy * 0.5);
  2735. var tanTheta = camera.frustum.aspectRatio * tanPhi;
  2736. var distToC = Cartesian3.magnitude(
  2737. Cartesian3.subtract(position, center, scratchCartesian3Subtract)
  2738. );
  2739. var dWidth = tanTheta * distToC;
  2740. var dHeight = tanPhi * distToC;
  2741. var mapWidth = camera._maxCoord.x;
  2742. var mapHeight = camera._maxCoord.y;
  2743. var maxX = Math.max(dWidth - mapWidth, mapWidth);
  2744. var maxY = Math.max(dHeight - mapHeight, mapHeight);
  2745. if (
  2746. position.z < -maxX ||
  2747. position.z > maxX ||
  2748. position.y < -maxY ||
  2749. position.y > maxY
  2750. ) {
  2751. var translateX = center.y < -maxX || center.y > maxX;
  2752. var translateY = center.z < -maxY || center.z > maxY;
  2753. if (translateX || translateY) {
  2754. return createAnimationTemplateCV(
  2755. camera,
  2756. position,
  2757. center,
  2758. maxX,
  2759. maxY,
  2760. duration
  2761. );
  2762. }
  2763. }
  2764. return undefined;
  2765. }
  2766. /**
  2767. * Create an animation to move the map into view. This method is only valid for 2D and Columbus modes.
  2768. *
  2769. * @param {Number} duration The duration, in seconds, of the animation.
  2770. * @returns {Object} The animation or undefined if the scene mode is 3D or the map is already ion view.
  2771. *
  2772. * @private
  2773. */
  2774. Camera.prototype.createCorrectPositionTween = function (duration) {
  2775. //>>includeStart('debug', pragmas.debug);
  2776. if (!defined(duration)) {
  2777. throw new DeveloperError("duration is required.");
  2778. }
  2779. //>>includeEnd('debug');
  2780. if (this._mode === SceneMode.COLUMBUS_VIEW) {
  2781. return createAnimationCV(this, duration);
  2782. }
  2783. return undefined;
  2784. };
  2785. var scratchFlyToDestination = new Cartesian3();
  2786. var newOptions = {
  2787. destination: undefined,
  2788. heading: undefined,
  2789. pitch: undefined,
  2790. roll: undefined,
  2791. duration: undefined,
  2792. complete: undefined,
  2793. cancel: undefined,
  2794. endTransform: undefined,
  2795. maximumHeight: undefined,
  2796. easingFunction: undefined,
  2797. };
  2798. /**
  2799. * Cancels the current camera flight and leaves the camera at its current location.
  2800. * If no flight is in progress, this this function does nothing.
  2801. */
  2802. Camera.prototype.cancelFlight = function () {
  2803. if (defined(this._currentFlight)) {
  2804. this._currentFlight.cancelTween();
  2805. this._currentFlight = undefined;
  2806. }
  2807. };
  2808. /**
  2809. * Completes the current camera flight and moves the camera immediately to its final destination.
  2810. * If no flight is in progress, this this function does nothing.
  2811. */
  2812. Camera.prototype.completeFlight = function () {
  2813. if (defined(this._currentFlight)) {
  2814. this._currentFlight.cancelTween();
  2815. var options = {
  2816. destination: undefined,
  2817. orientation: {
  2818. heading: undefined,
  2819. pitch: undefined,
  2820. roll: undefined,
  2821. },
  2822. };
  2823. options.destination = newOptions.destination;
  2824. options.orientation.heading = newOptions.heading;
  2825. options.orientation.pitch = newOptions.pitch;
  2826. options.orientation.roll = newOptions.roll;
  2827. this.setView(options);
  2828. if (defined(this._currentFlight.complete)) {
  2829. this._currentFlight.complete();
  2830. }
  2831. this._currentFlight = undefined;
  2832. }
  2833. };
  2834. /**
  2835. * Flies the camera from its current position to a new position.
  2836. *
  2837. * @param {Object} options Object with the following properties:
  2838. * @param {Cartesian3|Rectangle} options.destination The final position of the camera in WGS84 (world) coordinates or a rectangle that would be visible from a top-down view.
  2839. * @param {Object} [options.orientation] An object that contains either direction and up properties or heading, pitch and roll properties. By default, the direction will point
  2840. * towards the center of the frame in 3D and in the negative z direction in Columbus view. The up direction will point towards local north in 3D and in the positive
  2841. * y direction in Columbus view. Orientation is not used in 2D when in infinite scrolling mode.
  2842. * @param {Number} [options.duration] The duration of the flight in seconds. If omitted, Cesium attempts to calculate an ideal duration based on the distance to be traveled by the flight.
  2843. * @param {Camera.FlightCompleteCallback} [options.complete] The function to execute when the flight is complete.
  2844. * @param {Camera.FlightCancelledCallback} [options.cancel] The function to execute if the flight is cancelled.
  2845. * @param {Matrix4} [options.endTransform] Transform matrix representing the reference frame the camera will be in when the flight is completed.
  2846. * @param {Number} [options.maximumHeight] The maximum height at the peak of the flight.
  2847. * @param {Number} [options.pitchAdjustHeight] If camera flyes higher than that value, adjust pitch duiring the flight to look down, and keep Earth in viewport.
  2848. * @param {Number} [options.flyOverLongitude] There are always two ways between 2 points on globe. This option force camera to choose fight direction to fly over that longitude.
  2849. * @param {Number} [options.flyOverLongitudeWeight] Fly over the lon specifyed via flyOverLongitude only if that way is not longer than short way times flyOverLongitudeWeight.
  2850. * @param {Boolean} [options.convert] Whether to convert the destination from world coordinates to scene coordinates (only relevant when not using 3D). Defaults to <code>true</code>.
  2851. * @param {EasingFunction.Callback} [options.easingFunction] Controls how the time is interpolated over the duration of the flight.
  2852. *
  2853. * @exception {DeveloperError} If either direction or up is given, then both are required.
  2854. *
  2855. * @example
  2856. * // 1. Fly to a position with a top-down view
  2857. * viewer.camera.flyTo({
  2858. * destination : Cesium.Cartesian3.fromDegrees(-117.16, 32.71, 15000.0)
  2859. * });
  2860. *
  2861. * // 2. Fly to a Rectangle with a top-down view
  2862. * viewer.camera.flyTo({
  2863. * destination : Cesium.Rectangle.fromDegrees(west, south, east, north)
  2864. * });
  2865. *
  2866. * // 3. Fly to a position with an orientation using unit vectors.
  2867. * viewer.camera.flyTo({
  2868. * destination : Cesium.Cartesian3.fromDegrees(-122.19, 46.25, 5000.0),
  2869. * orientation : {
  2870. * direction : new Cesium.Cartesian3(-0.04231243104240401, -0.20123236049443421, -0.97862924300734),
  2871. * up : new Cesium.Cartesian3(-0.47934589305293746, -0.8553216253114552, 0.1966022179118339)
  2872. * }
  2873. * });
  2874. *
  2875. * // 4. Fly to a position with an orientation using heading, pitch and roll.
  2876. * viewer.camera.flyTo({
  2877. * destination : Cesium.Cartesian3.fromDegrees(-122.19, 46.25, 5000.0),
  2878. * orientation : {
  2879. * heading : Cesium.Math.toRadians(175.0),
  2880. * pitch : Cesium.Math.toRadians(-35.0),
  2881. * roll : 0.0
  2882. * }
  2883. * });
  2884. */
  2885. Camera.prototype.flyTo = function (options) {
  2886. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  2887. var destination = options.destination;
  2888. //>>includeStart('debug', pragmas.debug);
  2889. if (!defined(destination)) {
  2890. throw new DeveloperError("destination is required.");
  2891. }
  2892. //>>includeEnd('debug');
  2893. var mode = this._mode;
  2894. if (mode === SceneMode.MORPHING) {
  2895. return;
  2896. }
  2897. this.cancelFlight();
  2898. var orientation = defaultValue(
  2899. options.orientation,
  2900. defaultValue.EMPTY_OBJECT
  2901. );
  2902. if (defined(orientation.direction)) {
  2903. orientation = directionUpToHeadingPitchRoll(
  2904. this,
  2905. destination,
  2906. orientation,
  2907. scratchSetViewOptions.orientation
  2908. );
  2909. }
  2910. if (defined(options.duration) && options.duration <= 0.0) {
  2911. var setViewOptions = scratchSetViewOptions;
  2912. setViewOptions.destination = options.destination;
  2913. setViewOptions.orientation.heading = orientation.heading;
  2914. setViewOptions.orientation.pitch = orientation.pitch;
  2915. setViewOptions.orientation.roll = orientation.roll;
  2916. setViewOptions.convert = options.convert;
  2917. setViewOptions.endTransform = options.endTransform;
  2918. this.setView(setViewOptions);
  2919. if (typeof options.complete === "function") {
  2920. options.complete();
  2921. }
  2922. return;
  2923. }
  2924. var isRectangle = defined(destination.west);
  2925. if (isRectangle) {
  2926. destination = this.getRectangleCameraCoordinates(
  2927. destination,
  2928. scratchFlyToDestination
  2929. );
  2930. }
  2931. var that = this;
  2932. var flightTween;
  2933. newOptions.destination = destination;
  2934. newOptions.heading = orientation.heading;
  2935. newOptions.pitch = orientation.pitch;
  2936. newOptions.roll = orientation.roll;
  2937. newOptions.duration = options.duration;
  2938. newOptions.complete = function () {
  2939. if (flightTween === that._currentFlight) {
  2940. that._currentFlight = undefined;
  2941. }
  2942. if (defined(options.complete)) {
  2943. options.complete();
  2944. }
  2945. };
  2946. newOptions.cancel = options.cancel;
  2947. newOptions.endTransform = options.endTransform;
  2948. newOptions.convert = isRectangle ? false : options.convert;
  2949. newOptions.maximumHeight = options.maximumHeight;
  2950. newOptions.pitchAdjustHeight = options.pitchAdjustHeight;
  2951. newOptions.flyOverLongitude = options.flyOverLongitude;
  2952. newOptions.flyOverLongitudeWeight = options.flyOverLongitudeWeight;
  2953. newOptions.easingFunction = options.easingFunction;
  2954. var scene = this._scene;
  2955. var tweenOptions = CameraFlightPath.createTween(scene, newOptions);
  2956. // If the camera doesn't actually need to go anywhere, duration
  2957. // will be 0 and we can just complete the current flight.
  2958. if (tweenOptions.duration === 0) {
  2959. if (typeof tweenOptions.complete === "function") {
  2960. tweenOptions.complete();
  2961. }
  2962. return;
  2963. }
  2964. flightTween = scene.tweens.add(tweenOptions);
  2965. this._currentFlight = flightTween;
  2966. // Save the final destination view information for the PRELOAD_FLIGHT pass.
  2967. var preloadFlightCamera = this._scene.preloadFlightCamera;
  2968. if (this._mode !== SceneMode.SCENE2D) {
  2969. if (!defined(preloadFlightCamera)) {
  2970. preloadFlightCamera = Camera.clone(this);
  2971. }
  2972. preloadFlightCamera.setView({
  2973. destination: destination,
  2974. orientation: orientation,
  2975. });
  2976. this._scene.preloadFlightCullingVolume = preloadFlightCamera.frustum.computeCullingVolume(
  2977. preloadFlightCamera.positionWC,
  2978. preloadFlightCamera.directionWC,
  2979. preloadFlightCamera.upWC
  2980. );
  2981. }
  2982. };
  2983. function distanceToBoundingSphere3D(camera, radius) {
  2984. var frustum = camera.frustum;
  2985. var tanPhi = Math.tan(frustum.fovy * 0.5);
  2986. var tanTheta = frustum.aspectRatio * tanPhi;
  2987. return Math.max(radius / tanTheta, radius / tanPhi);
  2988. }
  2989. function distanceToBoundingSphere2D(camera, radius) {
  2990. var frustum = camera.frustum;
  2991. if (defined(frustum._offCenterFrustum)) {
  2992. frustum = frustum._offCenterFrustum;
  2993. }
  2994. var right, top;
  2995. var ratio = frustum.right / frustum.top;
  2996. var heightRatio = radius * ratio;
  2997. if (radius > heightRatio) {
  2998. right = radius;
  2999. top = right / ratio;
  3000. } else {
  3001. top = radius;
  3002. right = heightRatio;
  3003. }
  3004. return Math.max(right, top) * 1.5;
  3005. }
  3006. var MINIMUM_ZOOM = 100.0;
  3007. function adjustBoundingSphereOffset(camera, boundingSphere, offset) {
  3008. offset = HeadingPitchRange.clone(
  3009. defined(offset) ? offset : Camera.DEFAULT_OFFSET
  3010. );
  3011. var minimumZoom =
  3012. camera._scene.screenSpaceCameraController.minimumZoomDistance;
  3013. var maximumZoom =
  3014. camera._scene.screenSpaceCameraController.maximumZoomDistance;
  3015. var range = offset.range;
  3016. if (!defined(range) || range === 0.0) {
  3017. var radius = boundingSphere.radius;
  3018. if (radius === 0.0) {
  3019. offset.range = MINIMUM_ZOOM;
  3020. } else if (
  3021. camera.frustum instanceof OrthographicFrustum ||
  3022. camera._mode === SceneMode.SCENE2D
  3023. ) {
  3024. offset.range = distanceToBoundingSphere2D(camera, radius);
  3025. } else {
  3026. offset.range = distanceToBoundingSphere3D(camera, radius);
  3027. }
  3028. offset.range = CesiumMath.clamp(offset.range, minimumZoom, maximumZoom);
  3029. }
  3030. return offset;
  3031. }
  3032. /**
  3033. * Sets the camera so that the current view contains the provided bounding sphere.
  3034. *
  3035. * <p>The offset is heading/pitch/range in the local east-north-up reference frame centered at the center of the bounding sphere.
  3036. * The heading and the pitch angles are defined in the local east-north-up reference frame.
  3037. * The heading is the angle from y axis and increasing towards the x axis. Pitch is the rotation from the xy-plane. Positive pitch
  3038. * angles are below the plane. Negative pitch angles are above the plane. The range is the distance from the center. If the range is
  3039. * zero, a range will be computed such that the whole bounding sphere is visible.</p>
  3040. *
  3041. * <p>In 2D, there must be a top down view. The camera will be placed above the target looking down. The height above the
  3042. * target will be the range. The heading will be determined from the offset. If the heading cannot be
  3043. * determined from the offset, the heading will be north.</p>
  3044. *
  3045. * @param {BoundingSphere} boundingSphere The bounding sphere to view, in world coordinates.
  3046. * @param {HeadingPitchRange} [offset] The offset from the target in the local east-north-up reference frame centered at the target.
  3047. *
  3048. * @exception {DeveloperError} viewBoundingSphere is not supported while morphing.
  3049. */
  3050. Camera.prototype.viewBoundingSphere = function (boundingSphere, offset) {
  3051. //>>includeStart('debug', pragmas.debug);
  3052. if (!defined(boundingSphere)) {
  3053. throw new DeveloperError("boundingSphere is required.");
  3054. }
  3055. if (this._mode === SceneMode.MORPHING) {
  3056. throw new DeveloperError(
  3057. "viewBoundingSphere is not supported while morphing."
  3058. );
  3059. }
  3060. //>>includeEnd('debug');
  3061. offset = adjustBoundingSphereOffset(this, boundingSphere, offset);
  3062. this.lookAt(boundingSphere.center, offset);
  3063. };
  3064. var scratchflyToBoundingSphereTransform = new Matrix4();
  3065. var scratchflyToBoundingSphereDestination = new Cartesian3();
  3066. var scratchflyToBoundingSphereDirection = new Cartesian3();
  3067. var scratchflyToBoundingSphereUp = new Cartesian3();
  3068. var scratchflyToBoundingSphereRight = new Cartesian3();
  3069. var scratchFlyToBoundingSphereCart4 = new Cartesian4();
  3070. var scratchFlyToBoundingSphereQuaternion = new Quaternion();
  3071. var scratchFlyToBoundingSphereMatrix3 = new Matrix3();
  3072. /**
  3073. * Flies the camera to a location where the current view contains the provided bounding sphere.
  3074. *
  3075. * <p> The offset is heading/pitch/range in the local east-north-up reference frame centered at the center of the bounding sphere.
  3076. * The heading and the pitch angles are defined in the local east-north-up reference frame.
  3077. * The heading is the angle from y axis and increasing towards the x axis. Pitch is the rotation from the xy-plane. Positive pitch
  3078. * angles are below the plane. Negative pitch angles are above the plane. The range is the distance from the center. If the range is
  3079. * zero, a range will be computed such that the whole bounding sphere is visible.</p>
  3080. *
  3081. * <p>In 2D and Columbus View, there must be a top down view. The camera will be placed above the target looking down. The height above the
  3082. * target will be the range. The heading will be aligned to local north.</p>
  3083. *
  3084. * @param {BoundingSphere} boundingSphere The bounding sphere to view, in world coordinates.
  3085. * @param {Object} [options] Object with the following properties:
  3086. * @param {Number} [options.duration] The duration of the flight in seconds. If omitted, Cesium attempts to calculate an ideal duration based on the distance to be traveled by the flight.
  3087. * @param {HeadingPitchRange} [options.offset] The offset from the target in the local east-north-up reference frame centered at the target.
  3088. * @param {Camera.FlightCompleteCallback} [options.complete] The function to execute when the flight is complete.
  3089. * @param {Camera.FlightCancelledCallback} [options.cancel] The function to execute if the flight is cancelled.
  3090. * @param {Matrix4} [options.endTransform] Transform matrix representing the reference frame the camera will be in when the flight is completed.
  3091. * @param {Number} [options.maximumHeight] The maximum height at the peak of the flight.
  3092. * @param {Number} [options.pitchAdjustHeight] If camera flyes higher than that value, adjust pitch duiring the flight to look down, and keep Earth in viewport.
  3093. * @param {Number} [options.flyOverLongitude] There are always two ways between 2 points on globe. This option force camera to choose fight direction to fly over that longitude.
  3094. * @param {Number} [options.flyOverLongitudeWeight] Fly over the lon specifyed via flyOverLongitude only if that way is not longer than short way times flyOverLongitudeWeight.
  3095. * @param {EasingFunction.Callback} [options.easingFunction] Controls how the time is interpolated over the duration of the flight.
  3096. */
  3097. Camera.prototype.flyToBoundingSphere = function (boundingSphere, options) {
  3098. //>>includeStart('debug', pragmas.debug);
  3099. if (!defined(boundingSphere)) {
  3100. throw new DeveloperError("boundingSphere is required.");
  3101. }
  3102. //>>includeEnd('debug');
  3103. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  3104. var scene2D =
  3105. this._mode === SceneMode.SCENE2D || this._mode === SceneMode.COLUMBUS_VIEW;
  3106. this._setTransform(Matrix4.IDENTITY);
  3107. var offset = adjustBoundingSphereOffset(this, boundingSphere, options.offset);
  3108. var position;
  3109. if (scene2D) {
  3110. position = Cartesian3.multiplyByScalar(
  3111. Cartesian3.UNIT_Z,
  3112. offset.range,
  3113. scratchflyToBoundingSphereDestination
  3114. );
  3115. } else {
  3116. position = offsetFromHeadingPitchRange(
  3117. offset.heading,
  3118. offset.pitch,
  3119. offset.range
  3120. );
  3121. }
  3122. var transform = Transforms.eastNorthUpToFixedFrame(
  3123. boundingSphere.center,
  3124. Ellipsoid.WGS84,
  3125. scratchflyToBoundingSphereTransform
  3126. );
  3127. Matrix4.multiplyByPoint(transform, position, position);
  3128. var direction;
  3129. var up;
  3130. if (!scene2D) {
  3131. direction = Cartesian3.subtract(
  3132. boundingSphere.center,
  3133. position,
  3134. scratchflyToBoundingSphereDirection
  3135. );
  3136. Cartesian3.normalize(direction, direction);
  3137. up = Matrix4.multiplyByPointAsVector(
  3138. transform,
  3139. Cartesian3.UNIT_Z,
  3140. scratchflyToBoundingSphereUp
  3141. );
  3142. if (1.0 - Math.abs(Cartesian3.dot(direction, up)) < CesiumMath.EPSILON6) {
  3143. var rotateQuat = Quaternion.fromAxisAngle(
  3144. direction,
  3145. offset.heading,
  3146. scratchFlyToBoundingSphereQuaternion
  3147. );
  3148. var rotation = Matrix3.fromQuaternion(
  3149. rotateQuat,
  3150. scratchFlyToBoundingSphereMatrix3
  3151. );
  3152. Cartesian3.fromCartesian4(
  3153. Matrix4.getColumn(transform, 1, scratchFlyToBoundingSphereCart4),
  3154. up
  3155. );
  3156. Matrix3.multiplyByVector(rotation, up, up);
  3157. }
  3158. var right = Cartesian3.cross(
  3159. direction,
  3160. up,
  3161. scratchflyToBoundingSphereRight
  3162. );
  3163. Cartesian3.cross(right, direction, up);
  3164. Cartesian3.normalize(up, up);
  3165. }
  3166. this.flyTo({
  3167. destination: position,
  3168. orientation: {
  3169. direction: direction,
  3170. up: up,
  3171. },
  3172. duration: options.duration,
  3173. complete: options.complete,
  3174. cancel: options.cancel,
  3175. endTransform: options.endTransform,
  3176. maximumHeight: options.maximumHeight,
  3177. easingFunction: options.easingFunction,
  3178. flyOverLongitude: options.flyOverLongitude,
  3179. flyOverLongitudeWeight: options.flyOverLongitudeWeight,
  3180. pitchAdjustHeight: options.pitchAdjustHeight,
  3181. });
  3182. };
  3183. var scratchCartesian3_1 = new Cartesian3();
  3184. var scratchCartesian3_2 = new Cartesian3();
  3185. var scratchCartesian3_3 = new Cartesian3();
  3186. var scratchCartesian3_4 = new Cartesian3();
  3187. var horizonPoints = [
  3188. new Cartesian3(),
  3189. new Cartesian3(),
  3190. new Cartesian3(),
  3191. new Cartesian3(),
  3192. ];
  3193. function computeHorizonQuad(camera, ellipsoid) {
  3194. var radii = ellipsoid.radii;
  3195. var p = camera.positionWC;
  3196. // Find the corresponding position in the scaled space of the ellipsoid.
  3197. var q = Cartesian3.multiplyComponents(
  3198. ellipsoid.oneOverRadii,
  3199. p,
  3200. scratchCartesian3_1
  3201. );
  3202. var qMagnitude = Cartesian3.magnitude(q);
  3203. var qUnit = Cartesian3.normalize(q, scratchCartesian3_2);
  3204. // Determine the east and north directions at q.
  3205. var eUnit;
  3206. var nUnit;
  3207. if (
  3208. Cartesian3.equalsEpsilon(qUnit, Cartesian3.UNIT_Z, CesiumMath.EPSILON10)
  3209. ) {
  3210. eUnit = new Cartesian3(0, 1, 0);
  3211. nUnit = new Cartesian3(0, 0, 1);
  3212. } else {
  3213. eUnit = Cartesian3.normalize(
  3214. Cartesian3.cross(Cartesian3.UNIT_Z, qUnit, scratchCartesian3_3),
  3215. scratchCartesian3_3
  3216. );
  3217. nUnit = Cartesian3.normalize(
  3218. Cartesian3.cross(qUnit, eUnit, scratchCartesian3_4),
  3219. scratchCartesian3_4
  3220. );
  3221. }
  3222. // Determine the radius of the 'limb' of the ellipsoid.
  3223. var wMagnitude = Math.sqrt(Cartesian3.magnitudeSquared(q) - 1.0);
  3224. // Compute the center and offsets.
  3225. var center = Cartesian3.multiplyByScalar(
  3226. qUnit,
  3227. 1.0 / qMagnitude,
  3228. scratchCartesian3_1
  3229. );
  3230. var scalar = wMagnitude / qMagnitude;
  3231. var eastOffset = Cartesian3.multiplyByScalar(
  3232. eUnit,
  3233. scalar,
  3234. scratchCartesian3_2
  3235. );
  3236. var northOffset = Cartesian3.multiplyByScalar(
  3237. nUnit,
  3238. scalar,
  3239. scratchCartesian3_3
  3240. );
  3241. // A conservative measure for the longitudes would be to use the min/max longitudes of the bounding frustum.
  3242. var upperLeft = Cartesian3.add(center, northOffset, horizonPoints[0]);
  3243. Cartesian3.subtract(upperLeft, eastOffset, upperLeft);
  3244. Cartesian3.multiplyComponents(radii, upperLeft, upperLeft);
  3245. var lowerLeft = Cartesian3.subtract(center, northOffset, horizonPoints[1]);
  3246. Cartesian3.subtract(lowerLeft, eastOffset, lowerLeft);
  3247. Cartesian3.multiplyComponents(radii, lowerLeft, lowerLeft);
  3248. var lowerRight = Cartesian3.subtract(center, northOffset, horizonPoints[2]);
  3249. Cartesian3.add(lowerRight, eastOffset, lowerRight);
  3250. Cartesian3.multiplyComponents(radii, lowerRight, lowerRight);
  3251. var upperRight = Cartesian3.add(center, northOffset, horizonPoints[3]);
  3252. Cartesian3.add(upperRight, eastOffset, upperRight);
  3253. Cartesian3.multiplyComponents(radii, upperRight, upperRight);
  3254. return horizonPoints;
  3255. }
  3256. var scratchPickCartesian2 = new Cartesian2();
  3257. var scratchRectCartesian = new Cartesian3();
  3258. var cartoArray = [
  3259. new Cartographic(),
  3260. new Cartographic(),
  3261. new Cartographic(),
  3262. new Cartographic(),
  3263. ];
  3264. function addToResult(x, y, index, camera, ellipsoid, computedHorizonQuad) {
  3265. scratchPickCartesian2.x = x;
  3266. scratchPickCartesian2.y = y;
  3267. var r = camera.pickEllipsoid(
  3268. scratchPickCartesian2,
  3269. ellipsoid,
  3270. scratchRectCartesian
  3271. );
  3272. if (defined(r)) {
  3273. cartoArray[index] = ellipsoid.cartesianToCartographic(r, cartoArray[index]);
  3274. return 1;
  3275. }
  3276. cartoArray[index] = ellipsoid.cartesianToCartographic(
  3277. computedHorizonQuad[index],
  3278. cartoArray[index]
  3279. );
  3280. return 0;
  3281. }
  3282. /**
  3283. * Computes the approximate visible rectangle on the ellipsoid.
  3284. *
  3285. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid that you want to know the visible region.
  3286. * @param {Rectangle} [result] The rectangle in which to store the result
  3287. *
  3288. * @returns {Rectangle|undefined} The visible rectangle or undefined if the ellipsoid isn't visible at all.
  3289. */
  3290. Camera.prototype.computeViewRectangle = function (ellipsoid, result) {
  3291. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  3292. var cullingVolume = this.frustum.computeCullingVolume(
  3293. this.positionWC,
  3294. this.directionWC,
  3295. this.upWC
  3296. );
  3297. var boundingSphere = new BoundingSphere(
  3298. Cartesian3.ZERO,
  3299. ellipsoid.maximumRadius
  3300. );
  3301. var visibility = cullingVolume.computeVisibility(boundingSphere);
  3302. if (visibility === Intersect.OUTSIDE) {
  3303. return undefined;
  3304. }
  3305. var canvas = this._scene.canvas;
  3306. var width = canvas.clientWidth;
  3307. var height = canvas.clientHeight;
  3308. var successfulPickCount = 0;
  3309. var computedHorizonQuad = computeHorizonQuad(this, ellipsoid);
  3310. successfulPickCount += addToResult(
  3311. 0,
  3312. 0,
  3313. 0,
  3314. this,
  3315. ellipsoid,
  3316. computedHorizonQuad
  3317. );
  3318. successfulPickCount += addToResult(
  3319. 0,
  3320. height,
  3321. 1,
  3322. this,
  3323. ellipsoid,
  3324. computedHorizonQuad
  3325. );
  3326. successfulPickCount += addToResult(
  3327. width,
  3328. height,
  3329. 2,
  3330. this,
  3331. ellipsoid,
  3332. computedHorizonQuad
  3333. );
  3334. successfulPickCount += addToResult(
  3335. width,
  3336. 0,
  3337. 3,
  3338. this,
  3339. ellipsoid,
  3340. computedHorizonQuad
  3341. );
  3342. if (successfulPickCount < 2) {
  3343. // If we have space non-globe in 3 or 4 corners then return the whole globe
  3344. return Rectangle.MAX_VALUE;
  3345. }
  3346. result = Rectangle.fromCartographicArray(cartoArray, result);
  3347. // Detect if we go over the poles
  3348. var distance = 0;
  3349. var lastLon = cartoArray[3].longitude;
  3350. for (var i = 0; i < 4; ++i) {
  3351. var lon = cartoArray[i].longitude;
  3352. var diff = Math.abs(lon - lastLon);
  3353. if (diff > CesiumMath.PI) {
  3354. // Crossed the dateline
  3355. distance += CesiumMath.TWO_PI - diff;
  3356. } else {
  3357. distance += diff;
  3358. }
  3359. lastLon = lon;
  3360. }
  3361. // We are over one of the poles so adjust the rectangle accordingly
  3362. if (
  3363. CesiumMath.equalsEpsilon(
  3364. Math.abs(distance),
  3365. CesiumMath.TWO_PI,
  3366. CesiumMath.EPSILON9
  3367. )
  3368. ) {
  3369. result.west = -CesiumMath.PI;
  3370. result.east = CesiumMath.PI;
  3371. if (cartoArray[0].latitude >= 0.0) {
  3372. result.north = CesiumMath.PI_OVER_TWO;
  3373. } else {
  3374. result.south = -CesiumMath.PI_OVER_TWO;
  3375. }
  3376. }
  3377. return result;
  3378. };
  3379. /**
  3380. * Switches the frustum/projection to perspective.
  3381. *
  3382. * This function is a no-op in 2D which must always be orthographic.
  3383. */
  3384. Camera.prototype.switchToPerspectiveFrustum = function () {
  3385. if (
  3386. this._mode === SceneMode.SCENE2D ||
  3387. this.frustum instanceof PerspectiveFrustum
  3388. ) {
  3389. return;
  3390. }
  3391. var scene = this._scene;
  3392. this.frustum = new PerspectiveFrustum();
  3393. this.frustum.aspectRatio =
  3394. scene.drawingBufferWidth / scene.drawingBufferHeight;
  3395. this.frustum.fov = CesiumMath.toRadians(60.0);
  3396. };
  3397. /**
  3398. * Switches the frustum/projection to orthographic.
  3399. *
  3400. * This function is a no-op in 2D which will always be orthographic.
  3401. */
  3402. Camera.prototype.switchToOrthographicFrustum = function () {
  3403. if (
  3404. this._mode === SceneMode.SCENE2D ||
  3405. this.frustum instanceof OrthographicFrustum
  3406. ) {
  3407. return;
  3408. }
  3409. // This must be called before changing the frustum because it uses the previous
  3410. // frustum to reconstruct the world space position from the depth buffer.
  3411. var frustumWidth = calculateOrthographicFrustumWidth(this);
  3412. var scene = this._scene;
  3413. this.frustum = new OrthographicFrustum();
  3414. this.frustum.aspectRatio =
  3415. scene.drawingBufferWidth / scene.drawingBufferHeight;
  3416. this.frustum.width = frustumWidth;
  3417. };
  3418. /**
  3419. * @private
  3420. */
  3421. Camera.clone = function (camera, result) {
  3422. if (!defined(result)) {
  3423. result = new Camera(camera._scene);
  3424. }
  3425. Cartesian3.clone(camera.position, result.position);
  3426. Cartesian3.clone(camera.direction, result.direction);
  3427. Cartesian3.clone(camera.up, result.up);
  3428. Cartesian3.clone(camera.right, result.right);
  3429. Matrix4.clone(camera._transform, result.transform);
  3430. result._transformChanged = true;
  3431. result.frustum = camera.frustum.clone();
  3432. return result;
  3433. };
  3434. /**
  3435. * A function that will execute when a flight completes.
  3436. * @callback Camera.FlightCompleteCallback
  3437. */
  3438. /**
  3439. * A function that will execute when a flight is cancelled.
  3440. * @callback Camera.FlightCancelledCallback
  3441. */
  3442. export default Camera;