createGroundPolylineGeometry.js 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160
  1. /**
  2. * @license
  3. * Cesium - https://github.com/CesiumGS/cesium
  4. * Version 1.97
  5. *
  6. * Copyright 2011-2022 Cesium Contributors
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. *
  20. * Columbus View (Pat. Pend.)
  21. *
  22. * Portions licensed separately.
  23. * See https://github.com/CesiumGS/cesium/blob/main/LICENSE.md for full licensing details.
  24. */
  25. define(['./Transforms-c78c4637', './Matrix2-ab676047', './RuntimeError-1088cc64', './defaultValue-a6eb9f34', './ComponentDatatype-e06f4e16', './ArcType-b714639b', './arrayRemoveDuplicates-63722a6f', './EllipsoidGeodesic-f7721517', './EllipsoidRhumbLine-34574f75', './EncodedCartesian3-7959a913', './GeometryAttribute-4f02e2ad', './IntersectionTests-f96cd46d', './Plane-c985a1d2', './WebMercatorProjection-0d64470e', './_commonjsHelpers-89c9b271', './combine-7cf28d88', './WebGLConstants-d81b330d'], (function (Transforms, Matrix2, RuntimeError, defaultValue, ComponentDatatype, ArcType, arrayRemoveDuplicates, EllipsoidGeodesic, EllipsoidRhumbLine, EncodedCartesian3, GeometryAttribute, IntersectionTests, Plane, WebMercatorProjection, _commonjsHelpers, combine, WebGLConstants) { 'use strict';
  26. /**
  27. * A tiling scheme for geometry referenced to a simple {@link GeographicProjection} where
  28. * longitude and latitude are directly mapped to X and Y. This projection is commonly
  29. * known as geographic, equirectangular, equidistant cylindrical, or plate carrée.
  30. *
  31. * @alias GeographicTilingScheme
  32. * @constructor
  33. *
  34. * @param {Object} [options] Object with the following properties:
  35. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid whose surface is being tiled. Defaults to
  36. * the WGS84 ellipsoid.
  37. * @param {Rectangle} [options.rectangle=Rectangle.MAX_VALUE] The rectangle, in radians, covered by the tiling scheme.
  38. * @param {Number} [options.numberOfLevelZeroTilesX=2] The number of tiles in the X direction at level zero of
  39. * the tile tree.
  40. * @param {Number} [options.numberOfLevelZeroTilesY=1] The number of tiles in the Y direction at level zero of
  41. * the tile tree.
  42. */
  43. function GeographicTilingScheme(options) {
  44. options = defaultValue.defaultValue(options, defaultValue.defaultValue.EMPTY_OBJECT);
  45. this._ellipsoid = defaultValue.defaultValue(options.ellipsoid, Matrix2.Ellipsoid.WGS84);
  46. this._rectangle = defaultValue.defaultValue(options.rectangle, Matrix2.Rectangle.MAX_VALUE);
  47. this._projection = new Transforms.GeographicProjection(this._ellipsoid);
  48. this._numberOfLevelZeroTilesX = defaultValue.defaultValue(
  49. options.numberOfLevelZeroTilesX,
  50. 2
  51. );
  52. this._numberOfLevelZeroTilesY = defaultValue.defaultValue(
  53. options.numberOfLevelZeroTilesY,
  54. 1
  55. );
  56. }
  57. Object.defineProperties(GeographicTilingScheme.prototype, {
  58. /**
  59. * Gets the ellipsoid that is tiled by this tiling scheme.
  60. * @memberof GeographicTilingScheme.prototype
  61. * @type {Ellipsoid}
  62. */
  63. ellipsoid: {
  64. get: function () {
  65. return this._ellipsoid;
  66. },
  67. },
  68. /**
  69. * Gets the rectangle, in radians, covered by this tiling scheme.
  70. * @memberof GeographicTilingScheme.prototype
  71. * @type {Rectangle}
  72. */
  73. rectangle: {
  74. get: function () {
  75. return this._rectangle;
  76. },
  77. },
  78. /**
  79. * Gets the map projection used by this tiling scheme.
  80. * @memberof GeographicTilingScheme.prototype
  81. * @type {MapProjection}
  82. */
  83. projection: {
  84. get: function () {
  85. return this._projection;
  86. },
  87. },
  88. });
  89. /**
  90. * Gets the total number of tiles in the X direction at a specified level-of-detail.
  91. *
  92. * @param {Number} level The level-of-detail.
  93. * @returns {Number} The number of tiles in the X direction at the given level.
  94. */
  95. GeographicTilingScheme.prototype.getNumberOfXTilesAtLevel = function (level) {
  96. return this._numberOfLevelZeroTilesX << level;
  97. };
  98. /**
  99. * Gets the total number of tiles in the Y direction at a specified level-of-detail.
  100. *
  101. * @param {Number} level The level-of-detail.
  102. * @returns {Number} The number of tiles in the Y direction at the given level.
  103. */
  104. GeographicTilingScheme.prototype.getNumberOfYTilesAtLevel = function (level) {
  105. return this._numberOfLevelZeroTilesY << level;
  106. };
  107. /**
  108. * Transforms a rectangle specified in geodetic radians to the native coordinate system
  109. * of this tiling scheme.
  110. *
  111. * @param {Rectangle} rectangle The rectangle to transform.
  112. * @param {Rectangle} [result] The instance to which to copy the result, or undefined if a new instance
  113. * should be created.
  114. * @returns {Rectangle} The specified 'result', or a new object containing the native rectangle if 'result'
  115. * is undefined.
  116. */
  117. GeographicTilingScheme.prototype.rectangleToNativeRectangle = function (
  118. rectangle,
  119. result
  120. ) {
  121. //>>includeStart('debug', pragmas.debug);
  122. RuntimeError.Check.defined("rectangle", rectangle);
  123. //>>includeEnd('debug');
  124. const west = ComponentDatatype.CesiumMath.toDegrees(rectangle.west);
  125. const south = ComponentDatatype.CesiumMath.toDegrees(rectangle.south);
  126. const east = ComponentDatatype.CesiumMath.toDegrees(rectangle.east);
  127. const north = ComponentDatatype.CesiumMath.toDegrees(rectangle.north);
  128. if (!defaultValue.defined(result)) {
  129. return new Matrix2.Rectangle(west, south, east, north);
  130. }
  131. result.west = west;
  132. result.south = south;
  133. result.east = east;
  134. result.north = north;
  135. return result;
  136. };
  137. /**
  138. * Converts tile x, y coordinates and level to a rectangle expressed in the native coordinates
  139. * of the tiling scheme.
  140. *
  141. * @param {Number} x The integer x coordinate of the tile.
  142. * @param {Number} y The integer y coordinate of the tile.
  143. * @param {Number} level The tile level-of-detail. Zero is the least detailed.
  144. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance
  145. * should be created.
  146. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle
  147. * if 'result' is undefined.
  148. */
  149. GeographicTilingScheme.prototype.tileXYToNativeRectangle = function (
  150. x,
  151. y,
  152. level,
  153. result
  154. ) {
  155. const rectangleRadians = this.tileXYToRectangle(x, y, level, result);
  156. rectangleRadians.west = ComponentDatatype.CesiumMath.toDegrees(rectangleRadians.west);
  157. rectangleRadians.south = ComponentDatatype.CesiumMath.toDegrees(rectangleRadians.south);
  158. rectangleRadians.east = ComponentDatatype.CesiumMath.toDegrees(rectangleRadians.east);
  159. rectangleRadians.north = ComponentDatatype.CesiumMath.toDegrees(rectangleRadians.north);
  160. return rectangleRadians;
  161. };
  162. /**
  163. * Converts tile x, y coordinates and level to a cartographic rectangle in radians.
  164. *
  165. * @param {Number} x The integer x coordinate of the tile.
  166. * @param {Number} y The integer y coordinate of the tile.
  167. * @param {Number} level The tile level-of-detail. Zero is the least detailed.
  168. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance
  169. * should be created.
  170. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle
  171. * if 'result' is undefined.
  172. */
  173. GeographicTilingScheme.prototype.tileXYToRectangle = function (
  174. x,
  175. y,
  176. level,
  177. result
  178. ) {
  179. const rectangle = this._rectangle;
  180. const xTiles = this.getNumberOfXTilesAtLevel(level);
  181. const yTiles = this.getNumberOfYTilesAtLevel(level);
  182. const xTileWidth = rectangle.width / xTiles;
  183. const west = x * xTileWidth + rectangle.west;
  184. const east = (x + 1) * xTileWidth + rectangle.west;
  185. const yTileHeight = rectangle.height / yTiles;
  186. const north = rectangle.north - y * yTileHeight;
  187. const south = rectangle.north - (y + 1) * yTileHeight;
  188. if (!defaultValue.defined(result)) {
  189. result = new Matrix2.Rectangle(west, south, east, north);
  190. }
  191. result.west = west;
  192. result.south = south;
  193. result.east = east;
  194. result.north = north;
  195. return result;
  196. };
  197. /**
  198. * Calculates the tile x, y coordinates of the tile containing
  199. * a given cartographic position.
  200. *
  201. * @param {Cartographic} position The position.
  202. * @param {Number} level The tile level-of-detail. Zero is the least detailed.
  203. * @param {Cartesian2} [result] The instance to which to copy the result, or undefined if a new instance
  204. * should be created.
  205. * @returns {Cartesian2} The specified 'result', or a new object containing the tile x, y coordinates
  206. * if 'result' is undefined.
  207. */
  208. GeographicTilingScheme.prototype.positionToTileXY = function (
  209. position,
  210. level,
  211. result
  212. ) {
  213. const rectangle = this._rectangle;
  214. if (!Matrix2.Rectangle.contains(rectangle, position)) {
  215. // outside the bounds of the tiling scheme
  216. return undefined;
  217. }
  218. const xTiles = this.getNumberOfXTilesAtLevel(level);
  219. const yTiles = this.getNumberOfYTilesAtLevel(level);
  220. const xTileWidth = rectangle.width / xTiles;
  221. const yTileHeight = rectangle.height / yTiles;
  222. let longitude = position.longitude;
  223. if (rectangle.east < rectangle.west) {
  224. longitude += ComponentDatatype.CesiumMath.TWO_PI;
  225. }
  226. let xTileCoordinate = ((longitude - rectangle.west) / xTileWidth) | 0;
  227. if (xTileCoordinate >= xTiles) {
  228. xTileCoordinate = xTiles - 1;
  229. }
  230. let yTileCoordinate =
  231. ((rectangle.north - position.latitude) / yTileHeight) | 0;
  232. if (yTileCoordinate >= yTiles) {
  233. yTileCoordinate = yTiles - 1;
  234. }
  235. if (!defaultValue.defined(result)) {
  236. return new Matrix2.Cartesian2(xTileCoordinate, yTileCoordinate);
  237. }
  238. result.x = xTileCoordinate;
  239. result.y = yTileCoordinate;
  240. return result;
  241. };
  242. const scratchDiagonalCartesianNE = new Matrix2.Cartesian3();
  243. const scratchDiagonalCartesianSW = new Matrix2.Cartesian3();
  244. const scratchDiagonalCartographic = new Matrix2.Cartographic();
  245. const scratchCenterCartesian = new Matrix2.Cartesian3();
  246. const scratchSurfaceCartesian = new Matrix2.Cartesian3();
  247. const scratchBoundingSphere = new Transforms.BoundingSphere();
  248. const tilingScheme = new GeographicTilingScheme();
  249. const scratchCorners = [
  250. new Matrix2.Cartographic(),
  251. new Matrix2.Cartographic(),
  252. new Matrix2.Cartographic(),
  253. new Matrix2.Cartographic(),
  254. ];
  255. const scratchTileXY = new Matrix2.Cartesian2();
  256. /**
  257. * A collection of functions for approximating terrain height
  258. * @private
  259. */
  260. const ApproximateTerrainHeights = {};
  261. /**
  262. * Initializes the minimum and maximum terrain heights
  263. * @return {Promise.<void>}
  264. */
  265. ApproximateTerrainHeights.initialize = function () {
  266. let initPromise = ApproximateTerrainHeights._initPromise;
  267. if (defaultValue.defined(initPromise)) {
  268. return initPromise;
  269. }
  270. initPromise = Transforms.Resource.fetchJson(
  271. Transforms.buildModuleUrl("Assets/approximateTerrainHeights.json")
  272. ).then(function (json) {
  273. ApproximateTerrainHeights._terrainHeights = json;
  274. });
  275. ApproximateTerrainHeights._initPromise = initPromise;
  276. return initPromise;
  277. };
  278. /**
  279. * Computes the minimum and maximum terrain heights for a given rectangle
  280. * @param {Rectangle} rectangle The bounding rectangle
  281. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid
  282. * @return {{minimumTerrainHeight: Number, maximumTerrainHeight: Number}}
  283. */
  284. ApproximateTerrainHeights.getMinimumMaximumHeights = function (
  285. rectangle,
  286. ellipsoid
  287. ) {
  288. //>>includeStart('debug', pragmas.debug);
  289. RuntimeError.Check.defined("rectangle", rectangle);
  290. if (!defaultValue.defined(ApproximateTerrainHeights._terrainHeights)) {
  291. throw new RuntimeError.DeveloperError(
  292. "You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function"
  293. );
  294. }
  295. //>>includeEnd('debug');
  296. ellipsoid = defaultValue.defaultValue(ellipsoid, Matrix2.Ellipsoid.WGS84);
  297. const xyLevel = getTileXYLevel(rectangle);
  298. // Get the terrain min/max for that tile
  299. let minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
  300. let maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
  301. if (defaultValue.defined(xyLevel)) {
  302. const key = `${xyLevel.level}-${xyLevel.x}-${xyLevel.y}`;
  303. const heights = ApproximateTerrainHeights._terrainHeights[key];
  304. if (defaultValue.defined(heights)) {
  305. minTerrainHeight = heights[0];
  306. maxTerrainHeight = heights[1];
  307. }
  308. // Compute min by taking the center of the NE->SW diagonal and finding distance to the surface
  309. ellipsoid.cartographicToCartesian(
  310. Matrix2.Rectangle.northeast(rectangle, scratchDiagonalCartographic),
  311. scratchDiagonalCartesianNE
  312. );
  313. ellipsoid.cartographicToCartesian(
  314. Matrix2.Rectangle.southwest(rectangle, scratchDiagonalCartographic),
  315. scratchDiagonalCartesianSW
  316. );
  317. Matrix2.Cartesian3.midpoint(
  318. scratchDiagonalCartesianSW,
  319. scratchDiagonalCartesianNE,
  320. scratchCenterCartesian
  321. );
  322. const surfacePosition = ellipsoid.scaleToGeodeticSurface(
  323. scratchCenterCartesian,
  324. scratchSurfaceCartesian
  325. );
  326. if (defaultValue.defined(surfacePosition)) {
  327. const distance = Matrix2.Cartesian3.distance(
  328. scratchCenterCartesian,
  329. surfacePosition
  330. );
  331. minTerrainHeight = Math.min(minTerrainHeight, -distance);
  332. } else {
  333. minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
  334. }
  335. }
  336. minTerrainHeight = Math.max(
  337. ApproximateTerrainHeights._defaultMinTerrainHeight,
  338. minTerrainHeight
  339. );
  340. return {
  341. minimumTerrainHeight: minTerrainHeight,
  342. maximumTerrainHeight: maxTerrainHeight,
  343. };
  344. };
  345. /**
  346. * Computes the bounding sphere based on the tile heights in the rectangle
  347. * @param {Rectangle} rectangle The bounding rectangle
  348. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid
  349. * @return {BoundingSphere} The result bounding sphere
  350. */
  351. ApproximateTerrainHeights.getBoundingSphere = function (rectangle, ellipsoid) {
  352. //>>includeStart('debug', pragmas.debug);
  353. RuntimeError.Check.defined("rectangle", rectangle);
  354. if (!defaultValue.defined(ApproximateTerrainHeights._terrainHeights)) {
  355. throw new RuntimeError.DeveloperError(
  356. "You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function"
  357. );
  358. }
  359. //>>includeEnd('debug');
  360. ellipsoid = defaultValue.defaultValue(ellipsoid, Matrix2.Ellipsoid.WGS84);
  361. const xyLevel = getTileXYLevel(rectangle);
  362. // Get the terrain max for that tile
  363. let maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
  364. if (defaultValue.defined(xyLevel)) {
  365. const key = `${xyLevel.level}-${xyLevel.x}-${xyLevel.y}`;
  366. const heights = ApproximateTerrainHeights._terrainHeights[key];
  367. if (defaultValue.defined(heights)) {
  368. maxTerrainHeight = heights[1];
  369. }
  370. }
  371. const result = Transforms.BoundingSphere.fromRectangle3D(rectangle, ellipsoid, 0.0);
  372. Transforms.BoundingSphere.fromRectangle3D(
  373. rectangle,
  374. ellipsoid,
  375. maxTerrainHeight,
  376. scratchBoundingSphere
  377. );
  378. return Transforms.BoundingSphere.union(result, scratchBoundingSphere, result);
  379. };
  380. function getTileXYLevel(rectangle) {
  381. Matrix2.Cartographic.fromRadians(
  382. rectangle.east,
  383. rectangle.north,
  384. 0.0,
  385. scratchCorners[0]
  386. );
  387. Matrix2.Cartographic.fromRadians(
  388. rectangle.west,
  389. rectangle.north,
  390. 0.0,
  391. scratchCorners[1]
  392. );
  393. Matrix2.Cartographic.fromRadians(
  394. rectangle.east,
  395. rectangle.south,
  396. 0.0,
  397. scratchCorners[2]
  398. );
  399. Matrix2.Cartographic.fromRadians(
  400. rectangle.west,
  401. rectangle.south,
  402. 0.0,
  403. scratchCorners[3]
  404. );
  405. // Determine which tile the bounding rectangle is in
  406. let lastLevelX = 0,
  407. lastLevelY = 0;
  408. let currentX = 0,
  409. currentY = 0;
  410. const maxLevel = ApproximateTerrainHeights._terrainHeightsMaxLevel;
  411. let i;
  412. for (i = 0; i <= maxLevel; ++i) {
  413. let failed = false;
  414. for (let j = 0; j < 4; ++j) {
  415. const corner = scratchCorners[j];
  416. tilingScheme.positionToTileXY(corner, i, scratchTileXY);
  417. if (j === 0) {
  418. currentX = scratchTileXY.x;
  419. currentY = scratchTileXY.y;
  420. } else if (currentX !== scratchTileXY.x || currentY !== scratchTileXY.y) {
  421. failed = true;
  422. break;
  423. }
  424. }
  425. if (failed) {
  426. break;
  427. }
  428. lastLevelX = currentX;
  429. lastLevelY = currentY;
  430. }
  431. if (i === 0) {
  432. return undefined;
  433. }
  434. return {
  435. x: lastLevelX,
  436. y: lastLevelY,
  437. level: i > maxLevel ? maxLevel : i - 1,
  438. };
  439. }
  440. ApproximateTerrainHeights._terrainHeightsMaxLevel = 6;
  441. ApproximateTerrainHeights._defaultMaxTerrainHeight = 9000.0;
  442. ApproximateTerrainHeights._defaultMinTerrainHeight = -100000.0;
  443. ApproximateTerrainHeights._terrainHeights = undefined;
  444. ApproximateTerrainHeights._initPromise = undefined;
  445. Object.defineProperties(ApproximateTerrainHeights, {
  446. /**
  447. * Determines if the terrain heights are initialized and ready to use. To initialize the terrain heights,
  448. * call {@link ApproximateTerrainHeights#initialize} and wait for the returned promise to resolve.
  449. * @type {Boolean}
  450. * @readonly
  451. * @memberof ApproximateTerrainHeights
  452. */
  453. initialized: {
  454. get: function () {
  455. return defaultValue.defined(ApproximateTerrainHeights._terrainHeights);
  456. },
  457. },
  458. });
  459. var ApproximateTerrainHeights$1 = ApproximateTerrainHeights;
  460. const PROJECTIONS = [Transforms.GeographicProjection, WebMercatorProjection.WebMercatorProjection];
  461. const PROJECTION_COUNT = PROJECTIONS.length;
  462. const MITER_BREAK_SMALL = Math.cos(ComponentDatatype.CesiumMath.toRadians(30.0));
  463. const MITER_BREAK_LARGE = Math.cos(ComponentDatatype.CesiumMath.toRadians(150.0));
  464. // Initial heights for constructing the wall.
  465. // Keeping WALL_INITIAL_MIN_HEIGHT near the ellipsoid surface helps
  466. // prevent precision problems with planes in the shader.
  467. // Putting the start point of a plane at ApproximateTerrainHeights._defaultMinTerrainHeight,
  468. // which is a highly conservative bound, usually puts the plane origin several thousands
  469. // of meters away from the actual terrain, causing floating point problems when checking
  470. // fragments on terrain against the plane.
  471. // Ellipsoid height is generally much closer.
  472. // The initial max height is arbitrary.
  473. // Both heights are corrected using ApproximateTerrainHeights for computing the actual volume geometry.
  474. const WALL_INITIAL_MIN_HEIGHT = 0.0;
  475. const WALL_INITIAL_MAX_HEIGHT = 1000.0;
  476. /**
  477. * A description of a polyline on terrain or 3D Tiles. Only to be used with {@link GroundPolylinePrimitive}.
  478. *
  479. * @alias GroundPolylineGeometry
  480. * @constructor
  481. *
  482. * @param {Object} options Options with the following properties:
  483. * @param {Cartesian3[]} options.positions An array of {@link Cartesian3} defining the polyline's points. Heights above the ellipsoid will be ignored.
  484. * @param {Number} [options.width=1.0] The screen space width in pixels.
  485. * @param {Number} [options.granularity=9999.0] The distance interval in meters used for interpolating options.points. Defaults to 9999.0 meters. Zero indicates no interpolation.
  486. * @param {Boolean} [options.loop=false] Whether during geometry creation a line segment will be added between the last and first line positions to make this Polyline a loop.
  487. * @param {ArcType} [options.arcType=ArcType.GEODESIC] The type of line the polyline segments must follow. Valid options are {@link ArcType.GEODESIC} and {@link ArcType.RHUMB}.
  488. *
  489. * @exception {DeveloperError} At least two positions are required.
  490. *
  491. * @see GroundPolylinePrimitive
  492. *
  493. * @example
  494. * const positions = Cesium.Cartesian3.fromDegreesArray([
  495. * -112.1340164450331, 36.05494287836128,
  496. * -112.08821010582645, 36.097804071380715,
  497. * -112.13296079730024, 36.168769146801104
  498. * ]);
  499. *
  500. * const geometry = new Cesium.GroundPolylineGeometry({
  501. * positions : positions
  502. * });
  503. */
  504. function GroundPolylineGeometry(options) {
  505. options = defaultValue.defaultValue(options, defaultValue.defaultValue.EMPTY_OBJECT);
  506. const positions = options.positions;
  507. //>>includeStart('debug', pragmas.debug);
  508. if (!defaultValue.defined(positions) || positions.length < 2) {
  509. throw new RuntimeError.DeveloperError("At least two positions are required.");
  510. }
  511. if (
  512. defaultValue.defined(options.arcType) &&
  513. options.arcType !== ArcType.ArcType.GEODESIC &&
  514. options.arcType !== ArcType.ArcType.RHUMB
  515. ) {
  516. throw new RuntimeError.DeveloperError(
  517. "Valid options for arcType are ArcType.GEODESIC and ArcType.RHUMB."
  518. );
  519. }
  520. //>>includeEnd('debug');
  521. /**
  522. * The screen space width in pixels.
  523. * @type {Number}
  524. */
  525. this.width = defaultValue.defaultValue(options.width, 1.0); // Doesn't get packed, not necessary for computing geometry.
  526. this._positions = positions;
  527. /**
  528. * The distance interval used for interpolating options.points. Zero indicates no interpolation.
  529. * Default of 9999.0 allows centimeter accuracy with 32 bit floating point.
  530. * @type {Boolean}
  531. * @default 9999.0
  532. */
  533. this.granularity = defaultValue.defaultValue(options.granularity, 9999.0);
  534. /**
  535. * Whether during geometry creation a line segment will be added between the last and first line positions to make this Polyline a loop.
  536. * If the geometry has two positions this parameter will be ignored.
  537. * @type {Boolean}
  538. * @default false
  539. */
  540. this.loop = defaultValue.defaultValue(options.loop, false);
  541. /**
  542. * The type of path the polyline must follow. Valid options are {@link ArcType.GEODESIC} and {@link ArcType.RHUMB}.
  543. * @type {ArcType}
  544. * @default ArcType.GEODESIC
  545. */
  546. this.arcType = defaultValue.defaultValue(options.arcType, ArcType.ArcType.GEODESIC);
  547. this._ellipsoid = Matrix2.Ellipsoid.WGS84;
  548. // MapProjections can't be packed, so store the index to a known MapProjection.
  549. this._projectionIndex = 0;
  550. this._workerName = "createGroundPolylineGeometry";
  551. // Used by GroundPolylinePrimitive to signal worker that scenemode is 3D only.
  552. this._scene3DOnly = false;
  553. }
  554. Object.defineProperties(GroundPolylineGeometry.prototype, {
  555. /**
  556. * The number of elements used to pack the object into an array.
  557. * @memberof GroundPolylineGeometry.prototype
  558. * @type {Number}
  559. * @readonly
  560. * @private
  561. */
  562. packedLength: {
  563. get: function () {
  564. return (
  565. 1.0 +
  566. this._positions.length * 3 +
  567. 1.0 +
  568. 1.0 +
  569. 1.0 +
  570. Matrix2.Ellipsoid.packedLength +
  571. 1.0 +
  572. 1.0
  573. );
  574. },
  575. },
  576. });
  577. /**
  578. * Set the GroundPolylineGeometry's projection and ellipsoid.
  579. * Used by GroundPolylinePrimitive to signal scene information to the geometry for generating 2D attributes.
  580. *
  581. * @param {GroundPolylineGeometry} groundPolylineGeometry GroundPolylinGeometry describing a polyline on terrain or 3D Tiles.
  582. * @param {Projection} mapProjection A MapProjection used for projecting cartographic coordinates to 2D.
  583. * @private
  584. */
  585. GroundPolylineGeometry.setProjectionAndEllipsoid = function (
  586. groundPolylineGeometry,
  587. mapProjection
  588. ) {
  589. let projectionIndex = 0;
  590. for (let i = 0; i < PROJECTION_COUNT; i++) {
  591. if (mapProjection instanceof PROJECTIONS[i]) {
  592. projectionIndex = i;
  593. break;
  594. }
  595. }
  596. groundPolylineGeometry._projectionIndex = projectionIndex;
  597. groundPolylineGeometry._ellipsoid = mapProjection.ellipsoid;
  598. };
  599. const cart3Scratch1 = new Matrix2.Cartesian3();
  600. const cart3Scratch2 = new Matrix2.Cartesian3();
  601. const cart3Scratch3 = new Matrix2.Cartesian3();
  602. function computeRightNormal(start, end, maxHeight, ellipsoid, result) {
  603. const startBottom = getPosition(ellipsoid, start, 0.0, cart3Scratch1);
  604. const startTop = getPosition(ellipsoid, start, maxHeight, cart3Scratch2);
  605. const endBottom = getPosition(ellipsoid, end, 0.0, cart3Scratch3);
  606. const up = direction(startTop, startBottom, cart3Scratch2);
  607. const forward = direction(endBottom, startBottom, cart3Scratch3);
  608. Matrix2.Cartesian3.cross(forward, up, result);
  609. return Matrix2.Cartesian3.normalize(result, result);
  610. }
  611. const interpolatedCartographicScratch = new Matrix2.Cartographic();
  612. const interpolatedBottomScratch = new Matrix2.Cartesian3();
  613. const interpolatedTopScratch = new Matrix2.Cartesian3();
  614. const interpolatedNormalScratch = new Matrix2.Cartesian3();
  615. function interpolateSegment(
  616. start,
  617. end,
  618. minHeight,
  619. maxHeight,
  620. granularity,
  621. arcType,
  622. ellipsoid,
  623. normalsArray,
  624. bottomPositionsArray,
  625. topPositionsArray,
  626. cartographicsArray
  627. ) {
  628. if (granularity === 0.0) {
  629. return;
  630. }
  631. let ellipsoidLine;
  632. if (arcType === ArcType.ArcType.GEODESIC) {
  633. ellipsoidLine = new EllipsoidGeodesic.EllipsoidGeodesic(start, end, ellipsoid);
  634. } else if (arcType === ArcType.ArcType.RHUMB) {
  635. ellipsoidLine = new EllipsoidRhumbLine.EllipsoidRhumbLine(start, end, ellipsoid);
  636. }
  637. const surfaceDistance = ellipsoidLine.surfaceDistance;
  638. if (surfaceDistance < granularity) {
  639. return;
  640. }
  641. // Compute rightwards normal applicable at all interpolated points
  642. const interpolatedNormal = computeRightNormal(
  643. start,
  644. end,
  645. maxHeight,
  646. ellipsoid,
  647. interpolatedNormalScratch
  648. );
  649. const segments = Math.ceil(surfaceDistance / granularity);
  650. const interpointDistance = surfaceDistance / segments;
  651. let distanceFromStart = interpointDistance;
  652. const pointsToAdd = segments - 1;
  653. let packIndex = normalsArray.length;
  654. for (let i = 0; i < pointsToAdd; i++) {
  655. const interpolatedCartographic = ellipsoidLine.interpolateUsingSurfaceDistance(
  656. distanceFromStart,
  657. interpolatedCartographicScratch
  658. );
  659. const interpolatedBottom = getPosition(
  660. ellipsoid,
  661. interpolatedCartographic,
  662. minHeight,
  663. interpolatedBottomScratch
  664. );
  665. const interpolatedTop = getPosition(
  666. ellipsoid,
  667. interpolatedCartographic,
  668. maxHeight,
  669. interpolatedTopScratch
  670. );
  671. Matrix2.Cartesian3.pack(interpolatedNormal, normalsArray, packIndex);
  672. Matrix2.Cartesian3.pack(interpolatedBottom, bottomPositionsArray, packIndex);
  673. Matrix2.Cartesian3.pack(interpolatedTop, topPositionsArray, packIndex);
  674. cartographicsArray.push(interpolatedCartographic.latitude);
  675. cartographicsArray.push(interpolatedCartographic.longitude);
  676. packIndex += 3;
  677. distanceFromStart += interpointDistance;
  678. }
  679. }
  680. const heightlessCartographicScratch = new Matrix2.Cartographic();
  681. function getPosition(ellipsoid, cartographic, height, result) {
  682. Matrix2.Cartographic.clone(cartographic, heightlessCartographicScratch);
  683. heightlessCartographicScratch.height = height;
  684. return Matrix2.Cartographic.toCartesian(
  685. heightlessCartographicScratch,
  686. ellipsoid,
  687. result
  688. );
  689. }
  690. /**
  691. * Stores the provided instance into the provided array.
  692. *
  693. * @param {PolygonGeometry} value The value to pack.
  694. * @param {Number[]} array The array to pack into.
  695. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements.
  696. *
  697. * @returns {Number[]} The array that was packed into
  698. */
  699. GroundPolylineGeometry.pack = function (value, array, startingIndex) {
  700. //>>includeStart('debug', pragmas.debug);
  701. RuntimeError.Check.typeOf.object("value", value);
  702. RuntimeError.Check.defined("array", array);
  703. //>>includeEnd('debug');
  704. let index = defaultValue.defaultValue(startingIndex, 0);
  705. const positions = value._positions;
  706. const positionsLength = positions.length;
  707. array[index++] = positionsLength;
  708. for (let i = 0; i < positionsLength; ++i) {
  709. const cartesian = positions[i];
  710. Matrix2.Cartesian3.pack(cartesian, array, index);
  711. index += 3;
  712. }
  713. array[index++] = value.granularity;
  714. array[index++] = value.loop ? 1.0 : 0.0;
  715. array[index++] = value.arcType;
  716. Matrix2.Ellipsoid.pack(value._ellipsoid, array, index);
  717. index += Matrix2.Ellipsoid.packedLength;
  718. array[index++] = value._projectionIndex;
  719. array[index++] = value._scene3DOnly ? 1.0 : 0.0;
  720. return array;
  721. };
  722. /**
  723. * Retrieves an instance from a packed array.
  724. *
  725. * @param {Number[]} array The packed array.
  726. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked.
  727. * @param {PolygonGeometry} [result] The object into which to store the result.
  728. */
  729. GroundPolylineGeometry.unpack = function (array, startingIndex, result) {
  730. //>>includeStart('debug', pragmas.debug);
  731. RuntimeError.Check.defined("array", array);
  732. //>>includeEnd('debug');
  733. let index = defaultValue.defaultValue(startingIndex, 0);
  734. const positionsLength = array[index++];
  735. const positions = new Array(positionsLength);
  736. for (let i = 0; i < positionsLength; i++) {
  737. positions[i] = Matrix2.Cartesian3.unpack(array, index);
  738. index += 3;
  739. }
  740. const granularity = array[index++];
  741. const loop = array[index++] === 1.0;
  742. const arcType = array[index++];
  743. const ellipsoid = Matrix2.Ellipsoid.unpack(array, index);
  744. index += Matrix2.Ellipsoid.packedLength;
  745. const projectionIndex = array[index++];
  746. const scene3DOnly = array[index++] === 1.0;
  747. if (!defaultValue.defined(result)) {
  748. result = new GroundPolylineGeometry({
  749. positions: positions,
  750. });
  751. }
  752. result._positions = positions;
  753. result.granularity = granularity;
  754. result.loop = loop;
  755. result.arcType = arcType;
  756. result._ellipsoid = ellipsoid;
  757. result._projectionIndex = projectionIndex;
  758. result._scene3DOnly = scene3DOnly;
  759. return result;
  760. };
  761. function direction(target, origin, result) {
  762. Matrix2.Cartesian3.subtract(target, origin, result);
  763. Matrix2.Cartesian3.normalize(result, result);
  764. return result;
  765. }
  766. function tangentDirection(target, origin, up, result) {
  767. result = direction(target, origin, result);
  768. // orthogonalize
  769. result = Matrix2.Cartesian3.cross(result, up, result);
  770. result = Matrix2.Cartesian3.normalize(result, result);
  771. result = Matrix2.Cartesian3.cross(up, result, result);
  772. return result;
  773. }
  774. const toPreviousScratch = new Matrix2.Cartesian3();
  775. const toNextScratch = new Matrix2.Cartesian3();
  776. const forwardScratch = new Matrix2.Cartesian3();
  777. const vertexUpScratch = new Matrix2.Cartesian3();
  778. const cosine90 = 0.0;
  779. const cosine180 = -1.0;
  780. function computeVertexMiterNormal(
  781. previousBottom,
  782. vertexBottom,
  783. vertexTop,
  784. nextBottom,
  785. result
  786. ) {
  787. const up = direction(vertexTop, vertexBottom, vertexUpScratch);
  788. // Compute vectors pointing towards neighboring points but tangent to this point on the ellipsoid
  789. const toPrevious = tangentDirection(
  790. previousBottom,
  791. vertexBottom,
  792. up,
  793. toPreviousScratch
  794. );
  795. const toNext = tangentDirection(nextBottom, vertexBottom, up, toNextScratch);
  796. // Check if tangents are almost opposite - if so, no need to miter.
  797. if (
  798. ComponentDatatype.CesiumMath.equalsEpsilon(
  799. Matrix2.Cartesian3.dot(toPrevious, toNext),
  800. cosine180,
  801. ComponentDatatype.CesiumMath.EPSILON5
  802. )
  803. ) {
  804. result = Matrix2.Cartesian3.cross(up, toPrevious, result);
  805. result = Matrix2.Cartesian3.normalize(result, result);
  806. return result;
  807. }
  808. // Average directions to previous and to next in the plane of Up
  809. result = Matrix2.Cartesian3.add(toNext, toPrevious, result);
  810. result = Matrix2.Cartesian3.normalize(result, result);
  811. // Flip the normal if it isn't pointing roughly bound right (aka if forward is pointing more "backwards")
  812. const forward = Matrix2.Cartesian3.cross(up, result, forwardScratch);
  813. if (Matrix2.Cartesian3.dot(toNext, forward) < cosine90) {
  814. result = Matrix2.Cartesian3.negate(result, result);
  815. }
  816. return result;
  817. }
  818. const XZ_PLANE = Plane.Plane.fromPointNormal(Matrix2.Cartesian3.ZERO, Matrix2.Cartesian3.UNIT_Y);
  819. const previousBottomScratch = new Matrix2.Cartesian3();
  820. const vertexBottomScratch = new Matrix2.Cartesian3();
  821. const vertexTopScratch = new Matrix2.Cartesian3();
  822. const nextBottomScratch = new Matrix2.Cartesian3();
  823. const vertexNormalScratch = new Matrix2.Cartesian3();
  824. const intersectionScratch = new Matrix2.Cartesian3();
  825. const cartographicScratch0 = new Matrix2.Cartographic();
  826. const cartographicScratch1 = new Matrix2.Cartographic();
  827. const cartographicIntersectionScratch = new Matrix2.Cartographic();
  828. /**
  829. * Computes shadow volumes for the ground polyline, consisting of its vertices, indices, and a bounding sphere.
  830. * Vertices are "fat," packing all the data needed in each volume to describe a line on terrain or 3D Tiles.
  831. * Should not be called independent of {@link GroundPolylinePrimitive}.
  832. *
  833. * @param {GroundPolylineGeometry} groundPolylineGeometry
  834. * @private
  835. */
  836. GroundPolylineGeometry.createGeometry = function (groundPolylineGeometry) {
  837. const compute2dAttributes = !groundPolylineGeometry._scene3DOnly;
  838. let loop = groundPolylineGeometry.loop;
  839. const ellipsoid = groundPolylineGeometry._ellipsoid;
  840. const granularity = groundPolylineGeometry.granularity;
  841. const arcType = groundPolylineGeometry.arcType;
  842. const projection = new PROJECTIONS[groundPolylineGeometry._projectionIndex](
  843. ellipsoid
  844. );
  845. const minHeight = WALL_INITIAL_MIN_HEIGHT;
  846. const maxHeight = WALL_INITIAL_MAX_HEIGHT;
  847. let index;
  848. let i;
  849. const positions = groundPolylineGeometry._positions;
  850. const positionsLength = positions.length;
  851. if (positionsLength === 2) {
  852. loop = false;
  853. }
  854. // Split positions across the IDL and the Prime Meridian as well.
  855. // Split across prime meridian because very large geometries crossing the Prime Meridian but not the IDL
  856. // may get split by the plane of IDL + Prime Meridian.
  857. let p0;
  858. let p1;
  859. let c0;
  860. let c1;
  861. const rhumbLine = new EllipsoidRhumbLine.EllipsoidRhumbLine(undefined, undefined, ellipsoid);
  862. let intersection;
  863. let intersectionCartographic;
  864. let intersectionLongitude;
  865. const splitPositions = [positions[0]];
  866. for (i = 0; i < positionsLength - 1; i++) {
  867. p0 = positions[i];
  868. p1 = positions[i + 1];
  869. intersection = IntersectionTests.IntersectionTests.lineSegmentPlane(
  870. p0,
  871. p1,
  872. XZ_PLANE,
  873. intersectionScratch
  874. );
  875. if (
  876. defaultValue.defined(intersection) &&
  877. !Matrix2.Cartesian3.equalsEpsilon(intersection, p0, ComponentDatatype.CesiumMath.EPSILON7) &&
  878. !Matrix2.Cartesian3.equalsEpsilon(intersection, p1, ComponentDatatype.CesiumMath.EPSILON7)
  879. ) {
  880. if (groundPolylineGeometry.arcType === ArcType.ArcType.GEODESIC) {
  881. splitPositions.push(Matrix2.Cartesian3.clone(intersection));
  882. } else if (groundPolylineGeometry.arcType === ArcType.ArcType.RHUMB) {
  883. intersectionLongitude = ellipsoid.cartesianToCartographic(
  884. intersection,
  885. cartographicScratch0
  886. ).longitude;
  887. c0 = ellipsoid.cartesianToCartographic(p0, cartographicScratch0);
  888. c1 = ellipsoid.cartesianToCartographic(p1, cartographicScratch1);
  889. rhumbLine.setEndPoints(c0, c1);
  890. intersectionCartographic = rhumbLine.findIntersectionWithLongitude(
  891. intersectionLongitude,
  892. cartographicIntersectionScratch
  893. );
  894. intersection = ellipsoid.cartographicToCartesian(
  895. intersectionCartographic,
  896. intersectionScratch
  897. );
  898. if (
  899. defaultValue.defined(intersection) &&
  900. !Matrix2.Cartesian3.equalsEpsilon(intersection, p0, ComponentDatatype.CesiumMath.EPSILON7) &&
  901. !Matrix2.Cartesian3.equalsEpsilon(intersection, p1, ComponentDatatype.CesiumMath.EPSILON7)
  902. ) {
  903. splitPositions.push(Matrix2.Cartesian3.clone(intersection));
  904. }
  905. }
  906. }
  907. splitPositions.push(p1);
  908. }
  909. if (loop) {
  910. p0 = positions[positionsLength - 1];
  911. p1 = positions[0];
  912. intersection = IntersectionTests.IntersectionTests.lineSegmentPlane(
  913. p0,
  914. p1,
  915. XZ_PLANE,
  916. intersectionScratch
  917. );
  918. if (
  919. defaultValue.defined(intersection) &&
  920. !Matrix2.Cartesian3.equalsEpsilon(intersection, p0, ComponentDatatype.CesiumMath.EPSILON7) &&
  921. !Matrix2.Cartesian3.equalsEpsilon(intersection, p1, ComponentDatatype.CesiumMath.EPSILON7)
  922. ) {
  923. if (groundPolylineGeometry.arcType === ArcType.ArcType.GEODESIC) {
  924. splitPositions.push(Matrix2.Cartesian3.clone(intersection));
  925. } else if (groundPolylineGeometry.arcType === ArcType.ArcType.RHUMB) {
  926. intersectionLongitude = ellipsoid.cartesianToCartographic(
  927. intersection,
  928. cartographicScratch0
  929. ).longitude;
  930. c0 = ellipsoid.cartesianToCartographic(p0, cartographicScratch0);
  931. c1 = ellipsoid.cartesianToCartographic(p1, cartographicScratch1);
  932. rhumbLine.setEndPoints(c0, c1);
  933. intersectionCartographic = rhumbLine.findIntersectionWithLongitude(
  934. intersectionLongitude,
  935. cartographicIntersectionScratch
  936. );
  937. intersection = ellipsoid.cartographicToCartesian(
  938. intersectionCartographic,
  939. intersectionScratch
  940. );
  941. if (
  942. defaultValue.defined(intersection) &&
  943. !Matrix2.Cartesian3.equalsEpsilon(intersection, p0, ComponentDatatype.CesiumMath.EPSILON7) &&
  944. !Matrix2.Cartesian3.equalsEpsilon(intersection, p1, ComponentDatatype.CesiumMath.EPSILON7)
  945. ) {
  946. splitPositions.push(Matrix2.Cartesian3.clone(intersection));
  947. }
  948. }
  949. }
  950. }
  951. let cartographicsLength = splitPositions.length;
  952. let cartographics = new Array(cartographicsLength);
  953. for (i = 0; i < cartographicsLength; i++) {
  954. const cartographic = Matrix2.Cartographic.fromCartesian(
  955. splitPositions[i],
  956. ellipsoid
  957. );
  958. cartographic.height = 0.0;
  959. cartographics[i] = cartographic;
  960. }
  961. cartographics = arrayRemoveDuplicates.arrayRemoveDuplicates(
  962. cartographics,
  963. Matrix2.Cartographic.equalsEpsilon
  964. );
  965. cartographicsLength = cartographics.length;
  966. if (cartographicsLength < 2) {
  967. return undefined;
  968. }
  969. /**** Build heap-side arrays for positions, interpolated cartographics, and normals from which to compute vertices ****/
  970. // We build a "wall" and then decompose it into separately connected component "volumes" because we need a lot
  971. // of information about the wall. Also, this simplifies interpolation.
  972. // Convention: "next" and "end" are locally forward to each segment of the wall,
  973. // and we are computing normals pointing towards the local right side of the vertices in each segment.
  974. const cartographicsArray = [];
  975. const normalsArray = [];
  976. const bottomPositionsArray = [];
  977. const topPositionsArray = [];
  978. let previousBottom = previousBottomScratch;
  979. let vertexBottom = vertexBottomScratch;
  980. let vertexTop = vertexTopScratch;
  981. let nextBottom = nextBottomScratch;
  982. let vertexNormal = vertexNormalScratch;
  983. // First point - either loop or attach a "perpendicular" normal
  984. const startCartographic = cartographics[0];
  985. const nextCartographic = cartographics[1];
  986. const prestartCartographic = cartographics[cartographicsLength - 1];
  987. previousBottom = getPosition(
  988. ellipsoid,
  989. prestartCartographic,
  990. minHeight,
  991. previousBottom
  992. );
  993. nextBottom = getPosition(ellipsoid, nextCartographic, minHeight, nextBottom);
  994. vertexBottom = getPosition(
  995. ellipsoid,
  996. startCartographic,
  997. minHeight,
  998. vertexBottom
  999. );
  1000. vertexTop = getPosition(ellipsoid, startCartographic, maxHeight, vertexTop);
  1001. if (loop) {
  1002. vertexNormal = computeVertexMiterNormal(
  1003. previousBottom,
  1004. vertexBottom,
  1005. vertexTop,
  1006. nextBottom,
  1007. vertexNormal
  1008. );
  1009. } else {
  1010. vertexNormal = computeRightNormal(
  1011. startCartographic,
  1012. nextCartographic,
  1013. maxHeight,
  1014. ellipsoid,
  1015. vertexNormal
  1016. );
  1017. }
  1018. Matrix2.Cartesian3.pack(vertexNormal, normalsArray, 0);
  1019. Matrix2.Cartesian3.pack(vertexBottom, bottomPositionsArray, 0);
  1020. Matrix2.Cartesian3.pack(vertexTop, topPositionsArray, 0);
  1021. cartographicsArray.push(startCartographic.latitude);
  1022. cartographicsArray.push(startCartographic.longitude);
  1023. interpolateSegment(
  1024. startCartographic,
  1025. nextCartographic,
  1026. minHeight,
  1027. maxHeight,
  1028. granularity,
  1029. arcType,
  1030. ellipsoid,
  1031. normalsArray,
  1032. bottomPositionsArray,
  1033. topPositionsArray,
  1034. cartographicsArray
  1035. );
  1036. // All inbetween points
  1037. for (i = 1; i < cartographicsLength - 1; ++i) {
  1038. previousBottom = Matrix2.Cartesian3.clone(vertexBottom, previousBottom);
  1039. vertexBottom = Matrix2.Cartesian3.clone(nextBottom, vertexBottom);
  1040. const vertexCartographic = cartographics[i];
  1041. getPosition(ellipsoid, vertexCartographic, maxHeight, vertexTop);
  1042. getPosition(ellipsoid, cartographics[i + 1], minHeight, nextBottom);
  1043. computeVertexMiterNormal(
  1044. previousBottom,
  1045. vertexBottom,
  1046. vertexTop,
  1047. nextBottom,
  1048. vertexNormal
  1049. );
  1050. index = normalsArray.length;
  1051. Matrix2.Cartesian3.pack(vertexNormal, normalsArray, index);
  1052. Matrix2.Cartesian3.pack(vertexBottom, bottomPositionsArray, index);
  1053. Matrix2.Cartesian3.pack(vertexTop, topPositionsArray, index);
  1054. cartographicsArray.push(vertexCartographic.latitude);
  1055. cartographicsArray.push(vertexCartographic.longitude);
  1056. interpolateSegment(
  1057. cartographics[i],
  1058. cartographics[i + 1],
  1059. minHeight,
  1060. maxHeight,
  1061. granularity,
  1062. arcType,
  1063. ellipsoid,
  1064. normalsArray,
  1065. bottomPositionsArray,
  1066. topPositionsArray,
  1067. cartographicsArray
  1068. );
  1069. }
  1070. // Last point - either loop or attach a normal "perpendicular" to the wall.
  1071. const endCartographic = cartographics[cartographicsLength - 1];
  1072. const preEndCartographic = cartographics[cartographicsLength - 2];
  1073. vertexBottom = getPosition(
  1074. ellipsoid,
  1075. endCartographic,
  1076. minHeight,
  1077. vertexBottom
  1078. );
  1079. vertexTop = getPosition(ellipsoid, endCartographic, maxHeight, vertexTop);
  1080. if (loop) {
  1081. const postEndCartographic = cartographics[0];
  1082. previousBottom = getPosition(
  1083. ellipsoid,
  1084. preEndCartographic,
  1085. minHeight,
  1086. previousBottom
  1087. );
  1088. nextBottom = getPosition(
  1089. ellipsoid,
  1090. postEndCartographic,
  1091. minHeight,
  1092. nextBottom
  1093. );
  1094. vertexNormal = computeVertexMiterNormal(
  1095. previousBottom,
  1096. vertexBottom,
  1097. vertexTop,
  1098. nextBottom,
  1099. vertexNormal
  1100. );
  1101. } else {
  1102. vertexNormal = computeRightNormal(
  1103. preEndCartographic,
  1104. endCartographic,
  1105. maxHeight,
  1106. ellipsoid,
  1107. vertexNormal
  1108. );
  1109. }
  1110. index = normalsArray.length;
  1111. Matrix2.Cartesian3.pack(vertexNormal, normalsArray, index);
  1112. Matrix2.Cartesian3.pack(vertexBottom, bottomPositionsArray, index);
  1113. Matrix2.Cartesian3.pack(vertexTop, topPositionsArray, index);
  1114. cartographicsArray.push(endCartographic.latitude);
  1115. cartographicsArray.push(endCartographic.longitude);
  1116. if (loop) {
  1117. interpolateSegment(
  1118. endCartographic,
  1119. startCartographic,
  1120. minHeight,
  1121. maxHeight,
  1122. granularity,
  1123. arcType,
  1124. ellipsoid,
  1125. normalsArray,
  1126. bottomPositionsArray,
  1127. topPositionsArray,
  1128. cartographicsArray
  1129. );
  1130. index = normalsArray.length;
  1131. for (i = 0; i < 3; ++i) {
  1132. normalsArray[index + i] = normalsArray[i];
  1133. bottomPositionsArray[index + i] = bottomPositionsArray[i];
  1134. topPositionsArray[index + i] = topPositionsArray[i];
  1135. }
  1136. cartographicsArray.push(startCartographic.latitude);
  1137. cartographicsArray.push(startCartographic.longitude);
  1138. }
  1139. return generateGeometryAttributes(
  1140. loop,
  1141. projection,
  1142. bottomPositionsArray,
  1143. topPositionsArray,
  1144. normalsArray,
  1145. cartographicsArray,
  1146. compute2dAttributes
  1147. );
  1148. };
  1149. // If the end normal angle is too steep compared to the direction of the line segment,
  1150. // "break" the miter by rotating the normal 90 degrees around the "up" direction at the point
  1151. // For ultra precision we would want to project into a plane, but in practice this is sufficient.
  1152. const lineDirectionScratch = new Matrix2.Cartesian3();
  1153. const matrix3Scratch = new Matrix2.Matrix3();
  1154. const quaternionScratch = new Transforms.Quaternion();
  1155. function breakMiter(endGeometryNormal, startBottom, endBottom, endTop) {
  1156. const lineDirection = direction(endBottom, startBottom, lineDirectionScratch);
  1157. const dot = Matrix2.Cartesian3.dot(lineDirection, endGeometryNormal);
  1158. if (dot > MITER_BREAK_SMALL || dot < MITER_BREAK_LARGE) {
  1159. const vertexUp = direction(endTop, endBottom, vertexUpScratch);
  1160. const angle =
  1161. dot < MITER_BREAK_LARGE
  1162. ? ComponentDatatype.CesiumMath.PI_OVER_TWO
  1163. : -ComponentDatatype.CesiumMath.PI_OVER_TWO;
  1164. const quaternion = Transforms.Quaternion.fromAxisAngle(
  1165. vertexUp,
  1166. angle,
  1167. quaternionScratch
  1168. );
  1169. const rotationMatrix = Matrix2.Matrix3.fromQuaternion(quaternion, matrix3Scratch);
  1170. Matrix2.Matrix3.multiplyByVector(
  1171. rotationMatrix,
  1172. endGeometryNormal,
  1173. endGeometryNormal
  1174. );
  1175. return true;
  1176. }
  1177. return false;
  1178. }
  1179. const endPosCartographicScratch = new Matrix2.Cartographic();
  1180. const normalStartpointScratch = new Matrix2.Cartesian3();
  1181. const normalEndpointScratch = new Matrix2.Cartesian3();
  1182. function projectNormal(
  1183. projection,
  1184. cartographic,
  1185. normal,
  1186. projectedPosition,
  1187. result
  1188. ) {
  1189. const position = Matrix2.Cartographic.toCartesian(
  1190. cartographic,
  1191. projection._ellipsoid,
  1192. normalStartpointScratch
  1193. );
  1194. let normalEndpoint = Matrix2.Cartesian3.add(position, normal, normalEndpointScratch);
  1195. let flipNormal = false;
  1196. const ellipsoid = projection._ellipsoid;
  1197. let normalEndpointCartographic = ellipsoid.cartesianToCartographic(
  1198. normalEndpoint,
  1199. endPosCartographicScratch
  1200. );
  1201. // If normal crosses the IDL, go the other way and flip the result.
  1202. // In practice this almost never happens because the cartographic start
  1203. // and end points of each segment are "nudged" to be on the same side
  1204. // of the IDL and slightly away from the IDL.
  1205. if (
  1206. Math.abs(cartographic.longitude - normalEndpointCartographic.longitude) >
  1207. ComponentDatatype.CesiumMath.PI_OVER_TWO
  1208. ) {
  1209. flipNormal = true;
  1210. normalEndpoint = Matrix2.Cartesian3.subtract(
  1211. position,
  1212. normal,
  1213. normalEndpointScratch
  1214. );
  1215. normalEndpointCartographic = ellipsoid.cartesianToCartographic(
  1216. normalEndpoint,
  1217. endPosCartographicScratch
  1218. );
  1219. }
  1220. normalEndpointCartographic.height = 0.0;
  1221. const normalEndpointProjected = projection.project(
  1222. normalEndpointCartographic,
  1223. result
  1224. );
  1225. result = Matrix2.Cartesian3.subtract(
  1226. normalEndpointProjected,
  1227. projectedPosition,
  1228. result
  1229. );
  1230. result.z = 0.0;
  1231. result = Matrix2.Cartesian3.normalize(result, result);
  1232. if (flipNormal) {
  1233. Matrix2.Cartesian3.negate(result, result);
  1234. }
  1235. return result;
  1236. }
  1237. const adjustHeightNormalScratch = new Matrix2.Cartesian3();
  1238. const adjustHeightOffsetScratch = new Matrix2.Cartesian3();
  1239. function adjustHeights(
  1240. bottom,
  1241. top,
  1242. minHeight,
  1243. maxHeight,
  1244. adjustHeightBottom,
  1245. adjustHeightTop
  1246. ) {
  1247. // bottom and top should be at WALL_INITIAL_MIN_HEIGHT and WALL_INITIAL_MAX_HEIGHT, respectively
  1248. const adjustHeightNormal = Matrix2.Cartesian3.subtract(
  1249. top,
  1250. bottom,
  1251. adjustHeightNormalScratch
  1252. );
  1253. Matrix2.Cartesian3.normalize(adjustHeightNormal, adjustHeightNormal);
  1254. const distanceForBottom = minHeight - WALL_INITIAL_MIN_HEIGHT;
  1255. let adjustHeightOffset = Matrix2.Cartesian3.multiplyByScalar(
  1256. adjustHeightNormal,
  1257. distanceForBottom,
  1258. adjustHeightOffsetScratch
  1259. );
  1260. Matrix2.Cartesian3.add(bottom, adjustHeightOffset, adjustHeightBottom);
  1261. const distanceForTop = maxHeight - WALL_INITIAL_MAX_HEIGHT;
  1262. adjustHeightOffset = Matrix2.Cartesian3.multiplyByScalar(
  1263. adjustHeightNormal,
  1264. distanceForTop,
  1265. adjustHeightOffsetScratch
  1266. );
  1267. Matrix2.Cartesian3.add(top, adjustHeightOffset, adjustHeightTop);
  1268. }
  1269. const nudgeDirectionScratch = new Matrix2.Cartesian3();
  1270. function nudgeXZ(start, end) {
  1271. const startToXZdistance = Plane.Plane.getPointDistance(XZ_PLANE, start);
  1272. const endToXZdistance = Plane.Plane.getPointDistance(XZ_PLANE, end);
  1273. let offset = nudgeDirectionScratch;
  1274. // Larger epsilon than what's used in GeometryPipeline, a centimeter in world space
  1275. if (ComponentDatatype.CesiumMath.equalsEpsilon(startToXZdistance, 0.0, ComponentDatatype.CesiumMath.EPSILON2)) {
  1276. offset = direction(end, start, offset);
  1277. Matrix2.Cartesian3.multiplyByScalar(offset, ComponentDatatype.CesiumMath.EPSILON2, offset);
  1278. Matrix2.Cartesian3.add(start, offset, start);
  1279. } else if (
  1280. ComponentDatatype.CesiumMath.equalsEpsilon(endToXZdistance, 0.0, ComponentDatatype.CesiumMath.EPSILON2)
  1281. ) {
  1282. offset = direction(start, end, offset);
  1283. Matrix2.Cartesian3.multiplyByScalar(offset, ComponentDatatype.CesiumMath.EPSILON2, offset);
  1284. Matrix2.Cartesian3.add(end, offset, end);
  1285. }
  1286. }
  1287. // "Nudge" cartographic coordinates so start and end are on the same side of the IDL.
  1288. // Nudge amounts are tiny, basically just an IDL flip.
  1289. // Only used for 2D/CV.
  1290. function nudgeCartographic(start, end) {
  1291. const absStartLon = Math.abs(start.longitude);
  1292. const absEndLon = Math.abs(end.longitude);
  1293. if (
  1294. ComponentDatatype.CesiumMath.equalsEpsilon(absStartLon, ComponentDatatype.CesiumMath.PI, ComponentDatatype.CesiumMath.EPSILON11)
  1295. ) {
  1296. const endSign = ComponentDatatype.CesiumMath.sign(end.longitude);
  1297. start.longitude = endSign * (absStartLon - ComponentDatatype.CesiumMath.EPSILON11);
  1298. return 1;
  1299. } else if (
  1300. ComponentDatatype.CesiumMath.equalsEpsilon(absEndLon, ComponentDatatype.CesiumMath.PI, ComponentDatatype.CesiumMath.EPSILON11)
  1301. ) {
  1302. const startSign = ComponentDatatype.CesiumMath.sign(start.longitude);
  1303. end.longitude = startSign * (absEndLon - ComponentDatatype.CesiumMath.EPSILON11);
  1304. return 2;
  1305. }
  1306. return 0;
  1307. }
  1308. const startCartographicScratch = new Matrix2.Cartographic();
  1309. const endCartographicScratch = new Matrix2.Cartographic();
  1310. const segmentStartTopScratch = new Matrix2.Cartesian3();
  1311. const segmentEndTopScratch = new Matrix2.Cartesian3();
  1312. const segmentStartBottomScratch = new Matrix2.Cartesian3();
  1313. const segmentEndBottomScratch = new Matrix2.Cartesian3();
  1314. const segmentStartNormalScratch = new Matrix2.Cartesian3();
  1315. const segmentEndNormalScratch = new Matrix2.Cartesian3();
  1316. const getHeightCartographics = [
  1317. startCartographicScratch,
  1318. endCartographicScratch,
  1319. ];
  1320. const getHeightRectangleScratch = new Matrix2.Rectangle();
  1321. const adjustHeightStartTopScratch = new Matrix2.Cartesian3();
  1322. const adjustHeightEndTopScratch = new Matrix2.Cartesian3();
  1323. const adjustHeightStartBottomScratch = new Matrix2.Cartesian3();
  1324. const adjustHeightEndBottomScratch = new Matrix2.Cartesian3();
  1325. const segmentStart2DScratch = new Matrix2.Cartesian3();
  1326. const segmentEnd2DScratch = new Matrix2.Cartesian3();
  1327. const segmentStartNormal2DScratch = new Matrix2.Cartesian3();
  1328. const segmentEndNormal2DScratch = new Matrix2.Cartesian3();
  1329. const offsetScratch = new Matrix2.Cartesian3();
  1330. const startUpScratch = new Matrix2.Cartesian3();
  1331. const endUpScratch = new Matrix2.Cartesian3();
  1332. const rightScratch = new Matrix2.Cartesian3();
  1333. const startPlaneNormalScratch = new Matrix2.Cartesian3();
  1334. const endPlaneNormalScratch = new Matrix2.Cartesian3();
  1335. const encodeScratch = new EncodedCartesian3.EncodedCartesian3();
  1336. const encodeScratch2D = new EncodedCartesian3.EncodedCartesian3();
  1337. const forwardOffset2DScratch = new Matrix2.Cartesian3();
  1338. const right2DScratch = new Matrix2.Cartesian3();
  1339. const normalNudgeScratch = new Matrix2.Cartesian3();
  1340. const scratchBoundingSpheres = [new Transforms.BoundingSphere(), new Transforms.BoundingSphere()];
  1341. // Winding order is reversed so each segment's volume is inside-out
  1342. const REFERENCE_INDICES = [
  1343. 0,
  1344. 2,
  1345. 1,
  1346. 0,
  1347. 3,
  1348. 2, // right
  1349. 0,
  1350. 7,
  1351. 3,
  1352. 0,
  1353. 4,
  1354. 7, // start
  1355. 0,
  1356. 5,
  1357. 4,
  1358. 0,
  1359. 1,
  1360. 5, // bottom
  1361. 5,
  1362. 7,
  1363. 4,
  1364. 5,
  1365. 6,
  1366. 7, // left
  1367. 5,
  1368. 2,
  1369. 6,
  1370. 5,
  1371. 1,
  1372. 2, // end
  1373. 3,
  1374. 6,
  1375. 2,
  1376. 3,
  1377. 7,
  1378. 6, // top
  1379. ];
  1380. const REFERENCE_INDICES_LENGTH = REFERENCE_INDICES.length;
  1381. // Decompose the "wall" into a series of shadow volumes.
  1382. // Each shadow volume's vertices encode a description of the line it contains,
  1383. // including mitering planes at the end points, a plane along the line itself,
  1384. // and attributes for computing length-wise texture coordinates.
  1385. function generateGeometryAttributes(
  1386. loop,
  1387. projection,
  1388. bottomPositionsArray,
  1389. topPositionsArray,
  1390. normalsArray,
  1391. cartographicsArray,
  1392. compute2dAttributes
  1393. ) {
  1394. let i;
  1395. let index;
  1396. const ellipsoid = projection._ellipsoid;
  1397. // Each segment will have 8 vertices
  1398. const segmentCount = bottomPositionsArray.length / 3 - 1;
  1399. const vertexCount = segmentCount * 8;
  1400. const arraySizeVec4 = vertexCount * 4;
  1401. const indexCount = segmentCount * 36;
  1402. const indices =
  1403. vertexCount > 65535
  1404. ? new Uint32Array(indexCount)
  1405. : new Uint16Array(indexCount);
  1406. const positionsArray = new Float64Array(vertexCount * 3);
  1407. const startHiAndForwardOffsetX = new Float32Array(arraySizeVec4);
  1408. const startLoAndForwardOffsetY = new Float32Array(arraySizeVec4);
  1409. const startNormalAndForwardOffsetZ = new Float32Array(arraySizeVec4);
  1410. const endNormalAndTextureCoordinateNormalizationX = new Float32Array(
  1411. arraySizeVec4
  1412. );
  1413. const rightNormalAndTextureCoordinateNormalizationY = new Float32Array(
  1414. arraySizeVec4
  1415. );
  1416. let startHiLo2D;
  1417. let offsetAndRight2D;
  1418. let startEndNormals2D;
  1419. let texcoordNormalization2D;
  1420. if (compute2dAttributes) {
  1421. startHiLo2D = new Float32Array(arraySizeVec4);
  1422. offsetAndRight2D = new Float32Array(arraySizeVec4);
  1423. startEndNormals2D = new Float32Array(arraySizeVec4);
  1424. texcoordNormalization2D = new Float32Array(vertexCount * 2);
  1425. }
  1426. /*** Compute total lengths for texture coordinate normalization ***/
  1427. // 2D
  1428. const cartographicsLength = cartographicsArray.length / 2;
  1429. let length2D = 0.0;
  1430. const startCartographic = startCartographicScratch;
  1431. startCartographic.height = 0.0;
  1432. const endCartographic = endCartographicScratch;
  1433. endCartographic.height = 0.0;
  1434. let segmentStartCartesian = segmentStartTopScratch;
  1435. let segmentEndCartesian = segmentEndTopScratch;
  1436. if (compute2dAttributes) {
  1437. index = 0;
  1438. for (i = 1; i < cartographicsLength; i++) {
  1439. // Don't clone anything from previous segment b/c possible IDL touch
  1440. startCartographic.latitude = cartographicsArray[index];
  1441. startCartographic.longitude = cartographicsArray[index + 1];
  1442. endCartographic.latitude = cartographicsArray[index + 2];
  1443. endCartographic.longitude = cartographicsArray[index + 3];
  1444. segmentStartCartesian = projection.project(
  1445. startCartographic,
  1446. segmentStartCartesian
  1447. );
  1448. segmentEndCartesian = projection.project(
  1449. endCartographic,
  1450. segmentEndCartesian
  1451. );
  1452. length2D += Matrix2.Cartesian3.distance(
  1453. segmentStartCartesian,
  1454. segmentEndCartesian
  1455. );
  1456. index += 2;
  1457. }
  1458. }
  1459. // 3D
  1460. const positionsLength = topPositionsArray.length / 3;
  1461. segmentEndCartesian = Matrix2.Cartesian3.unpack(
  1462. topPositionsArray,
  1463. 0,
  1464. segmentEndCartesian
  1465. );
  1466. let length3D = 0.0;
  1467. index = 3;
  1468. for (i = 1; i < positionsLength; i++) {
  1469. segmentStartCartesian = Matrix2.Cartesian3.clone(
  1470. segmentEndCartesian,
  1471. segmentStartCartesian
  1472. );
  1473. segmentEndCartesian = Matrix2.Cartesian3.unpack(
  1474. topPositionsArray,
  1475. index,
  1476. segmentEndCartesian
  1477. );
  1478. length3D += Matrix2.Cartesian3.distance(segmentStartCartesian, segmentEndCartesian);
  1479. index += 3;
  1480. }
  1481. /*** Generate segments ***/
  1482. let j;
  1483. index = 3;
  1484. let cartographicsIndex = 0;
  1485. let vec2sWriteIndex = 0;
  1486. let vec3sWriteIndex = 0;
  1487. let vec4sWriteIndex = 0;
  1488. let miterBroken = false;
  1489. let endBottom = Matrix2.Cartesian3.unpack(
  1490. bottomPositionsArray,
  1491. 0,
  1492. segmentEndBottomScratch
  1493. );
  1494. let endTop = Matrix2.Cartesian3.unpack(topPositionsArray, 0, segmentEndTopScratch);
  1495. let endGeometryNormal = Matrix2.Cartesian3.unpack(
  1496. normalsArray,
  1497. 0,
  1498. segmentEndNormalScratch
  1499. );
  1500. if (loop) {
  1501. const preEndBottom = Matrix2.Cartesian3.unpack(
  1502. bottomPositionsArray,
  1503. bottomPositionsArray.length - 6,
  1504. segmentStartBottomScratch
  1505. );
  1506. if (breakMiter(endGeometryNormal, preEndBottom, endBottom, endTop)) {
  1507. // Miter broken as if for the last point in the loop, needs to be inverted for first point (clone of endBottom)
  1508. endGeometryNormal = Matrix2.Cartesian3.negate(
  1509. endGeometryNormal,
  1510. endGeometryNormal
  1511. );
  1512. }
  1513. }
  1514. let lengthSoFar3D = 0.0;
  1515. let lengthSoFar2D = 0.0;
  1516. // For translating bounding volume
  1517. let sumHeights = 0.0;
  1518. for (i = 0; i < segmentCount; i++) {
  1519. const startBottom = Matrix2.Cartesian3.clone(endBottom, segmentStartBottomScratch);
  1520. const startTop = Matrix2.Cartesian3.clone(endTop, segmentStartTopScratch);
  1521. let startGeometryNormal = Matrix2.Cartesian3.clone(
  1522. endGeometryNormal,
  1523. segmentStartNormalScratch
  1524. );
  1525. if (miterBroken) {
  1526. startGeometryNormal = Matrix2.Cartesian3.negate(
  1527. startGeometryNormal,
  1528. startGeometryNormal
  1529. );
  1530. }
  1531. endBottom = Matrix2.Cartesian3.unpack(
  1532. bottomPositionsArray,
  1533. index,
  1534. segmentEndBottomScratch
  1535. );
  1536. endTop = Matrix2.Cartesian3.unpack(topPositionsArray, index, segmentEndTopScratch);
  1537. endGeometryNormal = Matrix2.Cartesian3.unpack(
  1538. normalsArray,
  1539. index,
  1540. segmentEndNormalScratch
  1541. );
  1542. miterBroken = breakMiter(endGeometryNormal, startBottom, endBottom, endTop);
  1543. // 2D - don't clone anything from previous segment b/c possible IDL touch
  1544. startCartographic.latitude = cartographicsArray[cartographicsIndex];
  1545. startCartographic.longitude = cartographicsArray[cartographicsIndex + 1];
  1546. endCartographic.latitude = cartographicsArray[cartographicsIndex + 2];
  1547. endCartographic.longitude = cartographicsArray[cartographicsIndex + 3];
  1548. let start2D;
  1549. let end2D;
  1550. let startGeometryNormal2D;
  1551. let endGeometryNormal2D;
  1552. if (compute2dAttributes) {
  1553. const nudgeResult = nudgeCartographic(startCartographic, endCartographic);
  1554. start2D = projection.project(startCartographic, segmentStart2DScratch);
  1555. end2D = projection.project(endCartographic, segmentEnd2DScratch);
  1556. const direction2D = direction(end2D, start2D, forwardOffset2DScratch);
  1557. direction2D.y = Math.abs(direction2D.y);
  1558. startGeometryNormal2D = segmentStartNormal2DScratch;
  1559. endGeometryNormal2D = segmentEndNormal2DScratch;
  1560. if (
  1561. nudgeResult === 0 ||
  1562. Matrix2.Cartesian3.dot(direction2D, Matrix2.Cartesian3.UNIT_Y) > MITER_BREAK_SMALL
  1563. ) {
  1564. // No nudge - project the original normal
  1565. // Or, if the line's angle relative to the IDL is very acute,
  1566. // in which case snapping will produce oddly shaped volumes.
  1567. startGeometryNormal2D = projectNormal(
  1568. projection,
  1569. startCartographic,
  1570. startGeometryNormal,
  1571. start2D,
  1572. segmentStartNormal2DScratch
  1573. );
  1574. endGeometryNormal2D = projectNormal(
  1575. projection,
  1576. endCartographic,
  1577. endGeometryNormal,
  1578. end2D,
  1579. segmentEndNormal2DScratch
  1580. );
  1581. } else if (nudgeResult === 1) {
  1582. // Start is close to IDL - snap start normal to align with IDL
  1583. endGeometryNormal2D = projectNormal(
  1584. projection,
  1585. endCartographic,
  1586. endGeometryNormal,
  1587. end2D,
  1588. segmentEndNormal2DScratch
  1589. );
  1590. startGeometryNormal2D.x = 0.0;
  1591. // If start longitude is negative and end longitude is less negative, relative right is unit -Y
  1592. // If start longitude is positive and end longitude is less positive, relative right is unit +Y
  1593. startGeometryNormal2D.y = ComponentDatatype.CesiumMath.sign(
  1594. startCartographic.longitude - Math.abs(endCartographic.longitude)
  1595. );
  1596. startGeometryNormal2D.z = 0.0;
  1597. } else {
  1598. // End is close to IDL - snap end normal to align with IDL
  1599. startGeometryNormal2D = projectNormal(
  1600. projection,
  1601. startCartographic,
  1602. startGeometryNormal,
  1603. start2D,
  1604. segmentStartNormal2DScratch
  1605. );
  1606. endGeometryNormal2D.x = 0.0;
  1607. // If end longitude is negative and start longitude is less negative, relative right is unit Y
  1608. // If end longitude is positive and start longitude is less positive, relative right is unit -Y
  1609. endGeometryNormal2D.y = ComponentDatatype.CesiumMath.sign(
  1610. startCartographic.longitude - endCartographic.longitude
  1611. );
  1612. endGeometryNormal2D.z = 0.0;
  1613. }
  1614. }
  1615. /****************************************
  1616. * Geometry descriptors of a "line on terrain,"
  1617. * as opposed to the "shadow volume used to draw
  1618. * the line on terrain":
  1619. * - position of start + offset to end
  1620. * - start, end, and right-facing planes
  1621. * - encoded texture coordinate offsets
  1622. ****************************************/
  1623. /* 3D */
  1624. const segmentLength3D = Matrix2.Cartesian3.distance(startTop, endTop);
  1625. const encodedStart = EncodedCartesian3.EncodedCartesian3.fromCartesian(
  1626. startBottom,
  1627. encodeScratch
  1628. );
  1629. const forwardOffset = Matrix2.Cartesian3.subtract(
  1630. endBottom,
  1631. startBottom,
  1632. offsetScratch
  1633. );
  1634. const forward = Matrix2.Cartesian3.normalize(forwardOffset, rightScratch);
  1635. let startUp = Matrix2.Cartesian3.subtract(startTop, startBottom, startUpScratch);
  1636. startUp = Matrix2.Cartesian3.normalize(startUp, startUp);
  1637. let rightNormal = Matrix2.Cartesian3.cross(forward, startUp, rightScratch);
  1638. rightNormal = Matrix2.Cartesian3.normalize(rightNormal, rightNormal);
  1639. let startPlaneNormal = Matrix2.Cartesian3.cross(
  1640. startUp,
  1641. startGeometryNormal,
  1642. startPlaneNormalScratch
  1643. );
  1644. startPlaneNormal = Matrix2.Cartesian3.normalize(startPlaneNormal, startPlaneNormal);
  1645. let endUp = Matrix2.Cartesian3.subtract(endTop, endBottom, endUpScratch);
  1646. endUp = Matrix2.Cartesian3.normalize(endUp, endUp);
  1647. let endPlaneNormal = Matrix2.Cartesian3.cross(
  1648. endGeometryNormal,
  1649. endUp,
  1650. endPlaneNormalScratch
  1651. );
  1652. endPlaneNormal = Matrix2.Cartesian3.normalize(endPlaneNormal, endPlaneNormal);
  1653. const texcoordNormalization3DX = segmentLength3D / length3D;
  1654. const texcoordNormalization3DY = lengthSoFar3D / length3D;
  1655. /* 2D */
  1656. let segmentLength2D = 0.0;
  1657. let encodedStart2D;
  1658. let forwardOffset2D;
  1659. let right2D;
  1660. let texcoordNormalization2DX = 0.0;
  1661. let texcoordNormalization2DY = 0.0;
  1662. if (compute2dAttributes) {
  1663. segmentLength2D = Matrix2.Cartesian3.distance(start2D, end2D);
  1664. encodedStart2D = EncodedCartesian3.EncodedCartesian3.fromCartesian(
  1665. start2D,
  1666. encodeScratch2D
  1667. );
  1668. forwardOffset2D = Matrix2.Cartesian3.subtract(
  1669. end2D,
  1670. start2D,
  1671. forwardOffset2DScratch
  1672. );
  1673. // Right direction is just forward direction rotated by -90 degrees around Z
  1674. // Similarly with plane normals
  1675. right2D = Matrix2.Cartesian3.normalize(forwardOffset2D, right2DScratch);
  1676. const swap = right2D.x;
  1677. right2D.x = right2D.y;
  1678. right2D.y = -swap;
  1679. texcoordNormalization2DX = segmentLength2D / length2D;
  1680. texcoordNormalization2DY = lengthSoFar2D / length2D;
  1681. }
  1682. /** Pack **/
  1683. for (j = 0; j < 8; j++) {
  1684. const vec4Index = vec4sWriteIndex + j * 4;
  1685. const vec2Index = vec2sWriteIndex + j * 2;
  1686. const wIndex = vec4Index + 3;
  1687. // Encode sidedness of vertex relative to right plane in texture coordinate normalization X,
  1688. // whether vertex is top or bottom of volume in sign/magnitude of normalization Y.
  1689. const rightPlaneSide = j < 4 ? 1.0 : -1.0;
  1690. const topBottomSide =
  1691. j === 2 || j === 3 || j === 6 || j === 7 ? 1.0 : -1.0;
  1692. // 3D
  1693. Matrix2.Cartesian3.pack(encodedStart.high, startHiAndForwardOffsetX, vec4Index);
  1694. startHiAndForwardOffsetX[wIndex] = forwardOffset.x;
  1695. Matrix2.Cartesian3.pack(encodedStart.low, startLoAndForwardOffsetY, vec4Index);
  1696. startLoAndForwardOffsetY[wIndex] = forwardOffset.y;
  1697. Matrix2.Cartesian3.pack(
  1698. startPlaneNormal,
  1699. startNormalAndForwardOffsetZ,
  1700. vec4Index
  1701. );
  1702. startNormalAndForwardOffsetZ[wIndex] = forwardOffset.z;
  1703. Matrix2.Cartesian3.pack(
  1704. endPlaneNormal,
  1705. endNormalAndTextureCoordinateNormalizationX,
  1706. vec4Index
  1707. );
  1708. endNormalAndTextureCoordinateNormalizationX[wIndex] =
  1709. texcoordNormalization3DX * rightPlaneSide;
  1710. Matrix2.Cartesian3.pack(
  1711. rightNormal,
  1712. rightNormalAndTextureCoordinateNormalizationY,
  1713. vec4Index
  1714. );
  1715. let texcoordNormalization = texcoordNormalization3DY * topBottomSide;
  1716. if (texcoordNormalization === 0.0 && topBottomSide < 0.0) {
  1717. texcoordNormalization = 9.0; // some value greater than 1.0
  1718. }
  1719. rightNormalAndTextureCoordinateNormalizationY[
  1720. wIndex
  1721. ] = texcoordNormalization;
  1722. // 2D
  1723. if (compute2dAttributes) {
  1724. startHiLo2D[vec4Index] = encodedStart2D.high.x;
  1725. startHiLo2D[vec4Index + 1] = encodedStart2D.high.y;
  1726. startHiLo2D[vec4Index + 2] = encodedStart2D.low.x;
  1727. startHiLo2D[vec4Index + 3] = encodedStart2D.low.y;
  1728. startEndNormals2D[vec4Index] = -startGeometryNormal2D.y;
  1729. startEndNormals2D[vec4Index + 1] = startGeometryNormal2D.x;
  1730. startEndNormals2D[vec4Index + 2] = endGeometryNormal2D.y;
  1731. startEndNormals2D[vec4Index + 3] = -endGeometryNormal2D.x;
  1732. offsetAndRight2D[vec4Index] = forwardOffset2D.x;
  1733. offsetAndRight2D[vec4Index + 1] = forwardOffset2D.y;
  1734. offsetAndRight2D[vec4Index + 2] = right2D.x;
  1735. offsetAndRight2D[vec4Index + 3] = right2D.y;
  1736. texcoordNormalization2D[vec2Index] =
  1737. texcoordNormalization2DX * rightPlaneSide;
  1738. texcoordNormalization = texcoordNormalization2DY * topBottomSide;
  1739. if (texcoordNormalization === 0.0 && topBottomSide < 0.0) {
  1740. texcoordNormalization = 9.0; // some value greater than 1.0
  1741. }
  1742. texcoordNormalization2D[vec2Index + 1] = texcoordNormalization;
  1743. }
  1744. }
  1745. // Adjust height of volume in 3D
  1746. const adjustHeightStartBottom = adjustHeightStartBottomScratch;
  1747. const adjustHeightEndBottom = adjustHeightEndBottomScratch;
  1748. const adjustHeightStartTop = adjustHeightStartTopScratch;
  1749. const adjustHeightEndTop = adjustHeightEndTopScratch;
  1750. const getHeightsRectangle = Matrix2.Rectangle.fromCartographicArray(
  1751. getHeightCartographics,
  1752. getHeightRectangleScratch
  1753. );
  1754. const minMaxHeights = ApproximateTerrainHeights$1.getMinimumMaximumHeights(
  1755. getHeightsRectangle,
  1756. ellipsoid
  1757. );
  1758. const minHeight = minMaxHeights.minimumTerrainHeight;
  1759. const maxHeight = minMaxHeights.maximumTerrainHeight;
  1760. sumHeights += minHeight;
  1761. sumHeights += maxHeight;
  1762. adjustHeights(
  1763. startBottom,
  1764. startTop,
  1765. minHeight,
  1766. maxHeight,
  1767. adjustHeightStartBottom,
  1768. adjustHeightStartTop
  1769. );
  1770. adjustHeights(
  1771. endBottom,
  1772. endTop,
  1773. minHeight,
  1774. maxHeight,
  1775. adjustHeightEndBottom,
  1776. adjustHeightEndTop
  1777. );
  1778. // Nudge the positions away from the "polyline" a little bit to prevent errors in GeometryPipeline
  1779. let normalNudge = Matrix2.Cartesian3.multiplyByScalar(
  1780. rightNormal,
  1781. ComponentDatatype.CesiumMath.EPSILON5,
  1782. normalNudgeScratch
  1783. );
  1784. Matrix2.Cartesian3.add(
  1785. adjustHeightStartBottom,
  1786. normalNudge,
  1787. adjustHeightStartBottom
  1788. );
  1789. Matrix2.Cartesian3.add(adjustHeightEndBottom, normalNudge, adjustHeightEndBottom);
  1790. Matrix2.Cartesian3.add(adjustHeightStartTop, normalNudge, adjustHeightStartTop);
  1791. Matrix2.Cartesian3.add(adjustHeightEndTop, normalNudge, adjustHeightEndTop);
  1792. // If the segment is very close to the XZ plane, nudge the vertices slightly to avoid touching it.
  1793. nudgeXZ(adjustHeightStartBottom, adjustHeightEndBottom);
  1794. nudgeXZ(adjustHeightStartTop, adjustHeightEndTop);
  1795. Matrix2.Cartesian3.pack(adjustHeightStartBottom, positionsArray, vec3sWriteIndex);
  1796. Matrix2.Cartesian3.pack(adjustHeightEndBottom, positionsArray, vec3sWriteIndex + 3);
  1797. Matrix2.Cartesian3.pack(adjustHeightEndTop, positionsArray, vec3sWriteIndex + 6);
  1798. Matrix2.Cartesian3.pack(adjustHeightStartTop, positionsArray, vec3sWriteIndex + 9);
  1799. normalNudge = Matrix2.Cartesian3.multiplyByScalar(
  1800. rightNormal,
  1801. -2.0 * ComponentDatatype.CesiumMath.EPSILON5,
  1802. normalNudgeScratch
  1803. );
  1804. Matrix2.Cartesian3.add(
  1805. adjustHeightStartBottom,
  1806. normalNudge,
  1807. adjustHeightStartBottom
  1808. );
  1809. Matrix2.Cartesian3.add(adjustHeightEndBottom, normalNudge, adjustHeightEndBottom);
  1810. Matrix2.Cartesian3.add(adjustHeightStartTop, normalNudge, adjustHeightStartTop);
  1811. Matrix2.Cartesian3.add(adjustHeightEndTop, normalNudge, adjustHeightEndTop);
  1812. nudgeXZ(adjustHeightStartBottom, adjustHeightEndBottom);
  1813. nudgeXZ(adjustHeightStartTop, adjustHeightEndTop);
  1814. Matrix2.Cartesian3.pack(
  1815. adjustHeightStartBottom,
  1816. positionsArray,
  1817. vec3sWriteIndex + 12
  1818. );
  1819. Matrix2.Cartesian3.pack(
  1820. adjustHeightEndBottom,
  1821. positionsArray,
  1822. vec3sWriteIndex + 15
  1823. );
  1824. Matrix2.Cartesian3.pack(adjustHeightEndTop, positionsArray, vec3sWriteIndex + 18);
  1825. Matrix2.Cartesian3.pack(adjustHeightStartTop, positionsArray, vec3sWriteIndex + 21);
  1826. cartographicsIndex += 2;
  1827. index += 3;
  1828. vec2sWriteIndex += 16;
  1829. vec3sWriteIndex += 24;
  1830. vec4sWriteIndex += 32;
  1831. lengthSoFar3D += segmentLength3D;
  1832. lengthSoFar2D += segmentLength2D;
  1833. }
  1834. index = 0;
  1835. let indexOffset = 0;
  1836. for (i = 0; i < segmentCount; i++) {
  1837. for (j = 0; j < REFERENCE_INDICES_LENGTH; j++) {
  1838. indices[index + j] = REFERENCE_INDICES[j] + indexOffset;
  1839. }
  1840. indexOffset += 8;
  1841. index += REFERENCE_INDICES_LENGTH;
  1842. }
  1843. const boundingSpheres = scratchBoundingSpheres;
  1844. Transforms.BoundingSphere.fromVertices(
  1845. bottomPositionsArray,
  1846. Matrix2.Cartesian3.ZERO,
  1847. 3,
  1848. boundingSpheres[0]
  1849. );
  1850. Transforms.BoundingSphere.fromVertices(
  1851. topPositionsArray,
  1852. Matrix2.Cartesian3.ZERO,
  1853. 3,
  1854. boundingSpheres[1]
  1855. );
  1856. const boundingSphere = Transforms.BoundingSphere.fromBoundingSpheres(boundingSpheres);
  1857. // Adjust bounding sphere height and radius to cover more of the volume
  1858. boundingSphere.radius += sumHeights / (segmentCount * 2.0);
  1859. const attributes = {
  1860. position: new GeometryAttribute.GeometryAttribute({
  1861. componentDatatype: ComponentDatatype.ComponentDatatype.DOUBLE,
  1862. componentsPerAttribute: 3,
  1863. normalize: false,
  1864. values: positionsArray,
  1865. }),
  1866. startHiAndForwardOffsetX: getVec4GeometryAttribute(
  1867. startHiAndForwardOffsetX
  1868. ),
  1869. startLoAndForwardOffsetY: getVec4GeometryAttribute(
  1870. startLoAndForwardOffsetY
  1871. ),
  1872. startNormalAndForwardOffsetZ: getVec4GeometryAttribute(
  1873. startNormalAndForwardOffsetZ
  1874. ),
  1875. endNormalAndTextureCoordinateNormalizationX: getVec4GeometryAttribute(
  1876. endNormalAndTextureCoordinateNormalizationX
  1877. ),
  1878. rightNormalAndTextureCoordinateNormalizationY: getVec4GeometryAttribute(
  1879. rightNormalAndTextureCoordinateNormalizationY
  1880. ),
  1881. };
  1882. if (compute2dAttributes) {
  1883. attributes.startHiLo2D = getVec4GeometryAttribute(startHiLo2D);
  1884. attributes.offsetAndRight2D = getVec4GeometryAttribute(offsetAndRight2D);
  1885. attributes.startEndNormals2D = getVec4GeometryAttribute(startEndNormals2D);
  1886. attributes.texcoordNormalization2D = new GeometryAttribute.GeometryAttribute({
  1887. componentDatatype: ComponentDatatype.ComponentDatatype.FLOAT,
  1888. componentsPerAttribute: 2,
  1889. normalize: false,
  1890. values: texcoordNormalization2D,
  1891. });
  1892. }
  1893. return new GeometryAttribute.Geometry({
  1894. attributes: attributes,
  1895. indices: indices,
  1896. boundingSphere: boundingSphere,
  1897. });
  1898. }
  1899. function getVec4GeometryAttribute(typedArray) {
  1900. return new GeometryAttribute.GeometryAttribute({
  1901. componentDatatype: ComponentDatatype.ComponentDatatype.FLOAT,
  1902. componentsPerAttribute: 4,
  1903. normalize: false,
  1904. values: typedArray,
  1905. });
  1906. }
  1907. /**
  1908. * Approximates an ellipsoid-tangent vector in 2D by projecting the end point into 2D.
  1909. * Exposed for testing.
  1910. *
  1911. * @param {MapProjection} projection Map Projection for projecting coordinates to 2D.
  1912. * @param {Cartographic} cartographic The cartographic origin point of the normal.
  1913. * Used to check if the normal crosses the IDL during projection.
  1914. * @param {Cartesian3} normal The normal in 3D.
  1915. * @param {Cartesian3} projectedPosition The projected origin point of the normal in 2D.
  1916. * @param {Cartesian3} result Result parameter on which to store the projected normal.
  1917. * @private
  1918. */
  1919. GroundPolylineGeometry._projectNormal = projectNormal;
  1920. function createGroundPolylineGeometry(groundPolylineGeometry, offset) {
  1921. return ApproximateTerrainHeights$1.initialize().then(function () {
  1922. if (defaultValue.defined(offset)) {
  1923. groundPolylineGeometry = GroundPolylineGeometry.unpack(
  1924. groundPolylineGeometry,
  1925. offset
  1926. );
  1927. }
  1928. return GroundPolylineGeometry.createGeometry(groundPolylineGeometry);
  1929. });
  1930. }
  1931. return createGroundPolylineGeometry;
  1932. }));