createGroundPolylineGeometry.js 75 KB

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