createGroundPolylineGeometry.js 73 KB

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