| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951 | import BoundingSphere from "../Core/BoundingSphere.js";import Cartesian2 from "../Core/Cartesian2.js";import Cartesian3 from "../Core/Cartesian3.js";import Cartesian4 from "../Core/Cartesian4.js";import Cartographic from "../Core/Cartographic.js";import defaultValue from "../Core/defaultValue.js";import defined from "../Core/defined.js";import DeveloperError from "../Core/DeveloperError.js";import EasingFunction from "../Core/EasingFunction.js";import Ellipsoid from "../Core/Ellipsoid.js";import EllipsoidGeodesic from "../Core/EllipsoidGeodesic.js";import Event from "../Core/Event.js";import getTimestamp from "../Core/getTimestamp.js";import HeadingPitchRange from "../Core/HeadingPitchRange.js";import HeadingPitchRoll from "../Core/HeadingPitchRoll.js";import Intersect from "../Core/Intersect.js";import IntersectionTests from "../Core/IntersectionTests.js";import CesiumMath from "../Core/Math.js";import Matrix3 from "../Core/Matrix3.js";import Matrix4 from "../Core/Matrix4.js";import OrthographicFrustum from "../Core/OrthographicFrustum.js";import OrthographicOffCenterFrustum from "../Core/OrthographicOffCenterFrustum.js";import PerspectiveFrustum from "../Core/PerspectiveFrustum.js";import Quaternion from "../Core/Quaternion.js";import Ray from "../Core/Ray.js";import Rectangle from "../Core/Rectangle.js";import Transforms from "../Core/Transforms.js";import CameraFlightPath from "./CameraFlightPath.js";import MapMode2D from "./MapMode2D.js";import SceneMode from "./SceneMode.js";/** * @typedef {Object} DirectionUp * * An orientation given by a pair of unit vectors * * @property {Cartesian3} direction The unit "direction" vector * @property {Cartesian3} up The unit "up" vector **//** * @typedef {Object} HeadingPitchRollValues * * An orientation given by numeric heading, pitch, and roll * * @property {number} heading The heading in radians * @property {number} pitch The pitch in radians * @property {number} roll The roll in meters **//** * The camera is defined by a position, orientation, and view frustum. * <br /><br /> * The orientation forms an orthonormal basis with a view, up and right = view x up unit vectors. * <br /><br /> * The viewing frustum is defined by 6 planes. * Each plane is represented by a {@link Cartesian4} object, where the x, y, and z components * define the unit vector normal to the plane, and the w component is the distance of the * plane from the origin/camera position. * * @alias Camera * * @constructor * * @param {Scene} scene The scene. * * @demo {@link https://sandcastle.cesium.com/index.html?src=Camera.html|Cesium Sandcastle Camera Demo} * @demo {@link https://sandcastle.cesium.com/index.html?src=Camera%20Tutorial.html|Cesium Sandcastle Camera Tutorial Example} * @demo {@link https://cesium.com/learn/cesiumjs-learn/cesiumjs-camera|Camera Tutorial} * * @example * // Create a camera looking down the negative z-axis, positioned at the origin, * // with a field of view of 60 degrees, and 1:1 aspect ratio. * const camera = new Cesium.Camera(scene); * camera.position = new Cesium.Cartesian3(); * camera.direction = Cesium.Cartesian3.negate(Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3()); * camera.up = Cesium.Cartesian3.clone(Cesium.Cartesian3.UNIT_Y); * camera.frustum.fov = Cesium.Math.PI_OVER_THREE; * camera.frustum.near = 1.0; * camera.frustum.far = 2.0; */function Camera(scene) {  //>>includeStart('debug', pragmas.debug);  if (!defined(scene)) {    throw new DeveloperError("scene is required.");  }  //>>includeEnd('debug');  this._scene = scene;  this._transform = Matrix4.clone(Matrix4.IDENTITY);  this._invTransform = Matrix4.clone(Matrix4.IDENTITY);  this._actualTransform = Matrix4.clone(Matrix4.IDENTITY);  this._actualInvTransform = Matrix4.clone(Matrix4.IDENTITY);  this._transformChanged = false;  /**   * The position of the camera.   *   * @type {Cartesian3}   */  this.position = new Cartesian3();  this._position = new Cartesian3();  this._positionWC = new Cartesian3();  this._positionCartographic = new Cartographic();  this._oldPositionWC = undefined;  /**   * The position delta magnitude.   *   * @private   */  this.positionWCDeltaMagnitude = 0.0;  /**   * The position delta magnitude last frame.   *   * @private   */  this.positionWCDeltaMagnitudeLastFrame = 0.0;  /**   * How long in seconds since the camera has stopped moving   *   * @private   */  this.timeSinceMoved = 0.0;  this._lastMovedTimestamp = 0.0;  /**   * The view direction of the camera.   *   * @type {Cartesian3}   */  this.direction = new Cartesian3();  this._direction = new Cartesian3();  this._directionWC = new Cartesian3();  /**   * The up direction of the camera.   *   * @type {Cartesian3}   */  this.up = new Cartesian3();  this._up = new Cartesian3();  this._upWC = new Cartesian3();  /**   * The right direction of the camera.   *   * @type {Cartesian3}   */  this.right = new Cartesian3();  this._right = new Cartesian3();  this._rightWC = new Cartesian3();  /**   * The region of space in view.   *   * @type {PerspectiveFrustum|PerspectiveOffCenterFrustum|OrthographicFrustum}   * @default PerspectiveFrustum()   *   * @see PerspectiveFrustum   * @see PerspectiveOffCenterFrustum   * @see OrthographicFrustum   */  this.frustum = new PerspectiveFrustum();  this.frustum.aspectRatio =    scene.drawingBufferWidth / scene.drawingBufferHeight;  this.frustum.fov = CesiumMath.toRadians(60.0);  /**   * The default amount to move the camera when an argument is not   * provided to the move methods.   * @type {Number}   * @default 100000.0;   */  this.defaultMoveAmount = 100000.0;  /**   * The default amount to rotate the camera when an argument is not   * provided to the look methods.   * @type {Number}   * @default Math.PI / 60.0   */  this.defaultLookAmount = Math.PI / 60.0;  /**   * The default amount to rotate the camera when an argument is not   * provided to the rotate methods.   * @type {Number}   * @default Math.PI / 3600.0   */  this.defaultRotateAmount = Math.PI / 3600.0;  /**   * The default amount to move the camera when an argument is not   * provided to the zoom methods.   * @type {Number}   * @default 100000.0;   */  this.defaultZoomAmount = 100000.0;  /**   * If set, the camera will not be able to rotate past this axis in either direction.   * @type {Cartesian3}   * @default undefined   */  this.constrainedAxis = undefined;  /**   * The factor multiplied by the the map size used to determine where to clamp the camera position   * when zooming out from the surface. The default is 1.5. Only valid for 2D and the map is rotatable.   * @type {Number}   * @default 1.5   */  this.maximumZoomFactor = 1.5;  this._moveStart = new Event();  this._moveEnd = new Event();  this._changed = new Event();  this._changedPosition = undefined;  this._changedDirection = undefined;  this._changedFrustum = undefined;  this._changedHeading = undefined;  /**   * 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.   * @type {number}   * @default 0.5   */  this.percentageChanged = 0.5;  this._viewMatrix = new Matrix4();  this._invViewMatrix = new Matrix4();  updateViewMatrix(this);  this._mode = SceneMode.SCENE3D;  this._modeChanged = true;  const projection = scene.mapProjection;  this._projection = projection;  this._maxCoord = projection.project(    new Cartographic(Math.PI, CesiumMath.PI_OVER_TWO)  );  this._max2Dfrustum = undefined;  // set default view  rectangleCameraPosition3D(    this,    Camera.DEFAULT_VIEW_RECTANGLE,    this.position,    true  );  let mag = Cartesian3.magnitude(this.position);  mag += mag * Camera.DEFAULT_VIEW_FACTOR;  Cartesian3.normalize(this.position, this.position);  Cartesian3.multiplyByScalar(this.position, mag, this.position);}/** * @private */Camera.TRANSFORM_2D = new Matrix4(  0.0,  0.0,  1.0,  0.0,  1.0,  0.0,  0.0,  0.0,  0.0,  1.0,  0.0,  0.0,  0.0,  0.0,  0.0,  1.0);/** * @private */Camera.TRANSFORM_2D_INVERSE = Matrix4.inverseTransformation(  Camera.TRANSFORM_2D,  new Matrix4());/** * The default rectangle the camera will view on creation. * @type Rectangle */Camera.DEFAULT_VIEW_RECTANGLE = Rectangle.fromDegrees(  -95.0,  -20.0,  -70.0,  90.0);/** * A scalar to multiply to the camera position and add it back after setting the camera to view the rectangle. * A value of zero means the camera will view the entire {@link Camera#DEFAULT_VIEW_RECTANGLE}, a value greater than zero * will move it further away from the extent, and a value less than zero will move it close to the extent. * @type Number */Camera.DEFAULT_VIEW_FACTOR = 0.5;/** * The default heading/pitch/range that is used when the camera flies to a location that contains a bounding sphere. * @type HeadingPitchRange */Camera.DEFAULT_OFFSET = new HeadingPitchRange(  0.0,  -CesiumMath.PI_OVER_FOUR,  0.0);function updateViewMatrix(camera) {  Matrix4.computeView(    camera._position,    camera._direction,    camera._up,    camera._right,    camera._viewMatrix  );  Matrix4.multiply(    camera._viewMatrix,    camera._actualInvTransform,    camera._viewMatrix  );  Matrix4.inverseTransformation(camera._viewMatrix, camera._invViewMatrix);}function updateCameraDeltas(camera) {  if (!defined(camera._oldPositionWC)) {    camera._oldPositionWC = Cartesian3.clone(      camera.positionWC,      camera._oldPositionWC    );  } else {    camera.positionWCDeltaMagnitudeLastFrame = camera.positionWCDeltaMagnitude;    const delta = Cartesian3.subtract(      camera.positionWC,      camera._oldPositionWC,      camera._oldPositionWC    );    camera.positionWCDeltaMagnitude = Cartesian3.magnitude(delta);    camera._oldPositionWC = Cartesian3.clone(      camera.positionWC,      camera._oldPositionWC    );    // Update move timers    if (camera.positionWCDeltaMagnitude > 0.0) {      camera.timeSinceMoved = 0.0;      camera._lastMovedTimestamp = getTimestamp();    } else {      camera.timeSinceMoved =        Math.max(getTimestamp() - camera._lastMovedTimestamp, 0.0) / 1000.0;    }  }}/** * Checks if there's a camera flight with preload for this camera. * * @returns {Boolean} Whether or not this camera has a current flight with a valid preloadFlightCamera in scene. * * @private * */Camera.prototype.canPreloadFlight = function () {  return defined(this._currentFlight) && this._mode !== SceneMode.SCENE2D;};Camera.prototype._updateCameraChanged = function () {  const camera = this;  updateCameraDeltas(camera);  if (camera._changed.numberOfListeners === 0) {    return;  }  const percentageChanged = camera.percentageChanged;  const currentHeading = camera.heading;  if (!defined(camera._changedHeading)) {    camera._changedHeading = currentHeading;  }  let delta =    Math.abs(camera._changedHeading - currentHeading) % CesiumMath.TWO_PI;  delta = delta > CesiumMath.PI ? CesiumMath.TWO_PI - delta : delta;  // Since delta is computed as the shortest distance between two angles  // the percentage is relative to the half circle.  const headingChangedPercentage = delta / Math.PI;  if (headingChangedPercentage > percentageChanged) {    camera._changed.raiseEvent(headingChangedPercentage);    camera._changedHeading = currentHeading;  }  if (camera._mode === SceneMode.SCENE2D) {    if (!defined(camera._changedFrustum)) {      camera._changedPosition = Cartesian3.clone(        camera.position,        camera._changedPosition      );      camera._changedFrustum = camera.frustum.clone();      return;    }    const position = camera.position;    const lastPosition = camera._changedPosition;    const frustum = camera.frustum;    const lastFrustum = camera._changedFrustum;    const x0 = position.x + frustum.left;    const x1 = position.x + frustum.right;    const x2 = lastPosition.x + lastFrustum.left;    const x3 = lastPosition.x + lastFrustum.right;    const y0 = position.y + frustum.bottom;    const y1 = position.y + frustum.top;    const y2 = lastPosition.y + lastFrustum.bottom;    const y3 = lastPosition.y + lastFrustum.top;    const leftX = Math.max(x0, x2);    const rightX = Math.min(x1, x3);    const bottomY = Math.max(y0, y2);    const topY = Math.min(y1, y3);    let areaPercentage;    if (leftX >= rightX || bottomY >= y1) {      areaPercentage = 1.0;    } else {      let areaRef = lastFrustum;      if (x0 < x2 && x1 > x3 && y0 < y2 && y1 > y3) {        areaRef = frustum;      }      areaPercentage =        1.0 -        ((rightX - leftX) * (topY - bottomY)) /          ((areaRef.right - areaRef.left) * (areaRef.top - areaRef.bottom));    }    if (areaPercentage > percentageChanged) {      camera._changed.raiseEvent(areaPercentage);      camera._changedPosition = Cartesian3.clone(        camera.position,        camera._changedPosition      );      camera._changedFrustum = camera.frustum.clone(camera._changedFrustum);    }    return;  }  if (!defined(camera._changedDirection)) {    camera._changedPosition = Cartesian3.clone(      camera.positionWC,      camera._changedPosition    );    camera._changedDirection = Cartesian3.clone(      camera.directionWC,      camera._changedDirection    );    return;  }  const dirAngle = CesiumMath.acosClamped(    Cartesian3.dot(camera.directionWC, camera._changedDirection)  );  let dirPercentage;  if (defined(camera.frustum.fovy)) {    dirPercentage = dirAngle / (camera.frustum.fovy * 0.5);  } else {    dirPercentage = dirAngle;  }  const distance = Cartesian3.distance(    camera.positionWC,    camera._changedPosition  );  const heightPercentage = distance / camera.positionCartographic.height;  if (    dirPercentage > percentageChanged ||    heightPercentage > percentageChanged  ) {    camera._changed.raiseEvent(Math.max(dirPercentage, heightPercentage));    camera._changedPosition = Cartesian3.clone(      camera.positionWC,      camera._changedPosition    );    camera._changedDirection = Cartesian3.clone(      camera.directionWC,      camera._changedDirection    );  }};function convertTransformForColumbusView(camera) {  Transforms.basisTo2D(    camera._projection,    camera._transform,    camera._actualTransform  );}const scratchCartographic = new Cartographic();const scratchCartesian3Projection = new Cartesian3();const scratchCartesian3 = new Cartesian3();const scratchCartesian4Origin = new Cartesian4();const scratchCartesian4NewOrigin = new Cartesian4();const scratchCartesian4NewXAxis = new Cartesian4();const scratchCartesian4NewYAxis = new Cartesian4();const scratchCartesian4NewZAxis = new Cartesian4();function convertTransformFor2D(camera) {  const projection = camera._projection;  const ellipsoid = projection.ellipsoid;  const origin = Matrix4.getColumn(    camera._transform,    3,    scratchCartesian4Origin  );  const cartographic = ellipsoid.cartesianToCartographic(    origin,    scratchCartographic  );  const projectedPosition = projection.project(    cartographic,    scratchCartesian3Projection  );  const newOrigin = scratchCartesian4NewOrigin;  newOrigin.x = projectedPosition.z;  newOrigin.y = projectedPosition.x;  newOrigin.z = projectedPosition.y;  newOrigin.w = 1.0;  const newZAxis = Cartesian4.clone(    Cartesian4.UNIT_X,    scratchCartesian4NewZAxis  );  const xAxis = Cartesian4.add(    Matrix4.getColumn(camera._transform, 0, scratchCartesian3),    origin,    scratchCartesian3  );  ellipsoid.cartesianToCartographic(xAxis, cartographic);  projection.project(cartographic, projectedPosition);  const newXAxis = scratchCartesian4NewXAxis;  newXAxis.x = projectedPosition.z;  newXAxis.y = projectedPosition.x;  newXAxis.z = projectedPosition.y;  newXAxis.w = 0.0;  Cartesian3.subtract(newXAxis, newOrigin, newXAxis);  newXAxis.x = 0.0;  const newYAxis = scratchCartesian4NewYAxis;  if (Cartesian3.magnitudeSquared(newXAxis) > CesiumMath.EPSILON10) {    Cartesian3.cross(newZAxis, newXAxis, newYAxis);  } else {    const yAxis = Cartesian4.add(      Matrix4.getColumn(camera._transform, 1, scratchCartesian3),      origin,      scratchCartesian3    );    ellipsoid.cartesianToCartographic(yAxis, cartographic);    projection.project(cartographic, projectedPosition);    newYAxis.x = projectedPosition.z;    newYAxis.y = projectedPosition.x;    newYAxis.z = projectedPosition.y;    newYAxis.w = 0.0;    Cartesian3.subtract(newYAxis, newOrigin, newYAxis);    newYAxis.x = 0.0;    if (Cartesian3.magnitudeSquared(newYAxis) < CesiumMath.EPSILON10) {      Cartesian4.clone(Cartesian4.UNIT_Y, newXAxis);      Cartesian4.clone(Cartesian4.UNIT_Z, newYAxis);    }  }  Cartesian3.cross(newYAxis, newZAxis, newXAxis);  Cartesian3.normalize(newXAxis, newXAxis);  Cartesian3.cross(newZAxis, newXAxis, newYAxis);  Cartesian3.normalize(newYAxis, newYAxis);  Matrix4.setColumn(    camera._actualTransform,    0,    newXAxis,    camera._actualTransform  );  Matrix4.setColumn(    camera._actualTransform,    1,    newYAxis,    camera._actualTransform  );  Matrix4.setColumn(    camera._actualTransform,    2,    newZAxis,    camera._actualTransform  );  Matrix4.setColumn(    camera._actualTransform,    3,    newOrigin,    camera._actualTransform  );}const scratchCartesian = new Cartesian3();function updateMembers(camera) {  const mode = camera._mode;  let heightChanged = false;  let height = 0.0;  if (mode === SceneMode.SCENE2D) {    height = camera.frustum.right - camera.frustum.left;    heightChanged = height !== camera._positionCartographic.height;  }  let position = camera._position;  const positionChanged =    !Cartesian3.equals(position, camera.position) || heightChanged;  if (positionChanged) {    position = Cartesian3.clone(camera.position, camera._position);  }  let direction = camera._direction;  const directionChanged = !Cartesian3.equals(direction, camera.direction);  if (directionChanged) {    Cartesian3.normalize(camera.direction, camera.direction);    direction = Cartesian3.clone(camera.direction, camera._direction);  }  let up = camera._up;  const upChanged = !Cartesian3.equals(up, camera.up);  if (upChanged) {    Cartesian3.normalize(camera.up, camera.up);    up = Cartesian3.clone(camera.up, camera._up);  }  let right = camera._right;  const rightChanged = !Cartesian3.equals(right, camera.right);  if (rightChanged) {    Cartesian3.normalize(camera.right, camera.right);    right = Cartesian3.clone(camera.right, camera._right);  }  const transformChanged = camera._transformChanged || camera._modeChanged;  camera._transformChanged = false;  if (transformChanged) {    Matrix4.inverseTransformation(camera._transform, camera._invTransform);    if (      camera._mode === SceneMode.COLUMBUS_VIEW ||      camera._mode === SceneMode.SCENE2D    ) {      if (Matrix4.equals(Matrix4.IDENTITY, camera._transform)) {        Matrix4.clone(Camera.TRANSFORM_2D, camera._actualTransform);      } else if (camera._mode === SceneMode.COLUMBUS_VIEW) {        convertTransformForColumbusView(camera);      } else {        convertTransformFor2D(camera);      }    } else {      Matrix4.clone(camera._transform, camera._actualTransform);    }    Matrix4.inverseTransformation(      camera._actualTransform,      camera._actualInvTransform    );    camera._modeChanged = false;  }  const transform = camera._actualTransform;  if (positionChanged || transformChanged) {    camera._positionWC = Matrix4.multiplyByPoint(      transform,      position,      camera._positionWC    );    // Compute the Cartographic position of the camera.    if (mode === SceneMode.SCENE3D || mode === SceneMode.MORPHING) {      camera._positionCartographic = camera._projection.ellipsoid.cartesianToCartographic(        camera._positionWC,        camera._positionCartographic      );    } else {      // The camera position is expressed in the 2D coordinate system where the Y axis is to the East,      // the Z axis is to the North, and the X axis is out of the map.  Express them instead in the ENU axes where      // X is to the East, Y is to the North, and Z is out of the local horizontal plane.      const positionENU = scratchCartesian;      positionENU.x = camera._positionWC.y;      positionENU.y = camera._positionWC.z;      positionENU.z = camera._positionWC.x;      // In 2D, the camera height is always 12.7 million meters.      // The apparent height is equal to half the frustum width.      if (mode === SceneMode.SCENE2D) {        positionENU.z = height;      }      camera._projection.unproject(positionENU, camera._positionCartographic);    }  }  if (directionChanged || upChanged || rightChanged) {    const det = Cartesian3.dot(      direction,      Cartesian3.cross(up, right, scratchCartesian)    );    if (Math.abs(1.0 - det) > CesiumMath.EPSILON2) {      //orthonormalize axes      const invUpMag = 1.0 / Cartesian3.magnitudeSquared(up);      const scalar = Cartesian3.dot(up, direction) * invUpMag;      const w0 = Cartesian3.multiplyByScalar(        direction,        scalar,        scratchCartesian      );      up = Cartesian3.normalize(        Cartesian3.subtract(up, w0, camera._up),        camera._up      );      Cartesian3.clone(up, camera.up);      right = Cartesian3.cross(direction, up, camera._right);      Cartesian3.clone(right, camera.right);    }  }  if (directionChanged || transformChanged) {    camera._directionWC = Matrix4.multiplyByPointAsVector(      transform,      direction,      camera._directionWC    );    Cartesian3.normalize(camera._directionWC, camera._directionWC);  }  if (upChanged || transformChanged) {    camera._upWC = Matrix4.multiplyByPointAsVector(transform, up, camera._upWC);    Cartesian3.normalize(camera._upWC, camera._upWC);  }  if (rightChanged || transformChanged) {    camera._rightWC = Matrix4.multiplyByPointAsVector(      transform,      right,      camera._rightWC    );    Cartesian3.normalize(camera._rightWC, camera._rightWC);  }  if (    positionChanged ||    directionChanged ||    upChanged ||    rightChanged ||    transformChanged  ) {    updateViewMatrix(camera);  }}function getHeading(direction, up) {  let heading;  if (    !CesiumMath.equalsEpsilon(Math.abs(direction.z), 1.0, CesiumMath.EPSILON3)  ) {    heading = Math.atan2(direction.y, direction.x) - CesiumMath.PI_OVER_TWO;  } else {    heading = Math.atan2(up.y, up.x) - CesiumMath.PI_OVER_TWO;  }  return CesiumMath.TWO_PI - CesiumMath.zeroToTwoPi(heading);}function getPitch(direction) {  return CesiumMath.PI_OVER_TWO - CesiumMath.acosClamped(direction.z);}function getRoll(direction, up, right) {  let roll = 0.0;  if (    !CesiumMath.equalsEpsilon(Math.abs(direction.z), 1.0, CesiumMath.EPSILON3)  ) {    roll = Math.atan2(-right.z, up.z);    roll = CesiumMath.zeroToTwoPi(roll + CesiumMath.TWO_PI);  }  return roll;}const scratchHPRMatrix1 = new Matrix4();const scratchHPRMatrix2 = new Matrix4();Object.defineProperties(Camera.prototype, {  /**   * Gets the camera's reference frame. The inverse of this transformation is appended to the view matrix.   * @memberof Camera.prototype   *   * @type {Matrix4}   * @readonly   *   * @default {@link Matrix4.IDENTITY}   */  transform: {    get: function () {      return this._transform;    },  },  /**   * Gets the inverse camera transform.   * @memberof Camera.prototype   *   * @type {Matrix4}   * @readonly   *   * @default {@link Matrix4.IDENTITY}   */  inverseTransform: {    get: function () {      updateMembers(this);      return this._invTransform;    },  },  /**   * Gets the view matrix.   * @memberof Camera.prototype   *   * @type {Matrix4}   * @readonly   *   * @see Camera#inverseViewMatrix   */  viewMatrix: {    get: function () {      updateMembers(this);      return this._viewMatrix;    },  },  /**   * Gets the inverse view matrix.   * @memberof Camera.prototype   *   * @type {Matrix4}   * @readonly   *   * @see Camera#viewMatrix   */  inverseViewMatrix: {    get: function () {      updateMembers(this);      return this._invViewMatrix;    },  },  /**   * Gets the {@link Cartographic} position of the camera, with longitude and latitude   * expressed in radians and height in meters.  In 2D and Columbus View, it is possible   * for the returned longitude and latitude to be outside the range of valid longitudes   * and latitudes when the camera is outside the map.   * @memberof Camera.prototype   *   * @type {Cartographic}   * @readonly   */  positionCartographic: {    get: function () {      updateMembers(this);      return this._positionCartographic;    },  },  /**   * Gets the position of the camera in world coordinates.   * @memberof Camera.prototype   *   * @type {Cartesian3}   * @readonly   */  positionWC: {    get: function () {      updateMembers(this);      return this._positionWC;    },  },  /**   * Gets the view direction of the camera in world coordinates.   * @memberof Camera.prototype   *   * @type {Cartesian3}   * @readonly   */  directionWC: {    get: function () {      updateMembers(this);      return this._directionWC;    },  },  /**   * Gets the up direction of the camera in world coordinates.   * @memberof Camera.prototype   *   * @type {Cartesian3}   * @readonly   */  upWC: {    get: function () {      updateMembers(this);      return this._upWC;    },  },  /**   * Gets the right direction of the camera in world coordinates.   * @memberof Camera.prototype   *   * @type {Cartesian3}   * @readonly   */  rightWC: {    get: function () {      updateMembers(this);      return this._rightWC;    },  },  /**   * Gets the camera heading in radians.   * @memberof Camera.prototype   *   * @type {Number}   * @readonly   */  heading: {    get: function () {      if (this._mode !== SceneMode.MORPHING) {        const ellipsoid = this._projection.ellipsoid;        const oldTransform = Matrix4.clone(this._transform, scratchHPRMatrix1);        const transform = Transforms.eastNorthUpToFixedFrame(          this.positionWC,          ellipsoid,          scratchHPRMatrix2        );        this._setTransform(transform);        const heading = getHeading(this.direction, this.up);        this._setTransform(oldTransform);        return heading;      }      return undefined;    },  },  /**   * Gets the camera pitch in radians.   * @memberof Camera.prototype   *   * @type {Number}   * @readonly   */  pitch: {    get: function () {      if (this._mode !== SceneMode.MORPHING) {        const ellipsoid = this._projection.ellipsoid;        const oldTransform = Matrix4.clone(this._transform, scratchHPRMatrix1);        const transform = Transforms.eastNorthUpToFixedFrame(          this.positionWC,          ellipsoid,          scratchHPRMatrix2        );        this._setTransform(transform);        const pitch = getPitch(this.direction);        this._setTransform(oldTransform);        return pitch;      }      return undefined;    },  },  /**   * Gets the camera roll in radians.   * @memberof Camera.prototype   *   * @type {Number}   * @readonly   */  roll: {    get: function () {      if (this._mode !== SceneMode.MORPHING) {        const ellipsoid = this._projection.ellipsoid;        const oldTransform = Matrix4.clone(this._transform, scratchHPRMatrix1);        const transform = Transforms.eastNorthUpToFixedFrame(          this.positionWC,          ellipsoid,          scratchHPRMatrix2        );        this._setTransform(transform);        const roll = getRoll(this.direction, this.up, this.right);        this._setTransform(oldTransform);        return roll;      }      return undefined;    },  },  /**   * Gets the event that will be raised at when the camera starts to move.   * @memberof Camera.prototype   * @type {Event}   * @readonly   */  moveStart: {    get: function () {      return this._moveStart;    },  },  /**   * Gets the event that will be raised when the camera has stopped moving.   * @memberof Camera.prototype   * @type {Event}   * @readonly   */  moveEnd: {    get: function () {      return this._moveEnd;    },  },  /**   * Gets the event that will be raised when the camera has changed by <code>percentageChanged</code>.   * @memberof Camera.prototype   * @type {Event}   * @readonly   */  changed: {    get: function () {      return this._changed;    },  },});/** * @private */Camera.prototype.update = function (mode) {  //>>includeStart('debug', pragmas.debug);  if (!defined(mode)) {    throw new DeveloperError("mode is required.");  }  if (    mode === SceneMode.SCENE2D &&    !(this.frustum instanceof OrthographicOffCenterFrustum)  ) {    throw new DeveloperError(      "An OrthographicOffCenterFrustum is required in 2D."    );  }  if (    (mode === SceneMode.SCENE3D || mode === SceneMode.COLUMBUS_VIEW) &&    !(this.frustum instanceof PerspectiveFrustum) &&    !(this.frustum instanceof OrthographicFrustum)  ) {    throw new DeveloperError(      "A PerspectiveFrustum or OrthographicFrustum is required in 3D and Columbus view"    );  }  //>>includeEnd('debug');  let updateFrustum = false;  if (mode !== this._mode) {    this._mode = mode;    this._modeChanged = mode !== SceneMode.MORPHING;    updateFrustum = this._mode === SceneMode.SCENE2D;  }  if (updateFrustum) {    const frustum = (this._max2Dfrustum = this.frustum.clone());    //>>includeStart('debug', pragmas.debug);    if (!(frustum instanceof OrthographicOffCenterFrustum)) {      throw new DeveloperError(        "The camera frustum is expected to be orthographic for 2D camera control."      );    }    //>>includeEnd('debug');    const maxZoomOut = 2.0;    const ratio = frustum.top / frustum.right;    frustum.right = this._maxCoord.x * maxZoomOut;    frustum.left = -frustum.right;    frustum.top = ratio * frustum.right;    frustum.bottom = -frustum.top;  }  if (this._mode === SceneMode.SCENE2D) {    clampMove2D(this, this.position);  }};const setTransformPosition = new Cartesian3();const setTransformUp = new Cartesian3();const setTransformDirection = new Cartesian3();Camera.prototype._setTransform = function (transform) {  const position = Cartesian3.clone(this.positionWC, setTransformPosition);  const up = Cartesian3.clone(this.upWC, setTransformUp);  const direction = Cartesian3.clone(this.directionWC, setTransformDirection);  Matrix4.clone(transform, this._transform);  this._transformChanged = true;  updateMembers(this);  const inverse = this._actualInvTransform;  Matrix4.multiplyByPoint(inverse, position, this.position);  Matrix4.multiplyByPointAsVector(inverse, direction, this.direction);  Matrix4.multiplyByPointAsVector(inverse, up, this.up);  Cartesian3.cross(this.direction, this.up, this.right);  updateMembers(this);};const scratchAdjustOrthographicFrustumMousePosition = new Cartesian2();const scratchPickRay = new Ray();const scratchRayIntersection = new Cartesian3();const scratchDepthIntersection = new Cartesian3();function calculateOrthographicFrustumWidth(camera) {  // Camera is fixed to an object, so keep frustum width constant.  if (!Matrix4.equals(Matrix4.IDENTITY, camera.transform)) {    return Cartesian3.magnitude(camera.position);  }  const scene = camera._scene;  const globe = scene.globe;  const mousePosition = scratchAdjustOrthographicFrustumMousePosition;  mousePosition.x = scene.drawingBufferWidth / 2.0;  mousePosition.y = scene.drawingBufferHeight / 2.0;  let rayIntersection;  if (defined(globe)) {    const ray = camera.getPickRay(mousePosition, scratchPickRay);    rayIntersection = globe.pickWorldCoordinates(      ray,      scene,      true,      scratchRayIntersection    );  }  let depthIntersection;  if (scene.pickPositionSupported) {    depthIntersection = scene.pickPositionWorldCoordinates(      mousePosition,      scratchDepthIntersection    );  }  let distance;  if (defined(rayIntersection) || defined(depthIntersection)) {    const depthDistance = defined(depthIntersection)      ? Cartesian3.distance(depthIntersection, camera.positionWC)      : Number.POSITIVE_INFINITY;    const rayDistance = defined(rayIntersection)      ? Cartesian3.distance(rayIntersection, camera.positionWC)      : Number.POSITIVE_INFINITY;    distance = Math.min(depthDistance, rayDistance);  } else {    distance = Math.max(camera.positionCartographic.height, 0.0);  }  return distance;}Camera.prototype._adjustOrthographicFrustum = function (zooming) {  if (!(this.frustum instanceof OrthographicFrustum)) {    return;  }  if (!zooming && this._positionCartographic.height < 150000.0) {    return;  }  this.frustum.width = calculateOrthographicFrustumWidth(this);};const scratchSetViewCartesian = new Cartesian3();const scratchSetViewTransform1 = new Matrix4();const scratchSetViewTransform2 = new Matrix4();const scratchSetViewQuaternion = new Quaternion();const scratchSetViewMatrix3 = new Matrix3();const scratchSetViewCartographic = new Cartographic();function setView3D(camera, position, hpr) {  const currentTransform = Matrix4.clone(    camera.transform,    scratchSetViewTransform1  );  const localTransform = Transforms.eastNorthUpToFixedFrame(    position,    camera._projection.ellipsoid,    scratchSetViewTransform2  );  camera._setTransform(localTransform);  Cartesian3.clone(Cartesian3.ZERO, camera.position);  hpr.heading = hpr.heading - CesiumMath.PI_OVER_TWO;  const rotQuat = Quaternion.fromHeadingPitchRoll(    hpr,    scratchSetViewQuaternion  );  const rotMat = Matrix3.fromQuaternion(rotQuat, scratchSetViewMatrix3);  Matrix3.getColumn(rotMat, 0, camera.direction);  Matrix3.getColumn(rotMat, 2, camera.up);  Cartesian3.cross(camera.direction, camera.up, camera.right);  camera._setTransform(currentTransform);  camera._adjustOrthographicFrustum(true);}function setViewCV(camera, position, hpr, convert) {  const currentTransform = Matrix4.clone(    camera.transform,    scratchSetViewTransform1  );  camera._setTransform(Matrix4.IDENTITY);  if (!Cartesian3.equals(position, camera.positionWC)) {    if (convert) {      const projection = camera._projection;      const cartographic = projection.ellipsoid.cartesianToCartographic(        position,        scratchSetViewCartographic      );      position = projection.project(cartographic, scratchSetViewCartesian);    }    Cartesian3.clone(position, camera.position);  }  hpr.heading = hpr.heading - CesiumMath.PI_OVER_TWO;  const rotQuat = Quaternion.fromHeadingPitchRoll(    hpr,    scratchSetViewQuaternion  );  const rotMat = Matrix3.fromQuaternion(rotQuat, scratchSetViewMatrix3);  Matrix3.getColumn(rotMat, 0, camera.direction);  Matrix3.getColumn(rotMat, 2, camera.up);  Cartesian3.cross(camera.direction, camera.up, camera.right);  camera._setTransform(currentTransform);  camera._adjustOrthographicFrustum(true);}function setView2D(camera, position, hpr, convert) {  const currentTransform = Matrix4.clone(    camera.transform,    scratchSetViewTransform1  );  camera._setTransform(Matrix4.IDENTITY);  if (!Cartesian3.equals(position, camera.positionWC)) {    if (convert) {      const projection = camera._projection;      const cartographic = projection.ellipsoid.cartesianToCartographic(        position,        scratchSetViewCartographic      );      position = projection.project(cartographic, scratchSetViewCartesian);    }    Cartesian2.clone(position, camera.position);    const newLeft = -position.z * 0.5;    const newRight = -newLeft;    const frustum = camera.frustum;    if (newRight > newLeft) {      const ratio = frustum.top / frustum.right;      frustum.right = newRight;      frustum.left = newLeft;      frustum.top = frustum.right * ratio;      frustum.bottom = -frustum.top;    }  }  if (camera._scene.mapMode2D === MapMode2D.ROTATE) {    hpr.heading = hpr.heading - CesiumMath.PI_OVER_TWO;    hpr.pitch = -CesiumMath.PI_OVER_TWO;    hpr.roll = 0.0;    const rotQuat = Quaternion.fromHeadingPitchRoll(      hpr,      scratchSetViewQuaternion    );    const rotMat = Matrix3.fromQuaternion(rotQuat, scratchSetViewMatrix3);    Matrix3.getColumn(rotMat, 2, camera.up);    Cartesian3.cross(camera.direction, camera.up, camera.right);  }  camera._setTransform(currentTransform);}const scratchToHPRDirection = new Cartesian3();const scratchToHPRUp = new Cartesian3();const scratchToHPRRight = new Cartesian3();function directionUpToHeadingPitchRoll(camera, position, orientation, result) {  const direction = Cartesian3.clone(    orientation.direction,    scratchToHPRDirection  );  const up = Cartesian3.clone(orientation.up, scratchToHPRUp);  if (camera._scene.mode === SceneMode.SCENE3D) {    const ellipsoid = camera._projection.ellipsoid;    const transform = Transforms.eastNorthUpToFixedFrame(      position,      ellipsoid,      scratchHPRMatrix1    );    const invTransform = Matrix4.inverseTransformation(      transform,      scratchHPRMatrix2    );    Matrix4.multiplyByPointAsVector(invTransform, direction, direction);    Matrix4.multiplyByPointAsVector(invTransform, up, up);  }  const right = Cartesian3.cross(direction, up, scratchToHPRRight);  result.heading = getHeading(direction, up);  result.pitch = getPitch(direction);  result.roll = getRoll(direction, up, right);  return result;}const scratchSetViewOptions = {  destination: undefined,  orientation: {    direction: undefined,    up: undefined,    heading: undefined,    pitch: undefined,    roll: undefined,  },  convert: undefined,  endTransform: undefined,};const scratchHpr = new HeadingPitchRoll();/** * Sets the camera position, orientation and transform. * * @param {Object} options Object with the following properties: * @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. * @param {HeadingPitchRollValues|DirectionUp} [options.orientation] An object that contains either direction and up properties or heading, pitch and roll properties. By default, the direction will point * 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 * y direction in Columbus view. Orientation is not used in 2D when in infinite scrolling mode. * @param {Matrix4} [options.endTransform] Transform matrix representing the reference frame of the camera. * @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>. * * @example * // 1. Set position with a top-down view * viewer.camera.setView({ *     destination : Cesium.Cartesian3.fromDegrees(-117.16, 32.71, 15000.0) * }); * * // 2 Set view with heading, pitch and roll * viewer.camera.setView({ *     destination : cartesianPosition, *     orientation: { *         heading : Cesium.Math.toRadians(90.0), // east, default value is 0.0 (north) *         pitch : Cesium.Math.toRadians(-90),    // default value (looking down) *         roll : 0.0                             // default value *     } * }); * * // 3. Change heading, pitch and roll with the camera position remaining the same. * viewer.camera.setView({ *     orientation: { *         heading : Cesium.Math.toRadians(90.0), // east, default value is 0.0 (north) *         pitch : Cesium.Math.toRadians(-90),    // default value (looking down) *         roll : 0.0                             // default value *     } * }); * * * // 4. View rectangle with a top-down view * viewer.camera.setView({ *     destination : Cesium.Rectangle.fromDegrees(west, south, east, north) * }); * * // 5. Set position with an orientation using unit vectors. * viewer.camera.setView({ *     destination : Cesium.Cartesian3.fromDegrees(-122.19, 46.25, 5000.0), *     orientation : { *         direction : new Cesium.Cartesian3(-0.04231243104240401, -0.20123236049443421, -0.97862924300734), *         up : new Cesium.Cartesian3(-0.47934589305293746, -0.8553216253114552, 0.1966022179118339) *     } * }); */Camera.prototype.setView = function (options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  let orientation = defaultValue(    options.orientation,    defaultValue.EMPTY_OBJECT  );  const mode = this._mode;  if (mode === SceneMode.MORPHING) {    return;  }  if (defined(options.endTransform)) {    this._setTransform(options.endTransform);  }  let convert = defaultValue(options.convert, true);  let destination = defaultValue(    options.destination,    Cartesian3.clone(this.positionWC, scratchSetViewCartesian)  );  if (defined(destination) && defined(destination.west)) {    destination = this.getRectangleCameraCoordinates(      destination,      scratchSetViewCartesian    );    convert = false;  }  if (defined(orientation.direction)) {    orientation = directionUpToHeadingPitchRoll(      this,      destination,      orientation,      scratchSetViewOptions.orientation    );  }  scratchHpr.heading = defaultValue(orientation.heading, 0.0);  scratchHpr.pitch = defaultValue(orientation.pitch, -CesiumMath.PI_OVER_TWO);  scratchHpr.roll = defaultValue(orientation.roll, 0.0);  if (mode === SceneMode.SCENE3D) {    setView3D(this, destination, scratchHpr);  } else if (mode === SceneMode.SCENE2D) {    setView2D(this, destination, scratchHpr, convert);  } else {    setViewCV(this, destination, scratchHpr, convert);  }};const pitchScratch = new Cartesian3();/** * Fly the camera to the home view.  Use {@link Camera#.DEFAULT_VIEW_RECTANGLE} to set * the default view for the 3D scene.  The home view for 2D and columbus view shows the * entire map. * * @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} */Camera.prototype.flyHome = function (duration) {  const mode = this._mode;  if (mode === SceneMode.MORPHING) {    this._scene.completeMorph();  }  if (mode === SceneMode.SCENE2D) {    this.flyTo({      destination: Camera.DEFAULT_VIEW_RECTANGLE,      duration: duration,      endTransform: Matrix4.IDENTITY,    });  } else if (mode === SceneMode.SCENE3D) {    const destination = this.getRectangleCameraCoordinates(      Camera.DEFAULT_VIEW_RECTANGLE    );    let mag = Cartesian3.magnitude(destination);    mag += mag * Camera.DEFAULT_VIEW_FACTOR;    Cartesian3.normalize(destination, destination);    Cartesian3.multiplyByScalar(destination, mag, destination);    this.flyTo({      destination: destination,      duration: duration,      endTransform: Matrix4.IDENTITY,    });  } else if (mode === SceneMode.COLUMBUS_VIEW) {    const maxRadii = this._projection.ellipsoid.maximumRadius;    let position = new Cartesian3(0.0, -1.0, 1.0);    position = Cartesian3.multiplyByScalar(      Cartesian3.normalize(position, position),      5.0 * maxRadii,      position    );    this.flyTo({      destination: position,      duration: duration,      orientation: {        heading: 0.0,        pitch: -Math.acos(Cartesian3.normalize(position, pitchScratch).z),        roll: 0.0,      },      endTransform: Matrix4.IDENTITY,      convert: false,    });  }};/** * Transform a vector or point from world coordinates to the camera's reference frame. * * @param {Cartesian4} cartesian The vector or point to transform. * @param {Cartesian4} [result] The object onto which to store the result. * @returns {Cartesian4} The transformed vector or point. */Camera.prototype.worldToCameraCoordinates = function (cartesian, result) {  //>>includeStart('debug', pragmas.debug);  if (!defined(cartesian)) {    throw new DeveloperError("cartesian is required.");  }  //>>includeEnd('debug');  if (!defined(result)) {    result = new Cartesian4();  }  updateMembers(this);  return Matrix4.multiplyByVector(this._actualInvTransform, cartesian, result);};/** * Transform a point from world coordinates to the camera's reference frame. * * @param {Cartesian3} cartesian The point to transform. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} The transformed point. */Camera.prototype.worldToCameraCoordinatesPoint = function (cartesian, result) {  //>>includeStart('debug', pragmas.debug);  if (!defined(cartesian)) {    throw new DeveloperError("cartesian is required.");  }  //>>includeEnd('debug');  if (!defined(result)) {    result = new Cartesian3();  }  updateMembers(this);  return Matrix4.multiplyByPoint(this._actualInvTransform, cartesian, result);};/** * Transform a vector from world coordinates to the camera's reference frame. * * @param {Cartesian3} cartesian The vector to transform. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} The transformed vector. */Camera.prototype.worldToCameraCoordinatesVector = function (cartesian, result) {  //>>includeStart('debug', pragmas.debug);  if (!defined(cartesian)) {    throw new DeveloperError("cartesian is required.");  }  //>>includeEnd('debug');  if (!defined(result)) {    result = new Cartesian3();  }  updateMembers(this);  return Matrix4.multiplyByPointAsVector(    this._actualInvTransform,    cartesian,    result  );};/** * Transform a vector or point from the camera's reference frame to world coordinates. * * @param {Cartesian4} cartesian The vector or point to transform. * @param {Cartesian4} [result] The object onto which to store the result. * @returns {Cartesian4} The transformed vector or point. */Camera.prototype.cameraToWorldCoordinates = function (cartesian, result) {  //>>includeStart('debug', pragmas.debug);  if (!defined(cartesian)) {    throw new DeveloperError("cartesian is required.");  }  //>>includeEnd('debug');  if (!defined(result)) {    result = new Cartesian4();  }  updateMembers(this);  return Matrix4.multiplyByVector(this._actualTransform, cartesian, result);};/** * Transform a point from the camera's reference frame to world coordinates. * * @param {Cartesian3} cartesian The point to transform. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} The transformed point. */Camera.prototype.cameraToWorldCoordinatesPoint = function (cartesian, result) {  //>>includeStart('debug', pragmas.debug);  if (!defined(cartesian)) {    throw new DeveloperError("cartesian is required.");  }  //>>includeEnd('debug');  if (!defined(result)) {    result = new Cartesian3();  }  updateMembers(this);  return Matrix4.multiplyByPoint(this._actualTransform, cartesian, result);};/** * Transform a vector from the camera's reference frame to world coordinates. * * @param {Cartesian3} cartesian The vector to transform. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} The transformed vector. */Camera.prototype.cameraToWorldCoordinatesVector = function (cartesian, result) {  //>>includeStart('debug', pragmas.debug);  if (!defined(cartesian)) {    throw new DeveloperError("cartesian is required.");  }  //>>includeEnd('debug');  if (!defined(result)) {    result = new Cartesian3();  }  updateMembers(this);  return Matrix4.multiplyByPointAsVector(    this._actualTransform,    cartesian,    result  );};function clampMove2D(camera, position) {  const rotatable2D = camera._scene.mapMode2D === MapMode2D.ROTATE;  const maxProjectedX = camera._maxCoord.x;  const maxProjectedY = camera._maxCoord.y;  let minX;  let maxX;  if (rotatable2D) {    maxX = maxProjectedX;    minX = -maxX;  } else {    maxX = position.x - maxProjectedX * 2.0;    minX = position.x + maxProjectedX * 2.0;  }  if (position.x > maxProjectedX) {    position.x = maxX;  }  if (position.x < -maxProjectedX) {    position.x = minX;  }  if (position.y > maxProjectedY) {    position.y = maxProjectedY;  }  if (position.y < -maxProjectedY) {    position.y = -maxProjectedY;  }}const moveScratch = new Cartesian3();/** * Translates the camera's position by <code>amount</code> along <code>direction</code>. * * @param {Cartesian3} direction The direction to move. * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>. * * @see Camera#moveBackward * @see Camera#moveForward * @see Camera#moveLeft * @see Camera#moveRight * @see Camera#moveUp * @see Camera#moveDown */Camera.prototype.move = function (direction, amount) {  //>>includeStart('debug', pragmas.debug);  if (!defined(direction)) {    throw new DeveloperError("direction is required.");  }  //>>includeEnd('debug');  const cameraPosition = this.position;  Cartesian3.multiplyByScalar(direction, amount, moveScratch);  Cartesian3.add(cameraPosition, moveScratch, cameraPosition);  if (this._mode === SceneMode.SCENE2D) {    clampMove2D(this, cameraPosition);  }  this._adjustOrthographicFrustum(true);};/** * Translates the camera's position by <code>amount</code> along the camera's view vector. * When in 2D mode, this will zoom in the camera instead of translating the camera's position. * * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>. * * @see Camera#moveBackward */Camera.prototype.moveForward = function (amount) {  amount = defaultValue(amount, this.defaultMoveAmount);  if (this._mode === SceneMode.SCENE2D) {    // 2D mode    zoom2D(this, amount);  } else {    // 3D or Columbus view mode    this.move(this.direction, amount);  }};/** * Translates the camera's position by <code>amount</code> along the opposite direction * of the camera's view vector. * When in 2D mode, this will zoom out the camera instead of translating the camera's position. * * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>. * * @see Camera#moveForward */Camera.prototype.moveBackward = function (amount) {  amount = defaultValue(amount, this.defaultMoveAmount);  if (this._mode === SceneMode.SCENE2D) {    // 2D mode    zoom2D(this, -amount);  } else {    // 3D or Columbus view mode    this.move(this.direction, -amount);  }};/** * Translates the camera's position by <code>amount</code> along the camera's up vector. * * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>. * * @see Camera#moveDown */Camera.prototype.moveUp = function (amount) {  amount = defaultValue(amount, this.defaultMoveAmount);  this.move(this.up, amount);};/** * Translates the camera's position by <code>amount</code> along the opposite direction * of the camera's up vector. * * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>. * * @see Camera#moveUp */Camera.prototype.moveDown = function (amount) {  amount = defaultValue(amount, this.defaultMoveAmount);  this.move(this.up, -amount);};/** * Translates the camera's position by <code>amount</code> along the camera's right vector. * * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>. * * @see Camera#moveLeft */Camera.prototype.moveRight = function (amount) {  amount = defaultValue(amount, this.defaultMoveAmount);  this.move(this.right, amount);};/** * Translates the camera's position by <code>amount</code> along the opposite direction * of the camera's right vector. * * @param {Number} [amount] The amount, in meters, to move. Defaults to <code>defaultMoveAmount</code>. * * @see Camera#moveRight */Camera.prototype.moveLeft = function (amount) {  amount = defaultValue(amount, this.defaultMoveAmount);  this.move(this.right, -amount);};/** * Rotates the camera around its up vector by amount, in radians, in the opposite direction * of its right vector if not in 2D mode. * * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>. * * @see Camera#lookRight */Camera.prototype.lookLeft = function (amount) {  amount = defaultValue(amount, this.defaultLookAmount);  // only want view of map to change in 3D mode, 2D visual is incorrect when look changes  if (this._mode !== SceneMode.SCENE2D) {    this.look(this.up, -amount);  }};/** * Rotates the camera around its up vector by amount, in radians, in the direction * of its right vector if not in 2D mode. * * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>. * * @see Camera#lookLeft */Camera.prototype.lookRight = function (amount) {  amount = defaultValue(amount, this.defaultLookAmount);  // only want view of map to change in 3D mode, 2D visual is incorrect when look changes  if (this._mode !== SceneMode.SCENE2D) {    this.look(this.up, amount);  }};/** * Rotates the camera around its right vector by amount, in radians, in the direction * of its up vector if not in 2D mode. * * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>. * * @see Camera#lookDown */Camera.prototype.lookUp = function (amount) {  amount = defaultValue(amount, this.defaultLookAmount);  // only want view of map to change in 3D mode, 2D visual is incorrect when look changes  if (this._mode !== SceneMode.SCENE2D) {    this.look(this.right, -amount);  }};/** * Rotates the camera around its right vector by amount, in radians, in the opposite direction * of its up vector if not in 2D mode. * * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>. * * @see Camera#lookUp */Camera.prototype.lookDown = function (amount) {  amount = defaultValue(amount, this.defaultLookAmount);  // only want view of map to change in 3D mode, 2D visual is incorrect when look changes  if (this._mode !== SceneMode.SCENE2D) {    this.look(this.right, amount);  }};const lookScratchQuaternion = new Quaternion();const lookScratchMatrix = new Matrix3();/** * Rotate each of the camera's orientation vectors around <code>axis</code> by <code>angle</code> * * @param {Cartesian3} axis The axis to rotate around. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>. * * @see Camera#lookUp * @see Camera#lookDown * @see Camera#lookLeft * @see Camera#lookRight */Camera.prototype.look = function (axis, angle) {  //>>includeStart('debug', pragmas.debug);  if (!defined(axis)) {    throw new DeveloperError("axis is required.");  }  //>>includeEnd('debug');  const turnAngle = defaultValue(angle, this.defaultLookAmount);  const quaternion = Quaternion.fromAxisAngle(    axis,    -turnAngle,    lookScratchQuaternion  );  const rotation = Matrix3.fromQuaternion(quaternion, lookScratchMatrix);  const direction = this.direction;  const up = this.up;  const right = this.right;  Matrix3.multiplyByVector(rotation, direction, direction);  Matrix3.multiplyByVector(rotation, up, up);  Matrix3.multiplyByVector(rotation, right, right);};/** * Rotate the camera counter-clockwise around its direction vector by amount, in radians. * * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>. * * @see Camera#twistRight */Camera.prototype.twistLeft = function (amount) {  amount = defaultValue(amount, this.defaultLookAmount);  this.look(this.direction, amount);};/** * Rotate the camera clockwise around its direction vector by amount, in radians. * * @param {Number} [amount] The amount, in radians, to rotate by. Defaults to <code>defaultLookAmount</code>. * * @see Camera#twistLeft */Camera.prototype.twistRight = function (amount) {  amount = defaultValue(amount, this.defaultLookAmount);  this.look(this.direction, -amount);};const rotateScratchQuaternion = new Quaternion();const rotateScratchMatrix = new Matrix3();/** * Rotates the camera around <code>axis</code> by <code>angle</code>. The distance * of the camera's position to the center of the camera's reference frame remains the same. * * @param {Cartesian3} axis The axis to rotate around given in world coordinates. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>. * * @see Camera#rotateUp * @see Camera#rotateDown * @see Camera#rotateLeft * @see Camera#rotateRight */Camera.prototype.rotate = function (axis, angle) {  //>>includeStart('debug', pragmas.debug);  if (!defined(axis)) {    throw new DeveloperError("axis is required.");  }  //>>includeEnd('debug');  const turnAngle = defaultValue(angle, this.defaultRotateAmount);  const quaternion = Quaternion.fromAxisAngle(    axis,    -turnAngle,    rotateScratchQuaternion  );  const rotation = Matrix3.fromQuaternion(quaternion, rotateScratchMatrix);  Matrix3.multiplyByVector(rotation, this.position, this.position);  Matrix3.multiplyByVector(rotation, this.direction, this.direction);  Matrix3.multiplyByVector(rotation, this.up, this.up);  Cartesian3.cross(this.direction, this.up, this.right);  Cartesian3.cross(this.right, this.direction, this.up);  this._adjustOrthographicFrustum(false);};/** * Rotates the camera around the center of the camera's reference frame by angle downwards. * * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>. * * @see Camera#rotateUp * @see Camera#rotate */Camera.prototype.rotateDown = function (angle) {  angle = defaultValue(angle, this.defaultRotateAmount);  rotateVertical(this, angle);};/** * Rotates the camera around the center of the camera's reference frame by angle upwards. * * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>. * * @see Camera#rotateDown * @see Camera#rotate */Camera.prototype.rotateUp = function (angle) {  angle = defaultValue(angle, this.defaultRotateAmount);  rotateVertical(this, -angle);};const rotateVertScratchP = new Cartesian3();const rotateVertScratchA = new Cartesian3();const rotateVertScratchTan = new Cartesian3();const rotateVertScratchNegate = new Cartesian3();function rotateVertical(camera, angle) {  const position = camera.position;  if (    defined(camera.constrainedAxis) &&    !Cartesian3.equalsEpsilon(      camera.position,      Cartesian3.ZERO,      CesiumMath.EPSILON2    )  ) {    const p = Cartesian3.normalize(position, rotateVertScratchP);    const northParallel = Cartesian3.equalsEpsilon(      p,      camera.constrainedAxis,      CesiumMath.EPSILON2    );    const southParallel = Cartesian3.equalsEpsilon(      p,      Cartesian3.negate(camera.constrainedAxis, rotateVertScratchNegate),      CesiumMath.EPSILON2    );    if (!northParallel && !southParallel) {      const constrainedAxis = Cartesian3.normalize(        camera.constrainedAxis,        rotateVertScratchA      );      let dot = Cartesian3.dot(p, constrainedAxis);      let angleToAxis = CesiumMath.acosClamped(dot);      if (angle > 0 && angle > angleToAxis) {        angle = angleToAxis - CesiumMath.EPSILON4;      }      dot = Cartesian3.dot(        p,        Cartesian3.negate(constrainedAxis, rotateVertScratchNegate)      );      angleToAxis = CesiumMath.acosClamped(dot);      if (angle < 0 && -angle > angleToAxis) {        angle = -angleToAxis + CesiumMath.EPSILON4;      }      const tangent = Cartesian3.cross(        constrainedAxis,        p,        rotateVertScratchTan      );      camera.rotate(tangent, angle);    } else if ((northParallel && angle < 0) || (southParallel && angle > 0)) {      camera.rotate(camera.right, angle);    }  } else {    camera.rotate(camera.right, angle);  }}/** * Rotates the camera around the center of the camera's reference frame by angle to the right. * * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>. * * @see Camera#rotateLeft * @see Camera#rotate */Camera.prototype.rotateRight = function (angle) {  angle = defaultValue(angle, this.defaultRotateAmount);  rotateHorizontal(this, -angle);};/** * Rotates the camera around the center of the camera's reference frame by angle to the left. * * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to <code>defaultRotateAmount</code>. * * @see Camera#rotateRight * @see Camera#rotate */Camera.prototype.rotateLeft = function (angle) {  angle = defaultValue(angle, this.defaultRotateAmount);  rotateHorizontal(this, angle);};function rotateHorizontal(camera, angle) {  if (defined(camera.constrainedAxis)) {    camera.rotate(camera.constrainedAxis, angle);  } else {    camera.rotate(camera.up, angle);  }}function zoom2D(camera, amount) {  const frustum = camera.frustum;  //>>includeStart('debug', pragmas.debug);  if (    !(frustum instanceof OrthographicOffCenterFrustum) ||    !defined(frustum.left) ||    !defined(frustum.right) ||    !defined(frustum.bottom) ||    !defined(frustum.top)  ) {    throw new DeveloperError(      "The camera frustum is expected to be orthographic for 2D camera control."    );  }  //>>includeEnd('debug');  let ratio;  amount = amount * 0.5;  if (    Math.abs(frustum.top) + Math.abs(frustum.bottom) >    Math.abs(frustum.left) + Math.abs(frustum.right)  ) {    let newTop = frustum.top - amount;    let newBottom = frustum.bottom + amount;    let maxBottom = camera._maxCoord.y;    if (camera._scene.mapMode2D === MapMode2D.ROTATE) {      maxBottom *= camera.maximumZoomFactor;    }    if (newBottom > maxBottom) {      newBottom = maxBottom;      newTop = -maxBottom;    }    if (newTop <= newBottom) {      newTop = 1.0;      newBottom = -1.0;    }    ratio = frustum.right / frustum.top;    frustum.top = newTop;    frustum.bottom = newBottom;    frustum.right = frustum.top * ratio;    frustum.left = -frustum.right;  } else {    let newRight = frustum.right - amount;    let newLeft = frustum.left + amount;    let maxRight = camera._maxCoord.x;    if (camera._scene.mapMode2D === MapMode2D.ROTATE) {      maxRight *= camera.maximumZoomFactor;    }    if (newRight > maxRight) {      newRight = maxRight;      newLeft = -maxRight;    }    if (newRight <= newLeft) {      newRight = 1.0;      newLeft = -1.0;    }    ratio = frustum.top / frustum.right;    frustum.right = newRight;    frustum.left = newLeft;    frustum.top = frustum.right * ratio;    frustum.bottom = -frustum.top;  }}function zoom3D(camera, amount) {  camera.move(camera.direction, amount);}/** * Zooms <code>amount</code> along the camera's view vector. * * @param {Number} [amount] The amount to move. Defaults to <code>defaultZoomAmount</code>. * * @see Camera#zoomOut */Camera.prototype.zoomIn = function (amount) {  amount = defaultValue(amount, this.defaultZoomAmount);  if (this._mode === SceneMode.SCENE2D) {    zoom2D(this, amount);  } else {    zoom3D(this, amount);  }};/** * Zooms <code>amount</code> along the opposite direction of * the camera's view vector. * * @param {Number} [amount] The amount to move. Defaults to <code>defaultZoomAmount</code>. * * @see Camera#zoomIn */Camera.prototype.zoomOut = function (amount) {  amount = defaultValue(amount, this.defaultZoomAmount);  if (this._mode === SceneMode.SCENE2D) {    zoom2D(this, -amount);  } else {    zoom3D(this, -amount);  }};/** * Gets the magnitude of the camera position. In 3D, this is the vector magnitude. In 2D and * Columbus view, this is the distance to the map. * * @returns {Number} The magnitude of the position. */Camera.prototype.getMagnitude = function () {  if (this._mode === SceneMode.SCENE3D) {    return Cartesian3.magnitude(this.position);  } else if (this._mode === SceneMode.COLUMBUS_VIEW) {    return Math.abs(this.position.z);  } else if (this._mode === SceneMode.SCENE2D) {    return Math.max(      this.frustum.right - this.frustum.left,      this.frustum.top - this.frustum.bottom    );  }};const scratchLookAtMatrix4 = new Matrix4();/** * Sets the camera position and orientation using a target and offset. The target must be given in * 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. * 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 * is heading/pitch/range, then the heading and the pitch angles are defined in the reference frame defined by the transformation matrix. * The heading is the angle from y axis and increasing towards the x axis. Pitch is the rotation from the xy-plane. Positive pitch * angles are below the plane. Negative pitch angles are above the plane. The range is the distance from the center. * * In 2D, there must be a top down view. The camera will be placed above the target looking down. The height above the * target will be the magnitude of the offset. The heading will be determined from the offset. If the heading cannot be * determined from the offset, the heading will be north. * * @param {Cartesian3} target The target position in world coordinates. * @param {Cartesian3|HeadingPitchRange} offset The offset from the target in the local east-north-up reference frame centered at the target. * * @exception {DeveloperError} lookAt is not supported while morphing. * * @example * // 1. Using a cartesian offset * const center = Cesium.Cartesian3.fromDegrees(-98.0, 40.0); * viewer.camera.lookAt(center, new Cesium.Cartesian3(0.0, -4790000.0, 3930000.0)); * * // 2. Using a HeadingPitchRange offset * const center = Cesium.Cartesian3.fromDegrees(-72.0, 40.0); * const heading = Cesium.Math.toRadians(50.0); * const pitch = Cesium.Math.toRadians(-20.0); * const range = 5000.0; * viewer.camera.lookAt(center, new Cesium.HeadingPitchRange(heading, pitch, range)); */Camera.prototype.lookAt = function (target, offset) {  //>>includeStart('debug', pragmas.debug);  if (!defined(target)) {    throw new DeveloperError("target is required");  }  if (!defined(offset)) {    throw new DeveloperError("offset is required");  }  if (this._mode === SceneMode.MORPHING) {    throw new DeveloperError("lookAt is not supported while morphing.");  }  //>>includeEnd('debug');  const transform = Transforms.eastNorthUpToFixedFrame(    target,    Ellipsoid.WGS84,    scratchLookAtMatrix4  );  this.lookAtTransform(transform, offset);};const scratchLookAtHeadingPitchRangeOffset = new Cartesian3();const scratchLookAtHeadingPitchRangeQuaternion1 = new Quaternion();const scratchLookAtHeadingPitchRangeQuaternion2 = new Quaternion();const scratchHeadingPitchRangeMatrix3 = new Matrix3();function offsetFromHeadingPitchRange(heading, pitch, range) {  pitch = CesiumMath.clamp(    pitch,    -CesiumMath.PI_OVER_TWO,    CesiumMath.PI_OVER_TWO  );  heading = CesiumMath.zeroToTwoPi(heading) - CesiumMath.PI_OVER_TWO;  const pitchQuat = Quaternion.fromAxisAngle(    Cartesian3.UNIT_Y,    -pitch,    scratchLookAtHeadingPitchRangeQuaternion1  );  const headingQuat = Quaternion.fromAxisAngle(    Cartesian3.UNIT_Z,    -heading,    scratchLookAtHeadingPitchRangeQuaternion2  );  const rotQuat = Quaternion.multiply(headingQuat, pitchQuat, headingQuat);  const rotMatrix = Matrix3.fromQuaternion(    rotQuat,    scratchHeadingPitchRangeMatrix3  );  const offset = Cartesian3.clone(    Cartesian3.UNIT_X,    scratchLookAtHeadingPitchRangeOffset  );  Matrix3.multiplyByVector(rotMatrix, offset, offset);  Cartesian3.negate(offset, offset);  Cartesian3.multiplyByScalar(offset, range, offset);  return offset;}/** * Sets the camera position and orientation using a target and transformation matrix. The offset can be either a cartesian or heading/pitch/range. * 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 * is heading/pitch/range, then the heading and the pitch angles are defined in the reference frame defined by the transformation matrix. * The heading is the angle from y axis and increasing towards the x axis. Pitch is the rotation from the xy-plane. Positive pitch * angles are below the plane. Negative pitch angles are above the plane. The range is the distance from the center. * * 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 * target will be the magnitude of the offset. The heading will be determined from the offset. If the heading cannot be * determined from the offset, the heading will be north. * * @param {Matrix4} transform The transformation matrix defining the reference frame. * @param {Cartesian3|HeadingPitchRange} [offset] The offset from the target in a reference frame centered at the target. * * @exception {DeveloperError} lookAtTransform is not supported while morphing. * * @example * // 1. Using a cartesian offset * const transform = Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(-98.0, 40.0)); * viewer.camera.lookAtTransform(transform, new Cesium.Cartesian3(0.0, -4790000.0, 3930000.0)); * * // 2. Using a HeadingPitchRange offset * const transform = Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(-72.0, 40.0)); * const heading = Cesium.Math.toRadians(50.0); * const pitch = Cesium.Math.toRadians(-20.0); * const range = 5000.0; * viewer.camera.lookAtTransform(transform, new Cesium.HeadingPitchRange(heading, pitch, range)); */Camera.prototype.lookAtTransform = function (transform, offset) {  //>>includeStart('debug', pragmas.debug);  if (!defined(transform)) {    throw new DeveloperError("transform is required");  }  if (this._mode === SceneMode.MORPHING) {    throw new DeveloperError(      "lookAtTransform is not supported while morphing."    );  }  //>>includeEnd('debug');  this._setTransform(transform);  if (!defined(offset)) {    return;  }  let cartesianOffset;  if (defined(offset.heading)) {    cartesianOffset = offsetFromHeadingPitchRange(      offset.heading,      offset.pitch,      offset.range    );  } else {    cartesianOffset = offset;  }  if (this._mode === SceneMode.SCENE2D) {    Cartesian2.clone(Cartesian2.ZERO, this.position);    Cartesian3.negate(cartesianOffset, this.up);    this.up.z = 0.0;    if (Cartesian3.magnitudeSquared(this.up) < CesiumMath.EPSILON10) {      Cartesian3.clone(Cartesian3.UNIT_Y, this.up);    }    Cartesian3.normalize(this.up, this.up);    this._setTransform(Matrix4.IDENTITY);    Cartesian3.negate(Cartesian3.UNIT_Z, this.direction);    Cartesian3.cross(this.direction, this.up, this.right);    Cartesian3.normalize(this.right, this.right);    const frustum = this.frustum;    const ratio = frustum.top / frustum.right;    frustum.right = Cartesian3.magnitude(cartesianOffset) * 0.5;    frustum.left = -frustum.right;    frustum.top = ratio * frustum.right;    frustum.bottom = -frustum.top;    this._setTransform(transform);    return;  }  Cartesian3.clone(cartesianOffset, this.position);  Cartesian3.negate(this.position, this.direction);  Cartesian3.normalize(this.direction, this.direction);  Cartesian3.cross(this.direction, Cartesian3.UNIT_Z, this.right);  if (Cartesian3.magnitudeSquared(this.right) < CesiumMath.EPSILON10) {    Cartesian3.clone(Cartesian3.UNIT_X, this.right);  }  Cartesian3.normalize(this.right, this.right);  Cartesian3.cross(this.right, this.direction, this.up);  Cartesian3.normalize(this.up, this.up);  this._adjustOrthographicFrustum(true);};const viewRectangle3DCartographic1 = new Cartographic();const viewRectangle3DCartographic2 = new Cartographic();const viewRectangle3DNorthEast = new Cartesian3();const viewRectangle3DSouthWest = new Cartesian3();const viewRectangle3DNorthWest = new Cartesian3();const viewRectangle3DSouthEast = new Cartesian3();const viewRectangle3DNorthCenter = new Cartesian3();const viewRectangle3DSouthCenter = new Cartesian3();const viewRectangle3DCenter = new Cartesian3();const viewRectangle3DEquator = new Cartesian3();const defaultRF = {  direction: new Cartesian3(),  right: new Cartesian3(),  up: new Cartesian3(),};let viewRectangle3DEllipsoidGeodesic;function computeD(direction, upOrRight, corner, tanThetaOrPhi) {  const opposite = Math.abs(Cartesian3.dot(upOrRight, corner));  return opposite / tanThetaOrPhi - Cartesian3.dot(direction, corner);}function rectangleCameraPosition3D(camera, rectangle, result, updateCamera) {  const ellipsoid = camera._projection.ellipsoid;  const cameraRF = updateCamera ? camera : defaultRF;  const north = rectangle.north;  const south = rectangle.south;  let east = rectangle.east;  const west = rectangle.west;  // If we go across the International Date Line  if (west > east) {    east += CesiumMath.TWO_PI;  }  // Find the midpoint latitude.  //  // EllipsoidGeodesic will fail if the north and south edges are very close to being on opposite sides of the ellipsoid.  // Ideally we'd just call EllipsoidGeodesic.setEndPoints and let it throw when it detects this case, but sadly it doesn't  // even look for this case in optimized builds, so we have to test for it here instead.  //  // 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,  // so handle it just by using 0 latitude as the center.  It's certainliy possible to use a smaller tolerance  // than one degree here, but one degree is safe and putting the center at 0 latitude should be good enough for any  // rectangle that spans 178+ of the 180 degrees of latitude.  const longitude = (west + east) * 0.5;  let latitude;  if (    south < -CesiumMath.PI_OVER_TWO + CesiumMath.RADIANS_PER_DEGREE &&    north > CesiumMath.PI_OVER_TWO - CesiumMath.RADIANS_PER_DEGREE  ) {    latitude = 0.0;  } else {    const northCartographic = viewRectangle3DCartographic1;    northCartographic.longitude = longitude;    northCartographic.latitude = north;    northCartographic.height = 0.0;    const southCartographic = viewRectangle3DCartographic2;    southCartographic.longitude = longitude;    southCartographic.latitude = south;    southCartographic.height = 0.0;    let ellipsoidGeodesic = viewRectangle3DEllipsoidGeodesic;    if (      !defined(ellipsoidGeodesic) ||      ellipsoidGeodesic.ellipsoid !== ellipsoid    ) {      viewRectangle3DEllipsoidGeodesic = ellipsoidGeodesic = new EllipsoidGeodesic(        undefined,        undefined,        ellipsoid      );    }    ellipsoidGeodesic.setEndPoints(northCartographic, southCartographic);    latitude = ellipsoidGeodesic.interpolateUsingFraction(      0.5,      viewRectangle3DCartographic1    ).latitude;  }  const centerCartographic = viewRectangle3DCartographic1;  centerCartographic.longitude = longitude;  centerCartographic.latitude = latitude;  centerCartographic.height = 0.0;  const center = ellipsoid.cartographicToCartesian(    centerCartographic,    viewRectangle3DCenter  );  const cart = viewRectangle3DCartographic1;  cart.longitude = east;  cart.latitude = north;  const northEast = ellipsoid.cartographicToCartesian(    cart,    viewRectangle3DNorthEast  );  cart.longitude = west;  const northWest = ellipsoid.cartographicToCartesian(    cart,    viewRectangle3DNorthWest  );  cart.longitude = longitude;  const northCenter = ellipsoid.cartographicToCartesian(    cart,    viewRectangle3DNorthCenter  );  cart.latitude = south;  const southCenter = ellipsoid.cartographicToCartesian(    cart,    viewRectangle3DSouthCenter  );  cart.longitude = east;  const southEast = ellipsoid.cartographicToCartesian(    cart,    viewRectangle3DSouthEast  );  cart.longitude = west;  const southWest = ellipsoid.cartographicToCartesian(    cart,    viewRectangle3DSouthWest  );  Cartesian3.subtract(northWest, center, northWest);  Cartesian3.subtract(southEast, center, southEast);  Cartesian3.subtract(northEast, center, northEast);  Cartesian3.subtract(southWest, center, southWest);  Cartesian3.subtract(northCenter, center, northCenter);  Cartesian3.subtract(southCenter, center, southCenter);  const direction = ellipsoid.geodeticSurfaceNormal(center, cameraRF.direction);  Cartesian3.negate(direction, direction);  const right = Cartesian3.cross(direction, Cartesian3.UNIT_Z, cameraRF.right);  Cartesian3.normalize(right, right);  const up = Cartesian3.cross(right, direction, cameraRF.up);  let d;  if (camera.frustum instanceof OrthographicFrustum) {    const width = Math.max(      Cartesian3.distance(northEast, northWest),      Cartesian3.distance(southEast, southWest)    );    const height = Math.max(      Cartesian3.distance(northEast, southEast),      Cartesian3.distance(northWest, southWest)    );    let rightScalar;    let topScalar;    const ratio =      camera.frustum._offCenterFrustum.right /      camera.frustum._offCenterFrustum.top;    const heightRatio = height * ratio;    if (width > heightRatio) {      rightScalar = width;      topScalar = rightScalar / ratio;    } else {      topScalar = height;      rightScalar = heightRatio;    }    d = Math.max(rightScalar, topScalar);  } else {    const tanPhi = Math.tan(camera.frustum.fovy * 0.5);    const tanTheta = camera.frustum.aspectRatio * tanPhi;    d = Math.max(      computeD(direction, up, northWest, tanPhi),      computeD(direction, up, southEast, tanPhi),      computeD(direction, up, northEast, tanPhi),      computeD(direction, up, southWest, tanPhi),      computeD(direction, up, northCenter, tanPhi),      computeD(direction, up, southCenter, tanPhi),      computeD(direction, right, northWest, tanTheta),      computeD(direction, right, southEast, tanTheta),      computeD(direction, right, northEast, tanTheta),      computeD(direction, right, southWest, tanTheta),      computeD(direction, right, northCenter, tanTheta),      computeD(direction, right, southCenter, tanTheta)    );    // If the rectangle crosses the equator, compute D at the equator, too, because that's the    // widest part of the rectangle when projected onto the globe.    if (south < 0 && north > 0) {      const equatorCartographic = viewRectangle3DCartographic1;      equatorCartographic.longitude = west;      equatorCartographic.latitude = 0.0;      equatorCartographic.height = 0.0;      let equatorPosition = ellipsoid.cartographicToCartesian(        equatorCartographic,        viewRectangle3DEquator      );      Cartesian3.subtract(equatorPosition, center, equatorPosition);      d = Math.max(        d,        computeD(direction, up, equatorPosition, tanPhi),        computeD(direction, right, equatorPosition, tanTheta)      );      equatorCartographic.longitude = east;      equatorPosition = ellipsoid.cartographicToCartesian(        equatorCartographic,        viewRectangle3DEquator      );      Cartesian3.subtract(equatorPosition, center, equatorPosition);      d = Math.max(        d,        computeD(direction, up, equatorPosition, tanPhi),        computeD(direction, right, equatorPosition, tanTheta)      );    }  }  return Cartesian3.add(    center,    Cartesian3.multiplyByScalar(direction, -d, viewRectangle3DEquator),    result  );}const viewRectangleCVCartographic = new Cartographic();const viewRectangleCVNorthEast = new Cartesian3();const viewRectangleCVSouthWest = new Cartesian3();function rectangleCameraPositionColumbusView(camera, rectangle, result) {  const projection = camera._projection;  if (rectangle.west > rectangle.east) {    rectangle = Rectangle.MAX_VALUE;  }  const transform = camera._actualTransform;  const invTransform = camera._actualInvTransform;  const cart = viewRectangleCVCartographic;  cart.longitude = rectangle.east;  cart.latitude = rectangle.north;  const northEast = projection.project(cart, viewRectangleCVNorthEast);  Matrix4.multiplyByPoint(transform, northEast, northEast);  Matrix4.multiplyByPoint(invTransform, northEast, northEast);  cart.longitude = rectangle.west;  cart.latitude = rectangle.south;  const southWest = projection.project(cart, viewRectangleCVSouthWest);  Matrix4.multiplyByPoint(transform, southWest, southWest);  Matrix4.multiplyByPoint(invTransform, southWest, southWest);  result.x = (northEast.x - southWest.x) * 0.5 + southWest.x;  result.y = (northEast.y - southWest.y) * 0.5 + southWest.y;  if (defined(camera.frustum.fovy)) {    const tanPhi = Math.tan(camera.frustum.fovy * 0.5);    const tanTheta = camera.frustum.aspectRatio * tanPhi;    result.z =      Math.max(        (northEast.x - southWest.x) / tanTheta,        (northEast.y - southWest.y) / tanPhi      ) * 0.5;  } else {    const width = northEast.x - southWest.x;    const height = northEast.y - southWest.y;    result.z = Math.max(width, height);  }  return result;}const viewRectangle2DCartographic = new Cartographic();const viewRectangle2DNorthEast = new Cartesian3();const viewRectangle2DSouthWest = new Cartesian3();function rectangleCameraPosition2D(camera, rectangle, result) {  const projection = camera._projection;  // Account for the rectangle crossing the International Date Line in 2D mode  let east = rectangle.east;  if (rectangle.west > rectangle.east) {    if (camera._scene.mapMode2D === MapMode2D.INFINITE_SCROLL) {      east += CesiumMath.TWO_PI;    } else {      rectangle = Rectangle.MAX_VALUE;      east = rectangle.east;    }  }  let cart = viewRectangle2DCartographic;  cart.longitude = east;  cart.latitude = rectangle.north;  const northEast = projection.project(cart, viewRectangle2DNorthEast);  cart.longitude = rectangle.west;  cart.latitude = rectangle.south;  const southWest = projection.project(cart, viewRectangle2DSouthWest);  const width = Math.abs(northEast.x - southWest.x) * 0.5;  let height = Math.abs(northEast.y - southWest.y) * 0.5;  let right, top;  const ratio = camera.frustum.right / camera.frustum.top;  const heightRatio = height * ratio;  if (width > heightRatio) {    right = width;    top = right / ratio;  } else {    top = height;    right = heightRatio;  }  height = Math.max(2.0 * right, 2.0 * top);  result.x = (northEast.x - southWest.x) * 0.5 + southWest.x;  result.y = (northEast.y - southWest.y) * 0.5 + southWest.y;  cart = projection.unproject(result, cart);  cart.height = height;  result = projection.project(cart, result);  return result;}/** * Get the camera position needed to view a rectangle on an ellipsoid or map * * @param {Rectangle} rectangle The rectangle to view. * @param {Cartesian3} [result] The camera position needed to view the rectangle * @returns {Cartesian3} The camera position needed to view the rectangle */Camera.prototype.getRectangleCameraCoordinates = function (rectangle, result) {  //>>includeStart('debug', pragmas.debug);  if (!defined(rectangle)) {    throw new DeveloperError("rectangle is required");  }  //>>includeEnd('debug');  const mode = this._mode;  if (!defined(result)) {    result = new Cartesian3();  }  if (mode === SceneMode.SCENE3D) {    return rectangleCameraPosition3D(this, rectangle, result);  } else if (mode === SceneMode.COLUMBUS_VIEW) {    return rectangleCameraPositionColumbusView(this, rectangle, result);  } else if (mode === SceneMode.SCENE2D) {    return rectangleCameraPosition2D(this, rectangle, result);  }  return undefined;};const pickEllipsoid3DRay = new Ray();function pickEllipsoid3D(camera, windowPosition, ellipsoid, result) {  ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);  const ray = camera.getPickRay(windowPosition, pickEllipsoid3DRay);  const intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid);  if (!intersection) {    return undefined;  }  const t = intersection.start > 0.0 ? intersection.start : intersection.stop;  return Ray.getPoint(ray, t, result);}const pickEllipsoid2DRay = new Ray();function pickMap2D(camera, windowPosition, projection, result) {  const ray = camera.getPickRay(windowPosition, pickEllipsoid2DRay);  let position = ray.origin;  position = Cartesian3.fromElements(position.y, position.z, 0.0, position);  const cart = projection.unproject(position);  if (    cart.latitude < -CesiumMath.PI_OVER_TWO ||    cart.latitude > CesiumMath.PI_OVER_TWO  ) {    return undefined;  }  return projection.ellipsoid.cartographicToCartesian(cart, result);}const pickEllipsoidCVRay = new Ray();function pickMapColumbusView(camera, windowPosition, projection, result) {  const ray = camera.getPickRay(windowPosition, pickEllipsoidCVRay);  const scalar = -ray.origin.x / ray.direction.x;  Ray.getPoint(ray, scalar, result);  const cart = projection.unproject(new Cartesian3(result.y, result.z, 0.0));  if (    cart.latitude < -CesiumMath.PI_OVER_TWO ||    cart.latitude > CesiumMath.PI_OVER_TWO ||    cart.longitude < -Math.PI ||    cart.longitude > Math.PI  ) {    return undefined;  }  return projection.ellipsoid.cartographicToCartesian(cart, result);}/** * Pick an ellipsoid or map. * * @param {Cartesian2} windowPosition The x and y coordinates of a pixel. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to pick. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3 | undefined} If the ellipsoid or map was picked, * returns the point on the surface of the ellipsoid or map in world * coordinates. If the ellipsoid or map was not picked, returns undefined. * * @example * const canvas = viewer.scene.canvas; * const center = new Cesium.Cartesian2(canvas.clientWidth / 2.0, canvas.clientHeight / 2.0); * const ellipsoid = viewer.scene.globe.ellipsoid; * const result = viewer.camera.pickEllipsoid(center, ellipsoid); */Camera.prototype.pickEllipsoid = function (windowPosition, ellipsoid, result) {  //>>includeStart('debug', pragmas.debug);  if (!defined(windowPosition)) {    throw new DeveloperError("windowPosition is required.");  }  //>>includeEnd('debug');  const canvas = this._scene.canvas;  if (canvas.clientWidth === 0 || canvas.clientHeight === 0) {    return undefined;  }  if (!defined(result)) {    result = new Cartesian3();  }  ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);  if (this._mode === SceneMode.SCENE3D) {    result = pickEllipsoid3D(this, windowPosition, ellipsoid, result);  } else if (this._mode === SceneMode.SCENE2D) {    result = pickMap2D(this, windowPosition, this._projection, result);  } else if (this._mode === SceneMode.COLUMBUS_VIEW) {    result = pickMapColumbusView(      this,      windowPosition,      this._projection,      result    );  } else {    return undefined;  }  return result;};const pickPerspCenter = new Cartesian3();const pickPerspXDir = new Cartesian3();const pickPerspYDir = new Cartesian3();function getPickRayPerspective(camera, windowPosition, result) {  const canvas = camera._scene.canvas;  const width = canvas.clientWidth;  const height = canvas.clientHeight;  const tanPhi = Math.tan(camera.frustum.fovy * 0.5);  const tanTheta = camera.frustum.aspectRatio * tanPhi;  const near = camera.frustum.near;  const x = (2.0 / width) * windowPosition.x - 1.0;  const y = (2.0 / height) * (height - windowPosition.y) - 1.0;  const position = camera.positionWC;  Cartesian3.clone(position, result.origin);  const nearCenter = Cartesian3.multiplyByScalar(    camera.directionWC,    near,    pickPerspCenter  );  Cartesian3.add(position, nearCenter, nearCenter);  const xDir = Cartesian3.multiplyByScalar(    camera.rightWC,    x * near * tanTheta,    pickPerspXDir  );  const yDir = Cartesian3.multiplyByScalar(    camera.upWC,    y * near * tanPhi,    pickPerspYDir  );  const direction = Cartesian3.add(nearCenter, xDir, result.direction);  Cartesian3.add(direction, yDir, direction);  Cartesian3.subtract(direction, position, direction);  Cartesian3.normalize(direction, direction);  return result;}const scratchDirection = new Cartesian3();function getPickRayOrthographic(camera, windowPosition, result) {  const canvas = camera._scene.canvas;  const width = canvas.clientWidth;  const height = canvas.clientHeight;  let frustum = camera.frustum;  if (defined(frustum._offCenterFrustum)) {    frustum = frustum._offCenterFrustum;  }  let x = (2.0 / width) * windowPosition.x - 1.0;  x *= (frustum.right - frustum.left) * 0.5;  let y = (2.0 / height) * (height - windowPosition.y) - 1.0;  y *= (frustum.top - frustum.bottom) * 0.5;  const origin = result.origin;  Cartesian3.clone(camera.position, origin);  Cartesian3.multiplyByScalar(camera.right, x, scratchDirection);  Cartesian3.add(scratchDirection, origin, origin);  Cartesian3.multiplyByScalar(camera.up, y, scratchDirection);  Cartesian3.add(scratchDirection, origin, origin);  Cartesian3.clone(camera.directionWC, result.direction);  if (    camera._mode === SceneMode.COLUMBUS_VIEW ||    camera._mode === SceneMode.SCENE2D  ) {    Cartesian3.fromElements(      result.origin.z,      result.origin.x,      result.origin.y,      result.origin    );  }  return result;}/** * Create a ray from the camera position through the pixel at <code>windowPosition</code> * in world coordinates. * * @param {Cartesian2} windowPosition The x and y coordinates of a pixel. * @param {Ray} [result] The object onto which to store the result. * @returns {Ray|undefined} Returns the {@link Cartesian3} position and direction of the ray, or undefined if the pick ray cannot be determined. */Camera.prototype.getPickRay = function (windowPosition, result) {  //>>includeStart('debug', pragmas.debug);  if (!defined(windowPosition)) {    throw new DeveloperError("windowPosition is required.");  }  //>>includeEnd('debug');  if (!defined(result)) {    result = new Ray();  }  const canvas = this._scene.canvas;  if (canvas.clientWidth <= 0 || canvas.clientHeight <= 0) {    return undefined;  }  const frustum = this.frustum;  if (    defined(frustum.aspectRatio) &&    defined(frustum.fov) &&    defined(frustum.near)  ) {    return getPickRayPerspective(this, windowPosition, result);  }  return getPickRayOrthographic(this, windowPosition, result);};const scratchToCenter = new Cartesian3();const scratchProj = new Cartesian3();/** * Return the distance from the camera to the front of the bounding sphere. * * @param {BoundingSphere} boundingSphere The bounding sphere in world coordinates. * @returns {Number} The distance to the bounding sphere. */Camera.prototype.distanceToBoundingSphere = function (boundingSphere) {  //>>includeStart('debug', pragmas.debug);  if (!defined(boundingSphere)) {    throw new DeveloperError("boundingSphere is required.");  }  //>>includeEnd('debug');  const toCenter = Cartesian3.subtract(    this.positionWC,    boundingSphere.center,    scratchToCenter  );  const proj = Cartesian3.multiplyByScalar(    this.directionWC,    Cartesian3.dot(toCenter, this.directionWC),    scratchProj  );  return Math.max(0.0, Cartesian3.magnitude(proj) - boundingSphere.radius);};const scratchPixelSize = new Cartesian2();/** * Return the pixel size in meters. * * @param {BoundingSphere} boundingSphere The bounding sphere in world coordinates. * @param {Number} drawingBufferWidth The drawing buffer width. * @param {Number} drawingBufferHeight The drawing buffer height. * @returns {Number} The pixel size in meters. */Camera.prototype.getPixelSize = function (  boundingSphere,  drawingBufferWidth,  drawingBufferHeight) {  //>>includeStart('debug', pragmas.debug);  if (!defined(boundingSphere)) {    throw new DeveloperError("boundingSphere is required.");  }  if (!defined(drawingBufferWidth)) {    throw new DeveloperError("drawingBufferWidth is required.");  }  if (!defined(drawingBufferHeight)) {    throw new DeveloperError("drawingBufferHeight is required.");  }  //>>includeEnd('debug');  const distance = this.distanceToBoundingSphere(boundingSphere);  const pixelSize = this.frustum.getPixelDimensions(    drawingBufferWidth,    drawingBufferHeight,    distance,    this._scene.pixelRatio,    scratchPixelSize  );  return Math.max(pixelSize.x, pixelSize.y);};function createAnimationTemplateCV(  camera,  position,  center,  maxX,  maxY,  duration) {  const newPosition = Cartesian3.clone(position);  if (center.y > maxX) {    newPosition.y -= center.y - maxX;  } else if (center.y < -maxX) {    newPosition.y += -maxX - center.y;  }  if (center.z > maxY) {    newPosition.z -= center.z - maxY;  } else if (center.z < -maxY) {    newPosition.z += -maxY - center.z;  }  function updateCV(value) {    const interp = Cartesian3.lerp(      position,      newPosition,      value.time,      new Cartesian3()    );    camera.worldToCameraCoordinatesPoint(interp, camera.position);  }  return {    easingFunction: EasingFunction.EXPONENTIAL_OUT,    startObject: {      time: 0.0,    },    stopObject: {      time: 1.0,    },    duration: duration,    update: updateCV,  };}const normalScratch = new Cartesian3();const centerScratch = new Cartesian3();const posScratch = new Cartesian3();const scratchCartesian3Subtract = new Cartesian3();function createAnimationCV(camera, duration) {  let position = camera.position;  const direction = camera.direction;  const normal = camera.worldToCameraCoordinatesVector(    Cartesian3.UNIT_X,    normalScratch  );  const scalar =    -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction);  const center = Cartesian3.add(    position,    Cartesian3.multiplyByScalar(direction, scalar, centerScratch),    centerScratch  );  camera.cameraToWorldCoordinatesPoint(center, center);  position = camera.cameraToWorldCoordinatesPoint(camera.position, posScratch);  const tanPhi = Math.tan(camera.frustum.fovy * 0.5);  const tanTheta = camera.frustum.aspectRatio * tanPhi;  const distToC = Cartesian3.magnitude(    Cartesian3.subtract(position, center, scratchCartesian3Subtract)  );  const dWidth = tanTheta * distToC;  const dHeight = tanPhi * distToC;  const mapWidth = camera._maxCoord.x;  const mapHeight = camera._maxCoord.y;  const maxX = Math.max(dWidth - mapWidth, mapWidth);  const maxY = Math.max(dHeight - mapHeight, mapHeight);  if (    position.z < -maxX ||    position.z > maxX ||    position.y < -maxY ||    position.y > maxY  ) {    const translateX = center.y < -maxX || center.y > maxX;    const translateY = center.z < -maxY || center.z > maxY;    if (translateX || translateY) {      return createAnimationTemplateCV(        camera,        position,        center,        maxX,        maxY,        duration      );    }  }  return undefined;}/** * Create an animation to move the map into view. This method is only valid for 2D and Columbus modes. * * @param {Number} duration The duration, in seconds, of the animation. * @returns {Object} The animation or undefined if the scene mode is 3D or the map is already ion view. * * @private */Camera.prototype.createCorrectPositionTween = function (duration) {  //>>includeStart('debug', pragmas.debug);  if (!defined(duration)) {    throw new DeveloperError("duration is required.");  }  //>>includeEnd('debug');  if (this._mode === SceneMode.COLUMBUS_VIEW) {    return createAnimationCV(this, duration);  }  return undefined;};const scratchFlyToDestination = new Cartesian3();const newOptions = {  destination: undefined,  heading: undefined,  pitch: undefined,  roll: undefined,  duration: undefined,  complete: undefined,  cancel: undefined,  endTransform: undefined,  maximumHeight: undefined,  easingFunction: undefined,};/** * Cancels the current camera flight and leaves the camera at its current location. * If no flight is in progress, this this function does nothing. */Camera.prototype.cancelFlight = function () {  if (defined(this._currentFlight)) {    this._currentFlight.cancelTween();    this._currentFlight = undefined;  }};/** * Completes the current camera flight and moves the camera immediately to its final destination. * If no flight is in progress, this this function does nothing. */Camera.prototype.completeFlight = function () {  if (defined(this._currentFlight)) {    this._currentFlight.cancelTween();    const options = {      destination: undefined,      orientation: {        heading: undefined,        pitch: undefined,        roll: undefined,      },    };    options.destination = newOptions.destination;    options.orientation.heading = newOptions.heading;    options.orientation.pitch = newOptions.pitch;    options.orientation.roll = newOptions.roll;    this.setView(options);    if (defined(this._currentFlight.complete)) {      this._currentFlight.complete();    }    this._currentFlight = undefined;  }};/** * Flies the camera from its current position to a new position. * * @param {Object} options Object with the following properties: * @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. * @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 * 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 * y direction in Columbus view.  Orientation is not used in 2D when in infinite scrolling mode. * @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. * @param {Camera.FlightCompleteCallback} [options.complete] The function to execute when the flight is complete. * @param {Camera.FlightCancelledCallback} [options.cancel] The function to execute if the flight is cancelled. * @param {Matrix4} [options.endTransform] Transform matrix representing the reference frame the camera will be in when the flight is completed. * @param {Number} [options.maximumHeight] The maximum height at the peak of the flight. * @param {Number} [options.pitchAdjustHeight] If camera flyes higher than that value, adjust pitch duiring the flight to look down, and keep Earth in viewport. * @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. * @param {Number} [options.flyOverLongitudeWeight] Fly over the lon specifyed via flyOverLongitude only if that way is not longer than short way times flyOverLongitudeWeight. * @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>. * @param {EasingFunction.Callback} [options.easingFunction] Controls how the time is interpolated over the duration of the flight. * * @exception {DeveloperError} If either direction or up is given, then both are required. * * @example * // 1. Fly to a position with a top-down view * viewer.camera.flyTo({ *     destination : Cesium.Cartesian3.fromDegrees(-117.16, 32.71, 15000.0) * }); * * // 2. Fly to a Rectangle with a top-down view * viewer.camera.flyTo({ *     destination : Cesium.Rectangle.fromDegrees(west, south, east, north) * }); * * // 3. Fly to a position with an orientation using unit vectors. * viewer.camera.flyTo({ *     destination : Cesium.Cartesian3.fromDegrees(-122.19, 46.25, 5000.0), *     orientation : { *         direction : new Cesium.Cartesian3(-0.04231243104240401, -0.20123236049443421, -0.97862924300734), *         up : new Cesium.Cartesian3(-0.47934589305293746, -0.8553216253114552, 0.1966022179118339) *     } * }); * * // 4. Fly to a position with an orientation using heading, pitch and roll. * viewer.camera.flyTo({ *     destination : Cesium.Cartesian3.fromDegrees(-122.19, 46.25, 5000.0), *     orientation : { *         heading : Cesium.Math.toRadians(175.0), *         pitch : Cesium.Math.toRadians(-35.0), *         roll : 0.0 *     } * }); */Camera.prototype.flyTo = function (options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  let destination = options.destination;  //>>includeStart('debug', pragmas.debug);  if (!defined(destination)) {    throw new DeveloperError("destination is required.");  }  //>>includeEnd('debug');  const mode = this._mode;  if (mode === SceneMode.MORPHING) {    return;  }  this.cancelFlight();  let orientation = defaultValue(    options.orientation,    defaultValue.EMPTY_OBJECT  );  if (defined(orientation.direction)) {    orientation = directionUpToHeadingPitchRoll(      this,      destination,      orientation,      scratchSetViewOptions.orientation    );  }  if (defined(options.duration) && options.duration <= 0.0) {    const setViewOptions = scratchSetViewOptions;    setViewOptions.destination = options.destination;    setViewOptions.orientation.heading = orientation.heading;    setViewOptions.orientation.pitch = orientation.pitch;    setViewOptions.orientation.roll = orientation.roll;    setViewOptions.convert = options.convert;    setViewOptions.endTransform = options.endTransform;    this.setView(setViewOptions);    if (typeof options.complete === "function") {      options.complete();    }    return;  }  const isRectangle = defined(destination.west);  if (isRectangle) {    destination = this.getRectangleCameraCoordinates(      destination,      scratchFlyToDestination    );  }  const that = this;  /* eslint-disable-next-line prefer-const */  let flightTween;  newOptions.destination = destination;  newOptions.heading = orientation.heading;  newOptions.pitch = orientation.pitch;  newOptions.roll = orientation.roll;  newOptions.duration = options.duration;  newOptions.complete = function () {    if (flightTween === that._currentFlight) {      that._currentFlight = undefined;    }    if (defined(options.complete)) {      options.complete();    }  };  newOptions.cancel = options.cancel;  newOptions.endTransform = options.endTransform;  newOptions.convert = isRectangle ? false : options.convert;  newOptions.maximumHeight = options.maximumHeight;  newOptions.pitchAdjustHeight = options.pitchAdjustHeight;  newOptions.flyOverLongitude = options.flyOverLongitude;  newOptions.flyOverLongitudeWeight = options.flyOverLongitudeWeight;  newOptions.easingFunction = options.easingFunction;  const scene = this._scene;  const tweenOptions = CameraFlightPath.createTween(scene, newOptions);  // If the camera doesn't actually need to go anywhere, duration  // will be 0 and we can just complete the current flight.  if (tweenOptions.duration === 0) {    if (typeof tweenOptions.complete === "function") {      tweenOptions.complete();    }    return;  }  flightTween = scene.tweens.add(tweenOptions);  this._currentFlight = flightTween;  // Save the final destination view information for the PRELOAD_FLIGHT pass.  let preloadFlightCamera = this._scene.preloadFlightCamera;  if (this._mode !== SceneMode.SCENE2D) {    if (!defined(preloadFlightCamera)) {      preloadFlightCamera = Camera.clone(this);    }    preloadFlightCamera.setView({      destination: destination,      orientation: orientation,    });    this._scene.preloadFlightCullingVolume = preloadFlightCamera.frustum.computeCullingVolume(      preloadFlightCamera.positionWC,      preloadFlightCamera.directionWC,      preloadFlightCamera.upWC    );  }};function distanceToBoundingSphere3D(camera, radius) {  const frustum = camera.frustum;  const tanPhi = Math.tan(frustum.fovy * 0.5);  const tanTheta = frustum.aspectRatio * tanPhi;  return Math.max(radius / tanTheta, radius / tanPhi);}function distanceToBoundingSphere2D(camera, radius) {  let frustum = camera.frustum;  if (defined(frustum._offCenterFrustum)) {    frustum = frustum._offCenterFrustum;  }  let right, top;  const ratio = frustum.right / frustum.top;  const heightRatio = radius * ratio;  if (radius > heightRatio) {    right = radius;    top = right / ratio;  } else {    top = radius;    right = heightRatio;  }  return Math.max(right, top) * 1.5;}const MINIMUM_ZOOM = 100.0;function adjustBoundingSphereOffset(camera, boundingSphere, offset) {  offset = HeadingPitchRange.clone(    defined(offset) ? offset : Camera.DEFAULT_OFFSET  );  const minimumZoom =    camera._scene.screenSpaceCameraController.minimumZoomDistance;  const maximumZoom =    camera._scene.screenSpaceCameraController.maximumZoomDistance;  const range = offset.range;  if (!defined(range) || range === 0.0) {    const radius = boundingSphere.radius;    if (radius === 0.0) {      offset.range = MINIMUM_ZOOM;    } else if (      camera.frustum instanceof OrthographicFrustum ||      camera._mode === SceneMode.SCENE2D    ) {      offset.range = distanceToBoundingSphere2D(camera, radius);    } else {      offset.range = distanceToBoundingSphere3D(camera, radius);    }    offset.range = CesiumMath.clamp(offset.range, minimumZoom, maximumZoom);  }  return offset;}/** * Sets the camera so that the current view contains the provided bounding sphere. * * <p>The offset is heading/pitch/range in the local east-north-up reference frame centered at the center of the bounding sphere. * The heading and the pitch angles are defined in the local east-north-up reference frame. * The heading is the angle from y axis and increasing towards the x axis. Pitch is the rotation from the xy-plane. Positive pitch * angles are below the plane. Negative pitch angles are above the plane. The range is the distance from the center. If the range is * zero, a range will be computed such that the whole bounding sphere is visible.</p> * * <p>In 2D, there must be a top down view. The camera will be placed above the target looking down. The height above the * target will be the range. The heading will be determined from the offset. If the heading cannot be * determined from the offset, the heading will be north.</p> * * @param {BoundingSphere} boundingSphere The bounding sphere to view, in world coordinates. * @param {HeadingPitchRange} [offset] The offset from the target in the local east-north-up reference frame centered at the target. * * @exception {DeveloperError} viewBoundingSphere is not supported while morphing. */Camera.prototype.viewBoundingSphere = function (boundingSphere, offset) {  //>>includeStart('debug', pragmas.debug);  if (!defined(boundingSphere)) {    throw new DeveloperError("boundingSphere is required.");  }  if (this._mode === SceneMode.MORPHING) {    throw new DeveloperError(      "viewBoundingSphere is not supported while morphing."    );  }  //>>includeEnd('debug');  offset = adjustBoundingSphereOffset(this, boundingSphere, offset);  this.lookAt(boundingSphere.center, offset);};const scratchflyToBoundingSphereTransform = new Matrix4();const scratchflyToBoundingSphereDestination = new Cartesian3();const scratchflyToBoundingSphereDirection = new Cartesian3();const scratchflyToBoundingSphereUp = new Cartesian3();const scratchflyToBoundingSphereRight = new Cartesian3();const scratchFlyToBoundingSphereCart4 = new Cartesian4();const scratchFlyToBoundingSphereQuaternion = new Quaternion();const scratchFlyToBoundingSphereMatrix3 = new Matrix3();/** * Flies the camera to a location where the current view contains the provided bounding sphere. * * <p> The offset is heading/pitch/range in the local east-north-up reference frame centered at the center of the bounding sphere. * The heading and the pitch angles are defined in the local east-north-up reference frame. * The heading is the angle from y axis and increasing towards the x axis. Pitch is the rotation from the xy-plane. Positive pitch * angles are below the plane. Negative pitch angles are above the plane. The range is the distance from the center. If the range is * zero, a range will be computed such that the whole bounding sphere is visible.</p> * * <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 * target will be the range. The heading will be aligned to local north.</p> * * @param {BoundingSphere} boundingSphere The bounding sphere to view, in world coordinates. * @param {Object} [options] Object with the following properties: * @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. * @param {HeadingPitchRange} [options.offset] The offset from the target in the local east-north-up reference frame centered at the target. * @param {Camera.FlightCompleteCallback} [options.complete] The function to execute when the flight is complete. * @param {Camera.FlightCancelledCallback} [options.cancel] The function to execute if the flight is cancelled. * @param {Matrix4} [options.endTransform] Transform matrix representing the reference frame the camera will be in when the flight is completed. * @param {Number} [options.maximumHeight] The maximum height at the peak of the flight. * @param {Number} [options.pitchAdjustHeight] If camera flyes higher than that value, adjust pitch duiring the flight to look down, and keep Earth in viewport. * @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. * @param {Number} [options.flyOverLongitudeWeight] Fly over the lon specifyed via flyOverLongitude only if that way is not longer than short way times flyOverLongitudeWeight. * @param {EasingFunction.Callback} [options.easingFunction] Controls how the time is interpolated over the duration of the flight. */Camera.prototype.flyToBoundingSphere = function (boundingSphere, options) {  //>>includeStart('debug', pragmas.debug);  if (!defined(boundingSphere)) {    throw new DeveloperError("boundingSphere is required.");  }  //>>includeEnd('debug');  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  const scene2D =    this._mode === SceneMode.SCENE2D || this._mode === SceneMode.COLUMBUS_VIEW;  this._setTransform(Matrix4.IDENTITY);  const offset = adjustBoundingSphereOffset(    this,    boundingSphere,    options.offset  );  let position;  if (scene2D) {    position = Cartesian3.multiplyByScalar(      Cartesian3.UNIT_Z,      offset.range,      scratchflyToBoundingSphereDestination    );  } else {    position = offsetFromHeadingPitchRange(      offset.heading,      offset.pitch,      offset.range    );  }  const transform = Transforms.eastNorthUpToFixedFrame(    boundingSphere.center,    Ellipsoid.WGS84,    scratchflyToBoundingSphereTransform  );  Matrix4.multiplyByPoint(transform, position, position);  let direction;  let up;  if (!scene2D) {    direction = Cartesian3.subtract(      boundingSphere.center,      position,      scratchflyToBoundingSphereDirection    );    Cartesian3.normalize(direction, direction);    up = Matrix4.multiplyByPointAsVector(      transform,      Cartesian3.UNIT_Z,      scratchflyToBoundingSphereUp    );    if (1.0 - Math.abs(Cartesian3.dot(direction, up)) < CesiumMath.EPSILON6) {      const rotateQuat = Quaternion.fromAxisAngle(        direction,        offset.heading,        scratchFlyToBoundingSphereQuaternion      );      const rotation = Matrix3.fromQuaternion(        rotateQuat,        scratchFlyToBoundingSphereMatrix3      );      Cartesian3.fromCartesian4(        Matrix4.getColumn(transform, 1, scratchFlyToBoundingSphereCart4),        up      );      Matrix3.multiplyByVector(rotation, up, up);    }    const right = Cartesian3.cross(      direction,      up,      scratchflyToBoundingSphereRight    );    Cartesian3.cross(right, direction, up);    Cartesian3.normalize(up, up);  }  this.flyTo({    destination: position,    orientation: {      direction: direction,      up: up,    },    duration: options.duration,    complete: options.complete,    cancel: options.cancel,    endTransform: options.endTransform,    maximumHeight: options.maximumHeight,    easingFunction: options.easingFunction,    flyOverLongitude: options.flyOverLongitude,    flyOverLongitudeWeight: options.flyOverLongitudeWeight,    pitchAdjustHeight: options.pitchAdjustHeight,  });};const scratchCartesian3_1 = new Cartesian3();const scratchCartesian3_2 = new Cartesian3();const scratchCartesian3_3 = new Cartesian3();const scratchCartesian3_4 = new Cartesian3();const horizonPoints = [  new Cartesian3(),  new Cartesian3(),  new Cartesian3(),  new Cartesian3(),];function computeHorizonQuad(camera, ellipsoid) {  const radii = ellipsoid.radii;  const p = camera.positionWC;  // Find the corresponding position in the scaled space of the ellipsoid.  const q = Cartesian3.multiplyComponents(    ellipsoid.oneOverRadii,    p,    scratchCartesian3_1  );  const qMagnitude = Cartesian3.magnitude(q);  const qUnit = Cartesian3.normalize(q, scratchCartesian3_2);  // Determine the east and north directions at q.  let eUnit;  let nUnit;  if (    Cartesian3.equalsEpsilon(qUnit, Cartesian3.UNIT_Z, CesiumMath.EPSILON10)  ) {    eUnit = new Cartesian3(0, 1, 0);    nUnit = new Cartesian3(0, 0, 1);  } else {    eUnit = Cartesian3.normalize(      Cartesian3.cross(Cartesian3.UNIT_Z, qUnit, scratchCartesian3_3),      scratchCartesian3_3    );    nUnit = Cartesian3.normalize(      Cartesian3.cross(qUnit, eUnit, scratchCartesian3_4),      scratchCartesian3_4    );  }  // Determine the radius of the 'limb' of the ellipsoid.  const wMagnitude = Math.sqrt(Cartesian3.magnitudeSquared(q) - 1.0);  // Compute the center and offsets.  const center = Cartesian3.multiplyByScalar(    qUnit,    1.0 / qMagnitude,    scratchCartesian3_1  );  const scalar = wMagnitude / qMagnitude;  const eastOffset = Cartesian3.multiplyByScalar(    eUnit,    scalar,    scratchCartesian3_2  );  const northOffset = Cartesian3.multiplyByScalar(    nUnit,    scalar,    scratchCartesian3_3  );  // A conservative measure for the longitudes would be to use the min/max longitudes of the bounding frustum.  const upperLeft = Cartesian3.add(center, northOffset, horizonPoints[0]);  Cartesian3.subtract(upperLeft, eastOffset, upperLeft);  Cartesian3.multiplyComponents(radii, upperLeft, upperLeft);  const lowerLeft = Cartesian3.subtract(center, northOffset, horizonPoints[1]);  Cartesian3.subtract(lowerLeft, eastOffset, lowerLeft);  Cartesian3.multiplyComponents(radii, lowerLeft, lowerLeft);  const lowerRight = Cartesian3.subtract(center, northOffset, horizonPoints[2]);  Cartesian3.add(lowerRight, eastOffset, lowerRight);  Cartesian3.multiplyComponents(radii, lowerRight, lowerRight);  const upperRight = Cartesian3.add(center, northOffset, horizonPoints[3]);  Cartesian3.add(upperRight, eastOffset, upperRight);  Cartesian3.multiplyComponents(radii, upperRight, upperRight);  return horizonPoints;}const scratchPickCartesian2 = new Cartesian2();const scratchRectCartesian = new Cartesian3();const cartoArray = [  new Cartographic(),  new Cartographic(),  new Cartographic(),  new Cartographic(),];function addToResult(x, y, index, camera, ellipsoid, computedHorizonQuad) {  scratchPickCartesian2.x = x;  scratchPickCartesian2.y = y;  const r = camera.pickEllipsoid(    scratchPickCartesian2,    ellipsoid,    scratchRectCartesian  );  if (defined(r)) {    cartoArray[index] = ellipsoid.cartesianToCartographic(r, cartoArray[index]);    return 1;  }  cartoArray[index] = ellipsoid.cartesianToCartographic(    computedHorizonQuad[index],    cartoArray[index]  );  return 0;}/** * Computes the approximate visible rectangle on the ellipsoid. * * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid that you want to know the visible region. * @param {Rectangle} [result] The rectangle in which to store the result * * @returns {Rectangle|undefined} The visible rectangle or undefined if the ellipsoid isn't visible at all. */Camera.prototype.computeViewRectangle = function (ellipsoid, result) {  ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);  const cullingVolume = this.frustum.computeCullingVolume(    this.positionWC,    this.directionWC,    this.upWC  );  const boundingSphere = new BoundingSphere(    Cartesian3.ZERO,    ellipsoid.maximumRadius  );  const visibility = cullingVolume.computeVisibility(boundingSphere);  if (visibility === Intersect.OUTSIDE) {    return undefined;  }  const canvas = this._scene.canvas;  const width = canvas.clientWidth;  const height = canvas.clientHeight;  let successfulPickCount = 0;  const computedHorizonQuad = computeHorizonQuad(this, ellipsoid);  successfulPickCount += addToResult(    0,    0,    0,    this,    ellipsoid,    computedHorizonQuad  );  successfulPickCount += addToResult(    0,    height,    1,    this,    ellipsoid,    computedHorizonQuad  );  successfulPickCount += addToResult(    width,    height,    2,    this,    ellipsoid,    computedHorizonQuad  );  successfulPickCount += addToResult(    width,    0,    3,    this,    ellipsoid,    computedHorizonQuad  );  if (successfulPickCount < 2) {    // If we have space non-globe in 3 or 4 corners then return the whole globe    return Rectangle.MAX_VALUE;  }  result = Rectangle.fromCartographicArray(cartoArray, result);  // Detect if we go over the poles  let distance = 0;  let lastLon = cartoArray[3].longitude;  for (let i = 0; i < 4; ++i) {    const lon = cartoArray[i].longitude;    const diff = Math.abs(lon - lastLon);    if (diff > CesiumMath.PI) {      // Crossed the dateline      distance += CesiumMath.TWO_PI - diff;    } else {      distance += diff;    }    lastLon = lon;  }  // We are over one of the poles so adjust the rectangle accordingly  if (    CesiumMath.equalsEpsilon(      Math.abs(distance),      CesiumMath.TWO_PI,      CesiumMath.EPSILON9    )  ) {    result.west = -CesiumMath.PI;    result.east = CesiumMath.PI;    if (cartoArray[0].latitude >= 0.0) {      result.north = CesiumMath.PI_OVER_TWO;    } else {      result.south = -CesiumMath.PI_OVER_TWO;    }  }  return result;};/** * Switches the frustum/projection to perspective. * * This function is a no-op in 2D which must always be orthographic. */Camera.prototype.switchToPerspectiveFrustum = function () {  if (    this._mode === SceneMode.SCENE2D ||    this.frustum instanceof PerspectiveFrustum  ) {    return;  }  const scene = this._scene;  this.frustum = new PerspectiveFrustum();  this.frustum.aspectRatio =    scene.drawingBufferWidth / scene.drawingBufferHeight;  this.frustum.fov = CesiumMath.toRadians(60.0);};/** * Switches the frustum/projection to orthographic. * * This function is a no-op in 2D which will always be orthographic. */Camera.prototype.switchToOrthographicFrustum = function () {  if (    this._mode === SceneMode.SCENE2D ||    this.frustum instanceof OrthographicFrustum  ) {    return;  }  // This must be called before changing the frustum because it uses the previous  // frustum to reconstruct the world space position from the depth buffer.  const frustumWidth = calculateOrthographicFrustumWidth(this);  const scene = this._scene;  this.frustum = new OrthographicFrustum();  this.frustum.aspectRatio =    scene.drawingBufferWidth / scene.drawingBufferHeight;  this.frustum.width = frustumWidth;};/** * @private */Camera.clone = function (camera, result) {  if (!defined(result)) {    result = new Camera(camera._scene);  }  Cartesian3.clone(camera.position, result.position);  Cartesian3.clone(camera.direction, result.direction);  Cartesian3.clone(camera.up, result.up);  Cartesian3.clone(camera.right, result.right);  Matrix4.clone(camera._transform, result.transform);  result._transformChanged = true;  result.frustum = camera.frustum.clone();  return result;};/** * A function that will execute when a flight completes. * @callback Camera.FlightCompleteCallback *//** * A function that will execute when a flight is cancelled. * @callback Camera.FlightCancelledCallback */export default Camera;
 |