createGroundPolylineGeometry.js 80 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621
  1. /**
  2. * Cesium - https://github.com/AnalyticalGraphicsInc/cesium
  3. *
  4. * Copyright 2011-2017 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/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md for full licensing details.
  22. */
  23. define(['./when-8d13db60', './Check-70bec281', './Math-61ede240', './Cartographic-fe4be337', './Cartesian2-85064f09', './BoundingSphere-775c5788', './Cartesian4-5af5bb24', './RuntimeError-ba10bc3e', './WebGLConstants-4c11ee5f', './ComponentDatatype-5862616f', './GeometryAttribute-91704ebb', './PrimitiveType-97893bc7', './FeatureDetection-7bd32c34', './Transforms-b2e71640', './buildModuleUrl-14bfe498', './EncodedCartesian3-a569cba8', './IntersectionTests-397d9494', './Plane-8390418f', './WebMercatorProjection-80c70558', './arrayRemoveDuplicates-f0b089b1', './ArcType-66bc286a', './EllipsoidRhumbLine-f161e674', './EllipsoidGeodesic-84507801'], function (when, Check, _Math, Cartographic, Cartesian2, BoundingSphere, Cartesian4, RuntimeError, WebGLConstants, ComponentDatatype, GeometryAttribute, PrimitiveType, FeatureDetection, Transforms, buildModuleUrl, EncodedCartesian3, IntersectionTests, Plane, WebMercatorProjection, arrayRemoveDuplicates, ArcType, EllipsoidRhumbLine, EllipsoidGeodesic) { '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 = when.defaultValue(options, {});
  43. this._ellipsoid = when.defaultValue(options.ellipsoid, Cartesian2.Ellipsoid.WGS84);
  44. this._rectangle = when.defaultValue(options.rectangle, Cartesian2.Rectangle.MAX_VALUE);
  45. this._projection = new BoundingSphere.GeographicProjection(this._ellipsoid);
  46. this._numberOfLevelZeroTilesX = when.defaultValue(options.numberOfLevelZeroTilesX, 2);
  47. this._numberOfLevelZeroTilesY = when.defaultValue(options.numberOfLevelZeroTilesY, 1);
  48. this._customDPI = options.customDPI;
  49. this._scaleDenominators = options.scaleDenominators;
  50. this._tileWidth = when.defaultValue(options.tileWidth, 256);
  51. this._tileHeight = when.defaultValue(options.tileHeight, 256);
  52. this._beginLevel = when.defaultValue(options.beginLevel, 0);
  53. }
  54. Object.defineProperties(GeographicTilingScheme.prototype, {
  55. /**
  56. * Gets the ellipsoid that is tiled by this tiling scheme.
  57. * @memberof GeographicTilingScheme.prototype
  58. * @type {Ellipsoid}
  59. */
  60. ellipsoid : {
  61. get : function() {
  62. return this._ellipsoid;
  63. }
  64. },
  65. /**
  66. * Gets the rectangle, in radians, covered by this tiling scheme.
  67. * @memberof GeographicTilingScheme.prototype
  68. * @type {Rectangle}
  69. */
  70. rectangle : {
  71. get : function() {
  72. return this._rectangle;
  73. }
  74. },
  75. /**
  76. * Gets the map projection used by this tiling scheme.
  77. * @memberof GeographicTilingScheme.prototype
  78. * @type {MapProjection}
  79. */
  80. projection : {
  81. get : function() {
  82. return this._projection;
  83. }
  84. },
  85. beginLevel : {
  86. get : function() {
  87. return this._beginLevel;
  88. }
  89. }
  90. });
  91. /**
  92. * Gets the total number of tiles in the X direction at a specified level-of-detail.
  93. *
  94. * @param {Number} level The level-of-detail.
  95. * @returns {Number} The number of tiles in the X direction at the given level.
  96. */
  97. GeographicTilingScheme.prototype.getNumberOfXTilesAtLevel = function(level) {
  98. return this._numberOfLevelZeroTilesX << (level - this._beginLevel);
  99. };
  100. /**
  101. * Gets the total number of tiles in the Y direction at a specified level-of-detail.
  102. *
  103. * @param {Number} level The level-of-detail.
  104. * @returns {Number} The number of tiles in the Y direction at the given level.
  105. */
  106. GeographicTilingScheme.prototype.getNumberOfYTilesAtLevel = function(level) {
  107. return this._numberOfLevelZeroTilesY << (level - this._beginLevel);
  108. };
  109. /**
  110. * Transforms a rectangle specified in geodetic radians to the native coordinate system
  111. * of this tiling scheme.
  112. *
  113. * @param {Rectangle} rectangle The rectangle to transform.
  114. * @param {Rectangle} [result] The instance to which to copy the result, or undefined if a new instance
  115. * should be created.
  116. * @returns {Rectangle} The specified 'result', or a new object containing the native rectangle if 'result'
  117. * is undefined.
  118. */
  119. GeographicTilingScheme.prototype.rectangleToNativeRectangle = function(rectangle, result) {
  120. //>>includeStart('debug', pragmas.debug);
  121. Check.Check.defined('rectangle', rectangle);
  122. //>>includeEnd('debug');
  123. var west = _Math.CesiumMath.toDegrees(rectangle.west);
  124. var south = _Math.CesiumMath.toDegrees(rectangle.south);
  125. var east = _Math.CesiumMath.toDegrees(rectangle.east);
  126. var north = _Math.CesiumMath.toDegrees(rectangle.north);
  127. if (!when.defined(result)) {
  128. return new Cartesian2.Rectangle(west, south, east, north);
  129. }
  130. result.west = west;
  131. result.south = south;
  132. result.east = east;
  133. result.north = north;
  134. return result;
  135. };
  136. /**
  137. * Converts tile x, y coordinates and level to a rectangle expressed in the native coordinates
  138. * of the tiling scheme.
  139. *
  140. * @param {Number} x The integer x coordinate of the tile.
  141. * @param {Number} y The integer y coordinate of the tile.
  142. * @param {Number} level The tile level-of-detail. Zero is the least detailed.
  143. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance
  144. * should be created.
  145. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle
  146. * if 'result' is undefined.
  147. */
  148. GeographicTilingScheme.prototype.tileXYToNativeRectangle = function(x, y, level, result) {
  149. var rectangleRadians = this.tileXYToRectangle(x, y, level, result);
  150. rectangleRadians.west = _Math.CesiumMath.toDegrees(rectangleRadians.west);
  151. rectangleRadians.south = _Math.CesiumMath.toDegrees(rectangleRadians.south);
  152. rectangleRadians.east = _Math.CesiumMath.toDegrees(rectangleRadians.east);
  153. rectangleRadians.north = _Math.CesiumMath.toDegrees(rectangleRadians.north);
  154. return rectangleRadians;
  155. };
  156. /**
  157. * Converts tile x, y coordinates and level to a cartographic rectangle in radians.
  158. *
  159. * @param {Number} x The integer x coordinate of the tile.
  160. * @param {Number} y The integer y coordinate of the tile.
  161. * @param {Number} level The tile level-of-detail. Zero is the least detailed.
  162. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance
  163. * should be created.
  164. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle
  165. * if 'result' is undefined.
  166. */
  167. GeographicTilingScheme.prototype.tileXYToRectangle = function(x, y, level, result) {
  168. var rectangle = this._rectangle;
  169. if(when.defined(this._customDPI) && when.defined(this._scaleDenominators)){
  170. var resolution = this.calculateResolution(level);
  171. var west = -_Math.CesiumMath.PI + x * this._tileWidth * resolution.x;
  172. var east = -_Math.CesiumMath.PI + (x + 1) * this._tileWidth * resolution.x;
  173. var north = _Math.CesiumMath.PI_OVER_TWO - y * this._tileHeight * resolution.y;
  174. var south = _Math.CesiumMath.PI_OVER_TWO - (y + 1) * this._tileHeight * resolution.y;
  175. if (!when.defined(result)) {
  176. return new Cartesian2.Rectangle(west, south, east, north);
  177. }
  178. result.west = west;
  179. result.south = south;
  180. result.east = east;
  181. result.north = north;
  182. return result;
  183. }
  184. var xTiles = this.getNumberOfXTilesAtLevel(level);
  185. var yTiles = this.getNumberOfYTilesAtLevel(level);
  186. var xTileWidth = rectangle.width / xTiles;
  187. var west = x * xTileWidth + rectangle.west;
  188. var east = (x + 1) * xTileWidth + rectangle.west;
  189. var yTileHeight = rectangle.height / yTiles;
  190. var north = rectangle.north - y * yTileHeight;
  191. var south = rectangle.north - (y + 1) * yTileHeight;
  192. if (!when.defined(result)) {
  193. result = new Cartesian2.Rectangle(west, south, east, north);
  194. }
  195. result.west = west;
  196. result.south = south;
  197. result.east = east;
  198. result.north = north;
  199. return result;
  200. };
  201. /**
  202. * Calculates the tile x, y coordinates of the tile containing
  203. * a given cartographic position.
  204. *
  205. * @param {Cartographic} position The position.
  206. * @param {Number} level The tile level-of-detail. Zero is the least detailed.
  207. * @param {Cartesian2} [result] The instance to which to copy the result, or undefined if a new instance
  208. * should be created.
  209. * @returns {Cartesian2} The specified 'result', or a new object containing the tile x, y coordinates
  210. * if 'result' is undefined.
  211. */
  212. GeographicTilingScheme.prototype.positionToTileXY = function(position, level, result) {
  213. var rectangle = this._rectangle;
  214. if (!Cartesian2.Rectangle.contains(rectangle, position)) {
  215. // outside the bounds of the tiling scheme
  216. return undefined;
  217. }
  218. var xTiles = this.getNumberOfXTilesAtLevel(level);
  219. var yTiles = this.getNumberOfYTilesAtLevel(level);
  220. var xTileWidth = rectangle.width / xTiles;
  221. var yTileHeight = rectangle.height / yTiles;
  222. if(when.defined(this._customDPI) && when.defined(this._scaleDenominators)) {
  223. var resolution = this.calculateResolution(level);
  224. xTileWidth = this._tileWidth * resolution.x;
  225. yTileHeight = this._tileHeight * resolution.y;
  226. }
  227. var longitude = position.longitude;
  228. if (rectangle.east < rectangle.west) {
  229. longitude += _Math.CesiumMath.TWO_PI;
  230. }
  231. var xTileCoordinate = (longitude - rectangle.west) / xTileWidth | 0;
  232. if (xTileCoordinate >= xTiles) {
  233. xTileCoordinate = xTiles - 1;
  234. }
  235. var yTileCoordinate = (rectangle.north - position.latitude) / yTileHeight | 0;
  236. if (yTileCoordinate >= yTiles) {
  237. yTileCoordinate = yTiles - 1;
  238. }
  239. if (!when.defined(result)) {
  240. return new Cartesian2.Cartesian2(xTileCoordinate, yTileCoordinate);
  241. }
  242. result.x = xTileCoordinate;
  243. result.y = yTileCoordinate;
  244. return result;
  245. };
  246. GeographicTilingScheme.prototype.calculateResolution = function(level) {
  247. var xResolution = this._scaleDenominators[level - this._beginLevel] * 0.0254 / this._customDPI.x;
  248. var yResolution = this._scaleDenominators[level - this._beginLevel] * 0.0254 / this._customDPI.y;
  249. var meterPerDegreeInEquator = Cartesian2.Ellipsoid.WGS84.maximumRadius/* * CesiumMath.TWO_PI / 360.0*/;
  250. return new Cartesian2.Cartesian2(xResolution / meterPerDegreeInEquator, yResolution / meterPerDegreeInEquator);
  251. };
  252. var scratchDiagonalCartesianNE = new Cartographic.Cartesian3();
  253. var scratchDiagonalCartesianSW = new Cartographic.Cartesian3();
  254. var scratchDiagonalCartographic = new Cartographic.Cartographic();
  255. var scratchCenterCartesian = new Cartographic.Cartesian3();
  256. var scratchSurfaceCartesian = new Cartographic.Cartesian3();
  257. var scratchBoundingSphere = new BoundingSphere.BoundingSphere();
  258. var tilingScheme = new GeographicTilingScheme();
  259. var scratchCorners = [new Cartographic.Cartographic(), new Cartographic.Cartographic(), new Cartographic.Cartographic(), new Cartographic.Cartographic()];
  260. var scratchTileXY = new Cartesian2.Cartesian2();
  261. /**
  262. * A collection of functions for approximating terrain height
  263. * @private
  264. */
  265. var ApproximateTerrainHeights = {};
  266. /**
  267. * Initializes the minimum and maximum terrain heights
  268. * @return {Promise}
  269. */
  270. ApproximateTerrainHeights.initialize = function() {
  271. var initPromise = ApproximateTerrainHeights._initPromise;
  272. if (when.defined(initPromise)) {
  273. return initPromise;
  274. }
  275. initPromise = buildModuleUrl.Resource.fetchJson(buildModuleUrl.buildModuleUrl('Assets/approximateTerrainHeights.json'))
  276. .then(function(json) {
  277. ApproximateTerrainHeights._terrainHeights = json;
  278. });
  279. ApproximateTerrainHeights._initPromise = initPromise;
  280. return initPromise;
  281. };
  282. /**
  283. * Computes the minimum and maximum terrain heights for a given rectangle
  284. * @param {Rectangle} rectangle The bounding rectangle
  285. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid
  286. * @return {{minimumTerrainHeight: Number, maximumTerrainHeight: Number}}
  287. */
  288. ApproximateTerrainHeights.getMinimumMaximumHeights = function(rectangle, ellipsoid) {
  289. //>>includeStart('debug', pragmas.debug);
  290. Check.Check.defined('rectangle', rectangle);
  291. if (!when.defined(ApproximateTerrainHeights._terrainHeights)) {
  292. throw new Check.DeveloperError('You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function');
  293. }
  294. //>>includeEnd('debug');
  295. ellipsoid = when.defaultValue(ellipsoid, Cartesian2.Ellipsoid.WGS84);
  296. var xyLevel = getTileXYLevel(rectangle);
  297. // Get the terrain min/max for that tile
  298. var minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
  299. var maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
  300. if (when.defined(xyLevel)) {
  301. var key = xyLevel.level + '-' + xyLevel.x + '-' + xyLevel.y;
  302. var heights = ApproximateTerrainHeights._terrainHeights[key];
  303. if (when.defined(heights)) {
  304. minTerrainHeight = heights[0];
  305. maxTerrainHeight = heights[1];
  306. }
  307. // Compute min by taking the center of the NE->SW diagonal and finding distance to the surface
  308. ellipsoid.cartographicToCartesian(Cartesian2.Rectangle.northeast(rectangle, scratchDiagonalCartographic),
  309. scratchDiagonalCartesianNE);
  310. ellipsoid.cartographicToCartesian(Cartesian2.Rectangle.southwest(rectangle, scratchDiagonalCartographic),
  311. scratchDiagonalCartesianSW);
  312. Cartographic.Cartesian3.midpoint(scratchDiagonalCartesianSW, scratchDiagonalCartesianNE, scratchCenterCartesian);
  313. var surfacePosition = ellipsoid.scaleToGeodeticSurface(scratchCenterCartesian, scratchSurfaceCartesian);
  314. if (when.defined(surfacePosition)) {
  315. var distance = Cartographic.Cartesian3.distance(scratchCenterCartesian, surfacePosition);
  316. minTerrainHeight = Math.min(minTerrainHeight, -distance);
  317. } else {
  318. minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
  319. }
  320. }
  321. minTerrainHeight = Math.max(ApproximateTerrainHeights._defaultMinTerrainHeight, minTerrainHeight);
  322. return {
  323. minimumTerrainHeight: minTerrainHeight,
  324. maximumTerrainHeight: maxTerrainHeight
  325. };
  326. };
  327. /**
  328. * Computes the bounding sphere based on the tile heights in the rectangle
  329. * @param {Rectangle} rectangle The bounding rectangle
  330. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid
  331. * @return {BoundingSphere} The result bounding sphere
  332. */
  333. ApproximateTerrainHeights.getBoundingSphere = function(rectangle, ellipsoid) {
  334. //>>includeStart('debug', pragmas.debug);
  335. Check.Check.defined('rectangle', rectangle);
  336. if (!when.defined(ApproximateTerrainHeights._terrainHeights)) {
  337. throw new Check.DeveloperError('You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function');
  338. }
  339. //>>includeEnd('debug');
  340. ellipsoid = when.defaultValue(ellipsoid, Cartesian2.Ellipsoid.WGS84);
  341. var xyLevel = getTileXYLevel(rectangle);
  342. // Get the terrain max for that tile
  343. var maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
  344. if (when.defined(xyLevel)) {
  345. var key = xyLevel.level + '-' + xyLevel.x + '-' + xyLevel.y;
  346. var heights = ApproximateTerrainHeights._terrainHeights[key];
  347. if (when.defined(heights)) {
  348. maxTerrainHeight = heights[1];
  349. }
  350. }
  351. var result = BoundingSphere.BoundingSphere.fromRectangle3D(rectangle, ellipsoid, 0.0);
  352. BoundingSphere.BoundingSphere.fromRectangle3D(rectangle, ellipsoid, maxTerrainHeight, scratchBoundingSphere);
  353. return BoundingSphere.BoundingSphere.union(result, scratchBoundingSphere, result);
  354. };
  355. function getTileXYLevel(rectangle) {
  356. Cartographic.Cartographic.fromRadians(rectangle.east, rectangle.north, 0.0, scratchCorners[0]);
  357. Cartographic.Cartographic.fromRadians(rectangle.west, rectangle.north, 0.0, scratchCorners[1]);
  358. Cartographic.Cartographic.fromRadians(rectangle.east, rectangle.south, 0.0, scratchCorners[2]);
  359. Cartographic.Cartographic.fromRadians(rectangle.west, rectangle.south, 0.0, scratchCorners[3]);
  360. // Determine which tile the bounding rectangle is in
  361. var lastLevelX = 0, lastLevelY = 0;
  362. var currentX = 0, currentY = 0;
  363. var maxLevel = ApproximateTerrainHeights._terrainHeightsMaxLevel;
  364. var i;
  365. for(i = 0; i <= maxLevel; ++i) {
  366. var failed = false;
  367. for(var j = 0; j < 4; ++j) {
  368. var corner = scratchCorners[j];
  369. tilingScheme.positionToTileXY(corner, i, scratchTileXY);
  370. if (j === 0) {
  371. currentX = scratchTileXY.x;
  372. currentY = scratchTileXY.y;
  373. } else if(currentX !== scratchTileXY.x || currentY !== scratchTileXY.y) {
  374. failed = true;
  375. break;
  376. }
  377. }
  378. if (failed) {
  379. break;
  380. }
  381. lastLevelX = currentX;
  382. lastLevelY = currentY;
  383. }
  384. if (i === 0) {
  385. return undefined;
  386. }
  387. return {
  388. x : lastLevelX,
  389. y : lastLevelY,
  390. level : (i > maxLevel) ? maxLevel : (i - 1)
  391. };
  392. }
  393. ApproximateTerrainHeights._terrainHeightsMaxLevel = 6;
  394. ApproximateTerrainHeights._defaultMaxTerrainHeight = 9000.0;
  395. ApproximateTerrainHeights._defaultMinTerrainHeight = -100000.0;
  396. ApproximateTerrainHeights._terrainHeights = undefined;
  397. ApproximateTerrainHeights._initPromise = undefined;
  398. Object.defineProperties(ApproximateTerrainHeights, {
  399. /**
  400. * Determines if the terrain heights are initialized and ready to use. To initialize the terrain heights,
  401. * call {@link ApproximateTerrainHeights#initialize} and wait for the returned promise to resolve.
  402. * @type {Boolean}
  403. * @readonly
  404. * @memberof ApproximateTerrainHeights
  405. */
  406. initialized: {
  407. get: function() {
  408. return when.defined(ApproximateTerrainHeights._terrainHeights);
  409. }
  410. }
  411. });
  412. var PROJECTIONS = [BoundingSphere.GeographicProjection, WebMercatorProjection.WebMercatorProjection];
  413. var PROJECTION_COUNT = PROJECTIONS.length;
  414. var MITER_BREAK_SMALL = Math.cos(_Math.CesiumMath.toRadians(30.0));
  415. var MITER_BREAK_LARGE = Math.cos(_Math.CesiumMath.toRadians(150.0));
  416. // Initial heights for constructing the wall.
  417. // Keeping WALL_INITIAL_MIN_HEIGHT near the ellipsoid surface helps
  418. // prevent precision problems with planes in the shader.
  419. // Putting the start point of a plane at ApproximateTerrainHeights._defaultMinTerrainHeight,
  420. // which is a highly conservative bound, usually puts the plane origin several thousands
  421. // of meters away from the actual terrain, causing floating point problems when checking
  422. // fragments on terrain against the plane.
  423. // Ellipsoid height is generally much closer.
  424. // The initial max height is arbitrary.
  425. // Both heights are corrected using ApproximateTerrainHeights for computing the actual volume geometry.
  426. var WALL_INITIAL_MIN_HEIGHT = 0.0;
  427. var WALL_INITIAL_MAX_HEIGHT = 1000.0;
  428. /**
  429. * A description of a polyline on terrain or 3D Tiles. Only to be used with {@link GroundPolylinePrimitive}.
  430. *
  431. * @alias GroundPolylineGeometry
  432. * @constructor
  433. *
  434. * @param {Object} options Options with the following properties:
  435. * @param {Cartesian3[]} options.positions An array of {@link Cartesian3} defining the polyline's points. Heights above the ellipsoid will be ignored.
  436. * @param {Number} [options.width=1.0] The screen space width in pixels.
  437. * @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.
  438. * @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.
  439. * @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}.
  440. *
  441. * @exception {DeveloperError} At least two positions are required.
  442. *
  443. * @see GroundPolylinePrimitive
  444. *
  445. * @example
  446. * var positions = Cesium.Cartesian3.fromDegreesArray([
  447. * -112.1340164450331, 36.05494287836128,
  448. * -112.08821010582645, 36.097804071380715,
  449. * -112.13296079730024, 36.168769146801104
  450. * ]);
  451. *
  452. * var geometry = new Cesium.GroundPolylineGeometry({
  453. * positions : positions
  454. * });
  455. */
  456. function GroundPolylineGeometry(options) {
  457. options = when.defaultValue(options, when.defaultValue.EMPTY_OBJECT);
  458. var positions = options.positions;
  459. //>>includeStart('debug', pragmas.debug);
  460. if ((!when.defined(positions)) || (positions.length < 2)) {
  461. throw new Check.DeveloperError('At least two positions are required.');
  462. }
  463. if (when.defined(options.arcType) && options.arcType !== ArcType.ArcType.GEODESIC && options.arcType !== ArcType.ArcType.RHUMB) {
  464. throw new Check.DeveloperError('Valid options for arcType are ArcType.GEODESIC and ArcType.RHUMB.');
  465. }
  466. //>>includeEnd('debug');
  467. /**
  468. * The screen space width in pixels.
  469. * @type {Number}
  470. */
  471. this.width = when.defaultValue(options.width, 1.0); // Doesn't get packed, not necessary for computing geometry.
  472. this._positions = positions;
  473. /**
  474. * The distance interval used for interpolating options.points. Zero indicates no interpolation.
  475. * Default of 9999.0 allows centimeter accuracy with 32 bit floating point.
  476. * @type {Boolean}
  477. * @default 9999.0
  478. */
  479. this.granularity = when.defaultValue(options.granularity, 9999.0);
  480. /**
  481. * Whether during geometry creation a line segment will be added between the last and first line positions to make this Polyline a loop.
  482. * If the geometry has two positions this parameter will be ignored.
  483. * @type {Boolean}
  484. * @default false
  485. */
  486. this.loop = when.defaultValue(options.loop, false);
  487. /**
  488. * The type of path the polyline must follow. Valid options are {@link ArcType.GEODESIC} and {@link ArcType.RHUMB}.
  489. * @type {ArcType}
  490. * @default ArcType.GEODESIC
  491. */
  492. this.arcType = when.defaultValue(options.arcType, ArcType.ArcType.GEODESIC);
  493. this._ellipsoid = when.defaultValue(options.ellipsoid, Cartesian2.Ellipsoid.WGS84);
  494. // MapProjections can't be packed, so store the index to a known MapProjection.
  495. this._projectionIndex = 0;
  496. this._workerName = 'createGroundPolylineGeometry';
  497. // Used by GroundPolylinePrimitive to signal worker that scenemode is 3D only.
  498. this._scene3DOnly = false;
  499. }
  500. Object.defineProperties(GroundPolylineGeometry.prototype, {
  501. /**
  502. * The number of elements used to pack the object into an array.
  503. * @memberof GroundPolylineGeometry.prototype
  504. * @type {Number}
  505. * @readonly
  506. * @private
  507. */
  508. packedLength: {
  509. get: function() {
  510. return 1.0 + this._positions.length * 3 + 1.0 + 1.0 + 1.0 + Cartesian2.Ellipsoid.packedLength + 1.0 + 1.0;
  511. }
  512. }
  513. });
  514. /**
  515. * Set the GroundPolylineGeometry's projection and ellipsoid.
  516. * Used by GroundPolylinePrimitive to signal scene information to the geometry for generating 2D attributes.
  517. *
  518. * @param {GroundPolylineGeometry} groundPolylineGeometry GroundPolylinGeometry describing a polyline on terrain or 3D Tiles.
  519. * @param {Projection} mapProjection A MapProjection used for projecting cartographic coordinates to 2D.
  520. * @private
  521. */
  522. GroundPolylineGeometry.setProjectionAndEllipsoid = function(groundPolylineGeometry, mapProjection) {
  523. var projectionIndex = 0;
  524. for (var i = 0; i < PROJECTION_COUNT; i++) {
  525. if (mapProjection instanceof PROJECTIONS[i]) {
  526. projectionIndex = i;
  527. break;
  528. }
  529. }
  530. groundPolylineGeometry._projectionIndex = projectionIndex;
  531. groundPolylineGeometry._ellipsoid = mapProjection.ellipsoid;
  532. };
  533. var cart3Scratch1 = new Cartographic.Cartesian3();
  534. var cart3Scratch2 = new Cartographic.Cartesian3();
  535. var cart3Scratch3 = new Cartographic.Cartesian3();
  536. function computeRightNormal(start, end, maxHeight, ellipsoid, result) {
  537. var startBottom = getPosition(ellipsoid, start, 0.0, cart3Scratch1);
  538. var startTop = getPosition(ellipsoid, start, maxHeight, cart3Scratch2);
  539. var endBottom = getPosition(ellipsoid, end, 0.0, cart3Scratch3);
  540. var up = direction(startTop, startBottom, cart3Scratch2);
  541. var forward = direction(endBottom, startBottom, cart3Scratch3);
  542. Cartographic.Cartesian3.cross(forward, up, result);
  543. return Cartographic.Cartesian3.normalize(result, result);
  544. }
  545. var interpolatedCartographicScratch = new Cartographic.Cartographic();
  546. var interpolatedBottomScratch = new Cartographic.Cartesian3();
  547. var interpolatedTopScratch = new Cartographic.Cartesian3();
  548. var interpolatedNormalScratch = new Cartographic.Cartesian3();
  549. function interpolateSegment(start, end, minHeight, maxHeight, granularity, arcType, ellipsoid, normalsArray, bottomPositionsArray, topPositionsArray, cartographicsArray) {
  550. if (granularity === 0.0) {
  551. return;
  552. }
  553. var ellipsoidLine;
  554. if (arcType === ArcType.ArcType.GEODESIC) {
  555. ellipsoidLine = new EllipsoidGeodesic.EllipsoidGeodesic(start, end, ellipsoid);
  556. } else if (arcType === ArcType.ArcType.RHUMB) {
  557. ellipsoidLine = new EllipsoidRhumbLine.EllipsoidRhumbLine(start, end, ellipsoid);
  558. }
  559. var surfaceDistance = ellipsoidLine.surfaceDistance;
  560. if (surfaceDistance < granularity) {
  561. return;
  562. }
  563. // Compute rightwards normal applicable at all interpolated points
  564. var interpolatedNormal = computeRightNormal(start, end, maxHeight, ellipsoid, interpolatedNormalScratch);
  565. var segments = Math.ceil(surfaceDistance / granularity);
  566. var interpointDistance = surfaceDistance / segments;
  567. var distanceFromStart = interpointDistance;
  568. var pointsToAdd = segments - 1;
  569. var packIndex = normalsArray.length;
  570. for (var i = 0; i < pointsToAdd; i++) {
  571. var interpolatedCartographic = ellipsoidLine.interpolateUsingSurfaceDistance(distanceFromStart, interpolatedCartographicScratch);
  572. var interpolatedBottom = getPosition(ellipsoid, interpolatedCartographic, minHeight, interpolatedBottomScratch);
  573. var interpolatedTop = getPosition(ellipsoid, interpolatedCartographic, maxHeight, interpolatedTopScratch);
  574. Cartographic.Cartesian3.pack(interpolatedNormal, normalsArray, packIndex);
  575. Cartographic.Cartesian3.pack(interpolatedBottom, bottomPositionsArray, packIndex);
  576. Cartographic.Cartesian3.pack(interpolatedTop, topPositionsArray, packIndex);
  577. cartographicsArray.push(interpolatedCartographic.latitude);
  578. cartographicsArray.push(interpolatedCartographic.longitude);
  579. packIndex += 3;
  580. distanceFromStart += interpointDistance;
  581. }
  582. }
  583. var heightlessCartographicScratch = new Cartographic.Cartographic();
  584. function getPosition(ellipsoid, cartographic, height, result) {
  585. Cartographic.Cartographic.clone(cartographic, heightlessCartographicScratch);
  586. heightlessCartographicScratch.height = height;
  587. return Cartographic.Cartographic.toCartesian(heightlessCartographicScratch, ellipsoid, result);
  588. }
  589. /**
  590. * Stores the provided instance into the provided array.
  591. *
  592. * @param {PolygonGeometry} value The value to pack.
  593. * @param {Number[]} array The array to pack into.
  594. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements.
  595. *
  596. * @returns {Number[]} The array that was packed into
  597. */
  598. GroundPolylineGeometry.pack = function(value, array, startingIndex) {
  599. //>>includeStart('debug', pragmas.debug);
  600. Check.Check.typeOf.object('value', value);
  601. Check.Check.defined('array', array);
  602. //>>includeEnd('debug');
  603. var index = when.defaultValue(startingIndex, 0);
  604. var positions = value._positions;
  605. var positionsLength = positions.length;
  606. array[index++] = positionsLength;
  607. for (var i = 0; i < positionsLength; ++i) {
  608. var cartesian = positions[i];
  609. Cartographic.Cartesian3.pack(cartesian, array, index);
  610. index += 3;
  611. }
  612. array[index++] = value.granularity;
  613. array[index++] = value.loop ? 1.0 : 0.0;
  614. array[index++] = value.arcType;
  615. Cartesian2.Ellipsoid.pack(value._ellipsoid, array, index);
  616. index += Cartesian2.Ellipsoid.packedLength;
  617. array[index++] = value._projectionIndex;
  618. array[index++] = value._scene3DOnly ? 1.0 : 0.0;
  619. return array;
  620. };
  621. /**
  622. * Retrieves an instance from a packed array.
  623. *
  624. * @param {Number[]} array The packed array.
  625. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked.
  626. * @param {PolygonGeometry} [result] The object into which to store the result.
  627. */
  628. GroundPolylineGeometry.unpack = function(array, startingIndex, result) {
  629. //>>includeStart('debug', pragmas.debug);
  630. Check.Check.defined('array', array);
  631. //>>includeEnd('debug');
  632. var index = when.defaultValue(startingIndex, 0);
  633. var positionsLength = array[index++];
  634. var positions = new Array(positionsLength);
  635. for (var i = 0; i < positionsLength; i++) {
  636. positions[i] = Cartographic.Cartesian3.unpack(array, index);
  637. index += 3;
  638. }
  639. var granularity = array[index++];
  640. var loop = array[index++] === 1.0;
  641. var arcType = array[index++];
  642. var ellipsoid = Cartesian2.Ellipsoid.unpack(array, index);
  643. index += Cartesian2.Ellipsoid.packedLength;
  644. var projectionIndex = array[index++];
  645. var scene3DOnly = (array[index++] === 1.0);
  646. if (!when.defined(result)) {
  647. var geometry = new GroundPolylineGeometry({
  648. positions : positions,
  649. granularity : granularity,
  650. loop : loop,
  651. arcType : arcType,
  652. ellipsoid : ellipsoid
  653. });
  654. geometry._projectionIndex = projectionIndex;
  655. geometry._scene3DOnly = scene3DOnly;
  656. return geometry;
  657. }
  658. result._positions = positions;
  659. result.granularity = granularity;
  660. result.loop = loop;
  661. result.arcType = arcType;
  662. result._ellipsoid = ellipsoid;
  663. result._projectionIndex = projectionIndex;
  664. result._scene3DOnly = scene3DOnly;
  665. return result;
  666. };
  667. function direction(target, origin, result) {
  668. Cartographic.Cartesian3.subtract(target, origin, result);
  669. Cartographic.Cartesian3.normalize(result, result);
  670. return result;
  671. }
  672. var toPreviousScratch = new Cartographic.Cartesian3();
  673. var toNextScratch = new Cartographic.Cartesian3();
  674. var forwardScratch = new Cartographic.Cartesian3();
  675. var coplanarNormalScratch = new Cartographic.Cartesian3();
  676. var coplanarPlaneScratch = new Plane.Plane(Cartographic.Cartesian3.UNIT_X, 0.0);
  677. var vertexUpScratch = new Cartographic.Cartesian3();
  678. var cosine90 = 0.0;
  679. function computeVertexMiterNormal(previousBottom, vertexBottom, vertexTop, nextBottom, result) {
  680. var up = direction(vertexTop, vertexBottom, vertexUpScratch);
  681. var toPrevious = direction(previousBottom, vertexBottom, toPreviousScratch);
  682. var toNext = direction(nextBottom, vertexBottom, toNextScratch);
  683. // Check if points are coplanar in a right-side-pointing plane that contains "up."
  684. // This is roughly equivalent to the points being colinear in cartographic space.
  685. var coplanarNormal = Cartographic.Cartesian3.cross(up, toPrevious, coplanarNormalScratch);
  686. coplanarNormal = Cartographic.Cartesian3.normalize(coplanarNormal, coplanarNormal);
  687. var coplanarPlane = Plane.Plane.fromPointNormal(vertexBottom, coplanarNormal, coplanarPlaneScratch);
  688. var nextBottomDistance = Plane.Plane.getPointDistance(coplanarPlane, nextBottom);
  689. if (_Math.CesiumMath.equalsEpsilon(nextBottomDistance, 0.0, _Math.CesiumMath.EPSILON7)) {
  690. // If the points are coplanar, point the normal in the direction of the plane
  691. Cartographic.Cartesian3.clone(coplanarNormal, result);
  692. return result;
  693. }
  694. // Average directions to previous and to next
  695. result = Cartographic.Cartesian3.add(toNext, toPrevious, result);
  696. result = Cartographic.Cartesian3.normalize(result, result);
  697. // Rotate this direction to be orthogonal to up
  698. var forward = Cartographic.Cartesian3.cross(up, result, forwardScratch);
  699. Cartographic.Cartesian3.normalize(forward, forward);
  700. Cartographic.Cartesian3.cross(forward, up, result);
  701. Cartographic.Cartesian3.normalize(result, result);
  702. // Flip the normal if it isn't pointing roughly bound right (aka if forward is pointing more "backwards")
  703. if (Cartographic.Cartesian3.dot(toNext, forward) < cosine90) {
  704. result = Cartographic.Cartesian3.negate(result, result);
  705. }
  706. return result;
  707. }
  708. var XZ_PLANE = Plane.Plane.fromPointNormal(Cartographic.Cartesian3.ZERO, Cartographic.Cartesian3.UNIT_Y);
  709. var previousBottomScratch = new Cartographic.Cartesian3();
  710. var vertexBottomScratch = new Cartographic.Cartesian3();
  711. var vertexTopScratch = new Cartographic.Cartesian3();
  712. var nextBottomScratch = new Cartographic.Cartesian3();
  713. var vertexNormalScratch = new Cartographic.Cartesian3();
  714. var intersectionScratch = new Cartographic.Cartesian3();
  715. var cartographicScratch0 = new Cartographic.Cartographic();
  716. var cartographicScratch1 = new Cartographic.Cartographic();
  717. var cartographicIntersectionScratch = new Cartographic.Cartographic();
  718. /**
  719. * Computes shadow volumes for the ground polyline, consisting of its vertices, indices, and a bounding sphere.
  720. * Vertices are "fat," packing all the data needed in each volume to describe a line on terrain or 3D Tiles.
  721. * Should not be called independent of {@link GroundPolylinePrimitive}.
  722. *
  723. * @param {GroundPolylineGeometry} groundPolylineGeometry
  724. * @private
  725. */
  726. GroundPolylineGeometry.createGeometry = function(groundPolylineGeometry) {
  727. var compute2dAttributes = !groundPolylineGeometry._scene3DOnly;
  728. var loop = groundPolylineGeometry.loop;
  729. var ellipsoid = groundPolylineGeometry._ellipsoid;
  730. var granularity = groundPolylineGeometry.granularity;
  731. var arcType = groundPolylineGeometry.arcType;
  732. var projection = new PROJECTIONS[groundPolylineGeometry._projectionIndex](ellipsoid);
  733. var minHeight = WALL_INITIAL_MIN_HEIGHT;
  734. var maxHeight = WALL_INITIAL_MAX_HEIGHT;
  735. var index;
  736. var i;
  737. var positions = groundPolylineGeometry._positions;
  738. var positionsLength = positions.length;
  739. if (positionsLength === 2) {
  740. loop = false;
  741. }
  742. // Split positions across the IDL and the Prime Meridian as well.
  743. // Split across prime meridian because very large geometries crossing the Prime Meridian but not the IDL
  744. // may get split by the plane of IDL + Prime Meridian.
  745. var p0;
  746. var p1;
  747. var c0;
  748. var c1;
  749. var rhumbLine = new EllipsoidRhumbLine.EllipsoidRhumbLine(undefined, undefined, ellipsoid);
  750. var intersection;
  751. var intersectionCartographic;
  752. var intersectionLongitude;
  753. var splitPositions = [positions[0]];
  754. for (i = 0; i < positionsLength - 1; i++) {
  755. p0 = positions[i];
  756. p1 = positions[i + 1];
  757. intersection = IntersectionTests.IntersectionTests.lineSegmentPlane(p0, p1, XZ_PLANE, intersectionScratch);
  758. if (when.defined(intersection) &&
  759. !Cartographic.Cartesian3.equalsEpsilon(intersection, p0, _Math.CesiumMath.EPSILON7) &&
  760. !Cartographic.Cartesian3.equalsEpsilon(intersection, p1, _Math.CesiumMath.EPSILON7)) {
  761. if (groundPolylineGeometry.arcType === ArcType.ArcType.GEODESIC) {
  762. splitPositions.push(Cartographic.Cartesian3.clone(intersection));
  763. } else if (groundPolylineGeometry.arcType === ArcType.ArcType.RHUMB) {
  764. intersectionLongitude = ellipsoid.cartesianToCartographic(intersection, cartographicScratch0).longitude;
  765. c0 = ellipsoid.cartesianToCartographic(p0, cartographicScratch0);
  766. c1 = ellipsoid.cartesianToCartographic(p1, cartographicScratch1);
  767. rhumbLine.setEndPoints(c0, c1);
  768. intersectionCartographic = rhumbLine.findIntersectionWithLongitude(intersectionLongitude, cartographicIntersectionScratch);
  769. intersection = ellipsoid.cartographicToCartesian(intersectionCartographic, intersectionScratch);
  770. if (when.defined(intersection) &&
  771. !Cartographic.Cartesian3.equalsEpsilon(intersection, p0, _Math.CesiumMath.EPSILON7) &&
  772. !Cartographic.Cartesian3.equalsEpsilon(intersection, p1, _Math.CesiumMath.EPSILON7)) {
  773. splitPositions.push(Cartographic.Cartesian3.clone(intersection));
  774. }
  775. }
  776. }
  777. splitPositions.push(p1);
  778. }
  779. if (loop) {
  780. p0 = positions[positionsLength - 1];
  781. p1 = positions[0];
  782. intersection = IntersectionTests.IntersectionTests.lineSegmentPlane(p0, p1, XZ_PLANE, intersectionScratch);
  783. if (when.defined(intersection) &&
  784. !Cartographic.Cartesian3.equalsEpsilon(intersection, p0, _Math.CesiumMath.EPSILON7) &&
  785. !Cartographic.Cartesian3.equalsEpsilon(intersection, p1, _Math.CesiumMath.EPSILON7)) {
  786. if (groundPolylineGeometry.arcType === ArcType.ArcType.GEODESIC) {
  787. splitPositions.push(Cartographic.Cartesian3.clone(intersection));
  788. } else if (groundPolylineGeometry.arcType === ArcType.ArcType.RHUMB) {
  789. intersectionLongitude = ellipsoid.cartesianToCartographic(intersection, cartographicScratch0).longitude;
  790. c0 = ellipsoid.cartesianToCartographic(p0, cartographicScratch0);
  791. c1 = ellipsoid.cartesianToCartographic(p1, cartographicScratch1);
  792. rhumbLine.setEndPoints(c0, c1);
  793. intersectionCartographic = rhumbLine.findIntersectionWithLongitude(intersectionLongitude, cartographicIntersectionScratch);
  794. intersection = ellipsoid.cartographicToCartesian(intersectionCartographic, intersectionScratch);
  795. if (when.defined(intersection) &&
  796. !Cartographic.Cartesian3.equalsEpsilon(intersection, p0, _Math.CesiumMath.EPSILON7) &&
  797. !Cartographic.Cartesian3.equalsEpsilon(intersection, p1, _Math.CesiumMath.EPSILON7)) {
  798. splitPositions.push(Cartographic.Cartesian3.clone(intersection));
  799. }
  800. }
  801. }
  802. }
  803. var cartographicsLength = splitPositions.length;
  804. var cartographics = new Array(cartographicsLength);
  805. for (i = 0; i < cartographicsLength; i++) {
  806. var cartographic = Cartographic.Cartographic.fromCartesian(splitPositions[i], ellipsoid);
  807. cartographic.height = 0.0;
  808. cartographics[i] = cartographic;
  809. }
  810. cartographics = arrayRemoveDuplicates.arrayRemoveDuplicates(cartographics, Cartographic.Cartographic.equalsEpsilon);
  811. cartographicsLength = cartographics.length;
  812. if (cartographicsLength < 2) {
  813. return undefined;
  814. }
  815. /**** Build heap-side arrays for positions, interpolated cartographics, and normals from which to compute vertices ****/
  816. // We build a "wall" and then decompose it into separately connected component "volumes" because we need a lot
  817. // of information about the wall. Also, this simplifies interpolation.
  818. // Convention: "next" and "end" are locally forward to each segment of the wall,
  819. // and we are computing normals pointing towards the local right side of the vertices in each segment.
  820. var cartographicsArray = [];
  821. var normalsArray = [];
  822. var bottomPositionsArray = [];
  823. var topPositionsArray = [];
  824. var previousBottom = previousBottomScratch;
  825. var vertexBottom = vertexBottomScratch;
  826. var vertexTop = vertexTopScratch;
  827. var nextBottom = nextBottomScratch;
  828. var vertexNormal = vertexNormalScratch;
  829. // First point - either loop or attach a "perpendicular" normal
  830. var startCartographic = cartographics[0];
  831. var nextCartographic = cartographics[1];
  832. var prestartCartographic = cartographics[cartographicsLength - 1];
  833. previousBottom = getPosition(ellipsoid, prestartCartographic, minHeight, previousBottom);
  834. nextBottom = getPosition(ellipsoid, nextCartographic, minHeight, nextBottom);
  835. vertexBottom = getPosition(ellipsoid, startCartographic, minHeight, vertexBottom);
  836. vertexTop = getPosition(ellipsoid, startCartographic, maxHeight, vertexTop);
  837. if (loop) {
  838. vertexNormal = computeVertexMiterNormal(previousBottom, vertexBottom, vertexTop, nextBottom, vertexNormal);
  839. } else {
  840. vertexNormal = computeRightNormal(startCartographic, nextCartographic, maxHeight, ellipsoid, vertexNormal);
  841. }
  842. Cartographic.Cartesian3.pack(vertexNormal, normalsArray, 0);
  843. Cartographic.Cartesian3.pack(vertexBottom, bottomPositionsArray, 0);
  844. Cartographic.Cartesian3.pack(vertexTop, topPositionsArray, 0);
  845. cartographicsArray.push(startCartographic.latitude);
  846. cartographicsArray.push(startCartographic.longitude);
  847. interpolateSegment(startCartographic, nextCartographic, minHeight, maxHeight, granularity, arcType, ellipsoid, normalsArray, bottomPositionsArray, topPositionsArray, cartographicsArray);
  848. // All inbetween points
  849. for (i = 1; i < cartographicsLength - 1; ++i) {
  850. previousBottom = Cartographic.Cartesian3.clone(vertexBottom, previousBottom);
  851. vertexBottom = Cartographic.Cartesian3.clone(nextBottom, vertexBottom);
  852. var vertexCartographic = cartographics[i];
  853. getPosition(ellipsoid, vertexCartographic, maxHeight, vertexTop);
  854. getPosition(ellipsoid, cartographics[i + 1], minHeight, nextBottom);
  855. computeVertexMiterNormal(previousBottom, vertexBottom, vertexTop, nextBottom, vertexNormal);
  856. index = normalsArray.length;
  857. Cartographic.Cartesian3.pack(vertexNormal, normalsArray, index);
  858. Cartographic.Cartesian3.pack(vertexBottom, bottomPositionsArray, index);
  859. Cartographic.Cartesian3.pack(vertexTop, topPositionsArray, index);
  860. cartographicsArray.push(vertexCartographic.latitude);
  861. cartographicsArray.push(vertexCartographic.longitude);
  862. interpolateSegment(cartographics[i], cartographics[i + 1], minHeight, maxHeight, granularity, arcType, ellipsoid, normalsArray, bottomPositionsArray, topPositionsArray, cartographicsArray);
  863. }
  864. // Last point - either loop or attach a normal "perpendicular" to the wall.
  865. var endCartographic = cartographics[cartographicsLength - 1];
  866. var preEndCartographic = cartographics[cartographicsLength - 2];
  867. vertexBottom = getPosition(ellipsoid, endCartographic, minHeight, vertexBottom);
  868. vertexTop = getPosition(ellipsoid, endCartographic, maxHeight, vertexTop);
  869. if (loop) {
  870. var postEndCartographic = cartographics[0];
  871. previousBottom = getPosition(ellipsoid, preEndCartographic, minHeight, previousBottom);
  872. nextBottom = getPosition(ellipsoid, postEndCartographic, minHeight, nextBottom);
  873. vertexNormal = computeVertexMiterNormal(previousBottom, vertexBottom, vertexTop, nextBottom, vertexNormal);
  874. } else {
  875. vertexNormal = computeRightNormal(preEndCartographic, endCartographic, maxHeight, ellipsoid, vertexNormal);
  876. }
  877. index = normalsArray.length;
  878. Cartographic.Cartesian3.pack(vertexNormal, normalsArray, index);
  879. Cartographic.Cartesian3.pack(vertexBottom, bottomPositionsArray, index);
  880. Cartographic.Cartesian3.pack(vertexTop, topPositionsArray, index);
  881. cartographicsArray.push(endCartographic.latitude);
  882. cartographicsArray.push(endCartographic.longitude);
  883. if (loop) {
  884. interpolateSegment(endCartographic, startCartographic, minHeight, maxHeight, granularity, arcType, ellipsoid, normalsArray, bottomPositionsArray, topPositionsArray, cartographicsArray);
  885. index = normalsArray.length;
  886. for (i = 0; i < 3; ++i) {
  887. normalsArray[index + i] = normalsArray[i];
  888. bottomPositionsArray[index + i] = bottomPositionsArray[i];
  889. topPositionsArray[index + i] = topPositionsArray[i];
  890. }
  891. cartographicsArray.push(startCartographic.latitude);
  892. cartographicsArray.push(startCartographic.longitude);
  893. }
  894. return generateGeometryAttributes(loop, projection, bottomPositionsArray, topPositionsArray, normalsArray, cartographicsArray, compute2dAttributes);
  895. };
  896. // If the end normal angle is too steep compared to the direction of the line segment,
  897. // "break" the miter by rotating the normal 90 degrees around the "up" direction at the point
  898. // For ultra precision we would want to project into a plane, but in practice this is sufficient.
  899. var lineDirectionScratch = new Cartographic.Cartesian3();
  900. var matrix3Scratch = new BoundingSphere.Matrix3();
  901. var quaternionScratch = new Transforms.Quaternion();
  902. function breakMiter(endGeometryNormal, startBottom, endBottom, endTop) {
  903. var lineDirection = direction(endBottom, startBottom, lineDirectionScratch);
  904. var dot = Cartographic.Cartesian3.dot(lineDirection, endGeometryNormal);
  905. if (dot > MITER_BREAK_SMALL || dot < MITER_BREAK_LARGE) {
  906. var vertexUp = direction(endTop, endBottom, vertexUpScratch);
  907. var angle = dot < MITER_BREAK_LARGE ? _Math.CesiumMath.PI_OVER_TWO : -_Math.CesiumMath.PI_OVER_TWO;
  908. var quaternion = Transforms.Quaternion.fromAxisAngle(vertexUp, angle, quaternionScratch);
  909. var rotationMatrix = BoundingSphere.Matrix3.fromQuaternion(quaternion, matrix3Scratch);
  910. BoundingSphere.Matrix3.multiplyByVector(rotationMatrix, endGeometryNormal, endGeometryNormal);
  911. return true;
  912. }
  913. return false;
  914. }
  915. var endPosCartographicScratch = new Cartographic.Cartographic();
  916. var normalStartpointScratch = new Cartographic.Cartesian3();
  917. var normalEndpointScratch = new Cartographic.Cartesian3();
  918. function projectNormal(projection, cartographic, normal, projectedPosition, result) {
  919. var position = Cartographic.Cartographic.toCartesian(cartographic, projection._ellipsoid, normalStartpointScratch);
  920. var normalEndpoint = Cartographic.Cartesian3.add(position, normal, normalEndpointScratch);
  921. var flipNormal = false;
  922. var ellipsoid = projection._ellipsoid;
  923. var normalEndpointCartographic = ellipsoid.cartesianToCartographic(normalEndpoint, endPosCartographicScratch);
  924. // If normal crosses the IDL, go the other way and flip the result.
  925. // In practice this almost never happens because the cartographic start
  926. // and end points of each segment are "nudged" to be on the same side
  927. // of the IDL and slightly away from the IDL.
  928. if (Math.abs(cartographic.longitude - normalEndpointCartographic.longitude) > _Math.CesiumMath.PI_OVER_TWO) {
  929. flipNormal = true;
  930. normalEndpoint = Cartographic.Cartesian3.subtract(position, normal, normalEndpointScratch);
  931. normalEndpointCartographic = ellipsoid.cartesianToCartographic(normalEndpoint, endPosCartographicScratch);
  932. }
  933. normalEndpointCartographic.height = 0.0;
  934. var normalEndpointProjected = projection.project(normalEndpointCartographic, result);
  935. result = Cartographic.Cartesian3.subtract(normalEndpointProjected, projectedPosition, result);
  936. result.z = 0.0;
  937. result = Cartographic.Cartesian3.normalize(result, result);
  938. if (flipNormal) {
  939. Cartographic.Cartesian3.negate(result, result);
  940. }
  941. return result;
  942. }
  943. var adjustHeightNormalScratch = new Cartographic.Cartesian3();
  944. var adjustHeightOffsetScratch = new Cartographic.Cartesian3();
  945. function adjustHeights(bottom, top, minHeight, maxHeight, adjustHeightBottom, adjustHeightTop) {
  946. // bottom and top should be at WALL_INITIAL_MIN_HEIGHT and WALL_INITIAL_MAX_HEIGHT, respectively
  947. var adjustHeightNormal = Cartographic.Cartesian3.subtract(top, bottom, adjustHeightNormalScratch);
  948. Cartographic.Cartesian3.normalize(adjustHeightNormal, adjustHeightNormal);
  949. var distanceForBottom = minHeight - WALL_INITIAL_MIN_HEIGHT;
  950. var adjustHeightOffset = Cartographic.Cartesian3.multiplyByScalar(adjustHeightNormal, distanceForBottom, adjustHeightOffsetScratch);
  951. Cartographic.Cartesian3.add(bottom, adjustHeightOffset, adjustHeightBottom);
  952. var distanceForTop = maxHeight - WALL_INITIAL_MAX_HEIGHT;
  953. adjustHeightOffset = Cartographic.Cartesian3.multiplyByScalar(adjustHeightNormal, distanceForTop, adjustHeightOffsetScratch);
  954. Cartographic.Cartesian3.add(top, adjustHeightOffset, adjustHeightTop);
  955. }
  956. var nudgeDirectionScratch = new Cartographic.Cartesian3();
  957. function nudgeXZ(start, end) {
  958. var startToXZdistance = Plane.Plane.getPointDistance(XZ_PLANE, start);
  959. var endToXZdistance = Plane.Plane.getPointDistance(XZ_PLANE, end);
  960. var offset = nudgeDirectionScratch;
  961. // Larger epsilon than what's used in GeometryPipeline, a centimeter in world space
  962. if (_Math.CesiumMath.equalsEpsilon(startToXZdistance, 0.0, _Math.CesiumMath.EPSILON2)) {
  963. offset = direction(end, start, offset);
  964. Cartographic.Cartesian3.multiplyByScalar(offset, _Math.CesiumMath.EPSILON2, offset);
  965. Cartographic.Cartesian3.add(start, offset, start);
  966. } else if (_Math.CesiumMath.equalsEpsilon(endToXZdistance, 0.0, _Math.CesiumMath.EPSILON2)) {
  967. offset = direction(start, end, offset);
  968. Cartographic.Cartesian3.multiplyByScalar(offset, _Math.CesiumMath.EPSILON2, offset);
  969. Cartographic.Cartesian3.add(end, offset, end);
  970. }
  971. }
  972. // "Nudge" cartographic coordinates so start and end are on the same side of the IDL.
  973. // Nudge amounts are tiny, basically just an IDL flip.
  974. // Only used for 2D/CV.
  975. function nudgeCartographic(start, end) {
  976. var absStartLon = Math.abs(start.longitude);
  977. var absEndLon = Math.abs(end.longitude);
  978. if (_Math.CesiumMath.equalsEpsilon(absStartLon, _Math.CesiumMath.PI, _Math.CesiumMath.EPSILON11)) {
  979. var endSign = _Math.CesiumMath.sign(end.longitude);
  980. start.longitude = endSign * (absStartLon - _Math.CesiumMath.EPSILON11);
  981. return 1;
  982. } else if (_Math.CesiumMath.equalsEpsilon(absEndLon, _Math.CesiumMath.PI, _Math.CesiumMath.EPSILON11)) {
  983. var startSign = _Math.CesiumMath.sign(start.longitude);
  984. end.longitude = startSign * (absEndLon - _Math.CesiumMath.EPSILON11);
  985. return 2;
  986. }
  987. return 0;
  988. }
  989. var startCartographicScratch = new Cartographic.Cartographic();
  990. var endCartographicScratch = new Cartographic.Cartographic();
  991. var segmentStartTopScratch = new Cartographic.Cartesian3();
  992. var segmentEndTopScratch = new Cartographic.Cartesian3();
  993. var segmentStartBottomScratch = new Cartographic.Cartesian3();
  994. var segmentEndBottomScratch = new Cartographic.Cartesian3();
  995. var segmentStartNormalScratch = new Cartographic.Cartesian3();
  996. var segmentEndNormalScratch = new Cartographic.Cartesian3();
  997. var getHeightCartographics = [startCartographicScratch, endCartographicScratch];
  998. var getHeightRectangleScratch = new Cartesian2.Rectangle();
  999. var adjustHeightStartTopScratch = new Cartographic.Cartesian3();
  1000. var adjustHeightEndTopScratch = new Cartographic.Cartesian3();
  1001. var adjustHeightStartBottomScratch = new Cartographic.Cartesian3();
  1002. var adjustHeightEndBottomScratch = new Cartographic.Cartesian3();
  1003. var segmentStart2DScratch = new Cartographic.Cartesian3();
  1004. var segmentEnd2DScratch = new Cartographic.Cartesian3();
  1005. var segmentStartNormal2DScratch = new Cartographic.Cartesian3();
  1006. var segmentEndNormal2DScratch = new Cartographic.Cartesian3();
  1007. var offsetScratch = new Cartographic.Cartesian3();
  1008. var startUpScratch = new Cartographic.Cartesian3();
  1009. var endUpScratch = new Cartographic.Cartesian3();
  1010. var rightScratch = new Cartographic.Cartesian3();
  1011. var startPlaneNormalScratch = new Cartographic.Cartesian3();
  1012. var endPlaneNormalScratch = new Cartographic.Cartesian3();
  1013. var encodeScratch = new EncodedCartesian3.EncodedCartesian3();
  1014. var encodeScratch2D = new EncodedCartesian3.EncodedCartesian3();
  1015. var forwardOffset2DScratch = new Cartographic.Cartesian3();
  1016. var right2DScratch = new Cartographic.Cartesian3();
  1017. var normalNudgeScratch = new Cartographic.Cartesian3();
  1018. var scratchBoundingSpheres = [new BoundingSphere.BoundingSphere(), new BoundingSphere.BoundingSphere()];
  1019. // Winding order is reversed so each segment's volume is inside-out
  1020. var REFERENCE_INDICES = [
  1021. 0, 2, 1, 0, 3, 2, // right
  1022. 0, 7, 3, 0, 4, 7, // start
  1023. 0, 5, 4, 0, 1, 5, // bottom
  1024. 5, 7, 4, 5, 6, 7, // left
  1025. 5, 2, 6, 5, 1, 2, // end
  1026. 3, 6, 2, 3, 7, 6 // top
  1027. ];
  1028. var REFERENCE_INDICES_LENGTH = REFERENCE_INDICES.length;
  1029. // Decompose the "wall" into a series of shadow volumes.
  1030. // Each shadow volume's vertices encode a description of the line it contains,
  1031. // including mitering planes at the end points, a plane along the line itself,
  1032. // and attributes for computing length-wise texture coordinates.
  1033. function generateGeometryAttributes(loop, projection, bottomPositionsArray, topPositionsArray, normalsArray, cartographicsArray, compute2dAttributes) {
  1034. var i;
  1035. var index;
  1036. var ellipsoid = projection._ellipsoid;
  1037. // Each segment will have 8 vertices
  1038. var segmentCount = (bottomPositionsArray.length / 3) - 1;
  1039. var vertexCount = segmentCount * 8;
  1040. var arraySizeVec4 = vertexCount * 4;
  1041. var indexCount = segmentCount * 36;
  1042. var indices = vertexCount > 65535 ? new Uint32Array(indexCount) : new Uint16Array(indexCount);
  1043. var positionsArray = new Float64Array(vertexCount * 3);
  1044. var startHiAndForwardOffsetX = new Float32Array(arraySizeVec4);
  1045. var startLoAndForwardOffsetY = new Float32Array(arraySizeVec4);
  1046. var startNormalAndForwardOffsetZ = new Float32Array(arraySizeVec4);
  1047. var endNormalAndTextureCoordinateNormalizationX = new Float32Array(arraySizeVec4);
  1048. var rightNormalAndTextureCoordinateNormalizationY = new Float32Array(arraySizeVec4);
  1049. var startHiLo2D;
  1050. var offsetAndRight2D;
  1051. var startEndNormals2D;
  1052. var texcoordNormalization2D;
  1053. if (compute2dAttributes) {
  1054. startHiLo2D = new Float32Array(arraySizeVec4);
  1055. offsetAndRight2D = new Float32Array(arraySizeVec4);
  1056. startEndNormals2D = new Float32Array(arraySizeVec4);
  1057. texcoordNormalization2D = new Float32Array(vertexCount * 2);
  1058. }
  1059. /*** Compute total lengths for texture coordinate normalization ***/
  1060. // 2D
  1061. var cartographicsLength = cartographicsArray.length / 2;
  1062. var length2D = 0.0;
  1063. var startCartographic = startCartographicScratch;
  1064. startCartographic.height = 0.0;
  1065. var endCartographic = endCartographicScratch;
  1066. endCartographic.height = 0.0;
  1067. var segmentStartCartesian = segmentStartTopScratch;
  1068. var segmentEndCartesian = segmentEndTopScratch;
  1069. if (compute2dAttributes) {
  1070. index = 0;
  1071. for (i = 1; i < cartographicsLength; i++) {
  1072. // Don't clone anything from previous segment b/c possible IDL touch
  1073. startCartographic.latitude = cartographicsArray[index];
  1074. startCartographic.longitude = cartographicsArray[index + 1];
  1075. endCartographic.latitude = cartographicsArray[index + 2];
  1076. endCartographic.longitude = cartographicsArray[index + 3];
  1077. segmentStartCartesian = projection.project(startCartographic, segmentStartCartesian);
  1078. segmentEndCartesian = projection.project(endCartographic, segmentEndCartesian);
  1079. length2D += Cartographic.Cartesian3.distance(segmentStartCartesian, segmentEndCartesian);
  1080. index += 2;
  1081. }
  1082. }
  1083. // 3D
  1084. var positionsLength = topPositionsArray.length / 3;
  1085. segmentEndCartesian = Cartographic.Cartesian3.unpack(topPositionsArray, 0, segmentEndCartesian);
  1086. var length3D = 0.0;
  1087. index = 3;
  1088. for (i = 1; i < positionsLength; i++) {
  1089. segmentStartCartesian = Cartographic.Cartesian3.clone(segmentEndCartesian, segmentStartCartesian);
  1090. segmentEndCartesian = Cartographic.Cartesian3.unpack(topPositionsArray, index, segmentEndCartesian);
  1091. length3D += Cartographic.Cartesian3.distance(segmentStartCartesian, segmentEndCartesian);
  1092. index += 3;
  1093. }
  1094. /*** Generate segments ***/
  1095. var j;
  1096. index = 3;
  1097. var cartographicsIndex = 0;
  1098. var vec2sWriteIndex = 0;
  1099. var vec3sWriteIndex = 0;
  1100. var vec4sWriteIndex = 0;
  1101. var miterBroken = false;
  1102. var endBottom = Cartographic.Cartesian3.unpack(bottomPositionsArray, 0, segmentEndBottomScratch);
  1103. var endTop = Cartographic.Cartesian3.unpack(topPositionsArray, 0, segmentEndTopScratch);
  1104. var endGeometryNormal = Cartographic.Cartesian3.unpack(normalsArray, 0, segmentEndNormalScratch);
  1105. if (loop) {
  1106. var preEndBottom = Cartographic.Cartesian3.unpack(bottomPositionsArray, bottomPositionsArray.length - 6, segmentStartBottomScratch);
  1107. if (breakMiter(endGeometryNormal, preEndBottom, endBottom, endTop)) {
  1108. // Miter broken as if for the last point in the loop, needs to be inverted for first point (clone of endBottom)
  1109. endGeometryNormal = Cartographic.Cartesian3.negate(endGeometryNormal, endGeometryNormal);
  1110. }
  1111. }
  1112. var lengthSoFar3D = 0.0;
  1113. var lengthSoFar2D = 0.0;
  1114. // For translating bounding volume
  1115. var sumHeights = 0.0;
  1116. for (i = 0; i < segmentCount; i++) {
  1117. var startBottom = Cartographic.Cartesian3.clone(endBottom, segmentStartBottomScratch);
  1118. var startTop = Cartographic.Cartesian3.clone(endTop, segmentStartTopScratch);
  1119. var startGeometryNormal = Cartographic.Cartesian3.clone(endGeometryNormal, segmentStartNormalScratch);
  1120. if (miterBroken) {
  1121. startGeometryNormal = Cartographic.Cartesian3.negate(startGeometryNormal, startGeometryNormal);
  1122. }
  1123. endBottom = Cartographic.Cartesian3.unpack(bottomPositionsArray, index, segmentEndBottomScratch);
  1124. endTop = Cartographic.Cartesian3.unpack(topPositionsArray, index, segmentEndTopScratch);
  1125. endGeometryNormal = Cartographic.Cartesian3.unpack(normalsArray, index, segmentEndNormalScratch);
  1126. miterBroken = breakMiter(endGeometryNormal, startBottom, endBottom, endTop);
  1127. // 2D - don't clone anything from previous segment b/c possible IDL touch
  1128. startCartographic.latitude = cartographicsArray[cartographicsIndex];
  1129. startCartographic.longitude = cartographicsArray[cartographicsIndex + 1];
  1130. endCartographic.latitude = cartographicsArray[cartographicsIndex + 2];
  1131. endCartographic.longitude = cartographicsArray[cartographicsIndex + 3];
  1132. var start2D;
  1133. var end2D;
  1134. var startGeometryNormal2D;
  1135. var endGeometryNormal2D;
  1136. if (compute2dAttributes) {
  1137. var nudgeResult = nudgeCartographic(startCartographic, endCartographic);
  1138. start2D = projection.project(startCartographic, segmentStart2DScratch);
  1139. end2D = projection.project(endCartographic, segmentEnd2DScratch);
  1140. var direction2D = direction(end2D, start2D, forwardOffset2DScratch);
  1141. direction2D.y = Math.abs(direction2D.y);
  1142. startGeometryNormal2D = segmentStartNormal2DScratch;
  1143. endGeometryNormal2D = segmentEndNormal2DScratch;
  1144. if (nudgeResult === 0 || Cartographic.Cartesian3.dot(direction2D, Cartographic.Cartesian3.UNIT_Y) > MITER_BREAK_SMALL) {
  1145. // No nudge - project the original normal
  1146. // Or, if the line's angle relative to the IDL is very acute,
  1147. // in which case snapping will produce oddly shaped volumes.
  1148. startGeometryNormal2D = projectNormal(projection, startCartographic, startGeometryNormal, start2D, segmentStartNormal2DScratch);
  1149. endGeometryNormal2D = projectNormal(projection, endCartographic, endGeometryNormal, end2D, segmentEndNormal2DScratch);
  1150. } else if (nudgeResult === 1) {
  1151. // Start is close to IDL - snap start normal to align with IDL
  1152. endGeometryNormal2D = projectNormal(projection, endCartographic, endGeometryNormal, end2D, segmentEndNormal2DScratch);
  1153. startGeometryNormal2D.x = 0.0;
  1154. // If start longitude is negative and end longitude is less negative, relative right is unit -Y
  1155. // If start longitude is positive and end longitude is less positive, relative right is unit +Y
  1156. startGeometryNormal2D.y = _Math.CesiumMath.sign(startCartographic.longitude - Math.abs(endCartographic.longitude));
  1157. startGeometryNormal2D.z = 0.0;
  1158. } else {
  1159. // End is close to IDL - snap end normal to align with IDL
  1160. startGeometryNormal2D = projectNormal(projection, startCartographic, startGeometryNormal, start2D, segmentStartNormal2DScratch);
  1161. endGeometryNormal2D.x = 0.0;
  1162. // If end longitude is negative and start longitude is less negative, relative right is unit Y
  1163. // If end longitude is positive and start longitude is less positive, relative right is unit -Y
  1164. endGeometryNormal2D.y = _Math.CesiumMath.sign(startCartographic.longitude - endCartographic.longitude);
  1165. endGeometryNormal2D.z = 0.0;
  1166. }
  1167. }
  1168. /****************************************
  1169. * Geometry descriptors of a "line on terrain,"
  1170. * as opposed to the "shadow volume used to draw
  1171. * the line on terrain":
  1172. * - position of start + offset to end
  1173. * - start, end, and right-facing planes
  1174. * - encoded texture coordinate offsets
  1175. ****************************************/
  1176. /** 3D **/
  1177. var segmentLength3D = Cartographic.Cartesian3.distance(startTop, endTop);
  1178. var encodedStart = EncodedCartesian3.EncodedCartesian3.fromCartesian(startBottom, encodeScratch);
  1179. var forwardOffset = Cartographic.Cartesian3.subtract(endBottom, startBottom, offsetScratch);
  1180. var forward = Cartographic.Cartesian3.normalize(forwardOffset, rightScratch);
  1181. var startUp = Cartographic.Cartesian3.subtract(startTop, startBottom, startUpScratch);
  1182. startUp = Cartographic.Cartesian3.normalize(startUp, startUp);
  1183. var rightNormal = Cartographic.Cartesian3.cross(forward, startUp, rightScratch);
  1184. rightNormal = Cartographic.Cartesian3.normalize(rightNormal, rightNormal);
  1185. var startPlaneNormal = Cartographic.Cartesian3.cross(startUp, startGeometryNormal, startPlaneNormalScratch);
  1186. startPlaneNormal = Cartographic.Cartesian3.normalize(startPlaneNormal, startPlaneNormal);
  1187. var endUp = Cartographic.Cartesian3.subtract(endTop, endBottom, endUpScratch);
  1188. endUp = Cartographic.Cartesian3.normalize(endUp, endUp);
  1189. var endPlaneNormal = Cartographic.Cartesian3.cross(endGeometryNormal, endUp, endPlaneNormalScratch);
  1190. endPlaneNormal = Cartographic.Cartesian3.normalize(endPlaneNormal, endPlaneNormal);
  1191. var texcoordNormalization3DX = segmentLength3D / length3D;
  1192. var texcoordNormalization3DY = lengthSoFar3D / length3D;
  1193. /** 2D **/
  1194. var segmentLength2D = 0.0;
  1195. var encodedStart2D;
  1196. var forwardOffset2D;
  1197. var right2D;
  1198. var texcoordNormalization2DX = 0.0;
  1199. var texcoordNormalization2DY = 0.0;
  1200. if (compute2dAttributes) {
  1201. segmentLength2D = Cartographic.Cartesian3.distance(start2D, end2D);
  1202. encodedStart2D = EncodedCartesian3.EncodedCartesian3.fromCartesian(start2D, encodeScratch2D);
  1203. forwardOffset2D = Cartographic.Cartesian3.subtract(end2D, start2D, forwardOffset2DScratch);
  1204. // Right direction is just forward direction rotated by -90 degrees around Z
  1205. // Similarly with plane normals
  1206. right2D = Cartographic.Cartesian3.normalize(forwardOffset2D, right2DScratch);
  1207. var swap = right2D.x;
  1208. right2D.x = right2D.y;
  1209. right2D.y = -swap;
  1210. texcoordNormalization2DX = segmentLength2D / length2D;
  1211. texcoordNormalization2DY = lengthSoFar2D / length2D;
  1212. }
  1213. /** Pack **/
  1214. for (j = 0; j < 8; j++) {
  1215. var vec4Index = vec4sWriteIndex + j * 4;
  1216. var vec2Index = vec2sWriteIndex + j * 2;
  1217. var wIndex = vec4Index + 3;
  1218. // Encode sidedness of vertex relative to right plane in texture coordinate normalization X,
  1219. // whether vertex is top or bottom of volume in sign/magnitude of normalization Y.
  1220. var rightPlaneSide = j < 4 ? 1.0 : -1.0;
  1221. var topBottomSide = (j === 2 || j === 3 || j === 6 || j === 7) ? 1.0 : -1.0;
  1222. // 3D
  1223. Cartographic.Cartesian3.pack(encodedStart.high, startHiAndForwardOffsetX, vec4Index);
  1224. startHiAndForwardOffsetX[wIndex] = forwardOffset.x;
  1225. Cartographic.Cartesian3.pack(encodedStart.low, startLoAndForwardOffsetY, vec4Index);
  1226. startLoAndForwardOffsetY[wIndex] = forwardOffset.y;
  1227. Cartographic.Cartesian3.pack(startPlaneNormal, startNormalAndForwardOffsetZ, vec4Index);
  1228. startNormalAndForwardOffsetZ[wIndex] = forwardOffset.z;
  1229. Cartographic.Cartesian3.pack(endPlaneNormal, endNormalAndTextureCoordinateNormalizationX, vec4Index);
  1230. endNormalAndTextureCoordinateNormalizationX[wIndex] = texcoordNormalization3DX * rightPlaneSide;
  1231. Cartographic.Cartesian3.pack(rightNormal, rightNormalAndTextureCoordinateNormalizationY, vec4Index);
  1232. var texcoordNormalization = texcoordNormalization3DY * topBottomSide;
  1233. if (texcoordNormalization === 0.0 && topBottomSide < 0.0) {
  1234. texcoordNormalization = Number.POSITIVE_INFINITY;
  1235. }
  1236. rightNormalAndTextureCoordinateNormalizationY[wIndex] = texcoordNormalization;
  1237. // 2D
  1238. if (compute2dAttributes) {
  1239. startHiLo2D[vec4Index] = encodedStart2D.high.x;
  1240. startHiLo2D[vec4Index + 1] = encodedStart2D.high.y;
  1241. startHiLo2D[vec4Index + 2] = encodedStart2D.low.x;
  1242. startHiLo2D[vec4Index + 3] = encodedStart2D.low.y;
  1243. startEndNormals2D[vec4Index] = -startGeometryNormal2D.y;
  1244. startEndNormals2D[vec4Index + 1] = startGeometryNormal2D.x;
  1245. startEndNormals2D[vec4Index + 2] = endGeometryNormal2D.y;
  1246. startEndNormals2D[vec4Index + 3] = -endGeometryNormal2D.x;
  1247. offsetAndRight2D[vec4Index] = forwardOffset2D.x;
  1248. offsetAndRight2D[vec4Index + 1] = forwardOffset2D.y;
  1249. offsetAndRight2D[vec4Index + 2] = right2D.x;
  1250. offsetAndRight2D[vec4Index + 3] = right2D.y;
  1251. texcoordNormalization2D[vec2Index] = texcoordNormalization2DX * rightPlaneSide;
  1252. texcoordNormalization = texcoordNormalization2DY * topBottomSide;
  1253. if (texcoordNormalization === 0.0 && topBottomSide < 0.0) {
  1254. texcoordNormalization = Number.POSITIVE_INFINITY;
  1255. }
  1256. texcoordNormalization2D[vec2Index + 1] = texcoordNormalization;
  1257. }
  1258. }
  1259. // Adjust height of volume in 3D
  1260. var adjustHeightStartBottom = adjustHeightStartBottomScratch;
  1261. var adjustHeightEndBottom = adjustHeightEndBottomScratch;
  1262. var adjustHeightStartTop = adjustHeightStartTopScratch;
  1263. var adjustHeightEndTop = adjustHeightEndTopScratch;
  1264. var getHeightsRectangle = Cartesian2.Rectangle.fromCartographicArray(getHeightCartographics, getHeightRectangleScratch);
  1265. var minMaxHeights = ApproximateTerrainHeights.getMinimumMaximumHeights(getHeightsRectangle, ellipsoid);
  1266. var minHeight = minMaxHeights.minimumTerrainHeight;
  1267. var maxHeight = minMaxHeights.maximumTerrainHeight;
  1268. sumHeights += minHeight;
  1269. sumHeights += maxHeight;
  1270. adjustHeights(startBottom, startTop, minHeight, maxHeight, adjustHeightStartBottom, adjustHeightStartTop);
  1271. adjustHeights(endBottom, endTop, minHeight, maxHeight, adjustHeightEndBottom, adjustHeightEndTop);
  1272. // Nudge the positions away from the "polyline" a little bit to prevent errors in GeometryPipeline
  1273. var normalNudge = Cartographic.Cartesian3.multiplyByScalar(rightNormal, _Math.CesiumMath.EPSILON5, normalNudgeScratch);
  1274. Cartographic.Cartesian3.add(adjustHeightStartBottom, normalNudge, adjustHeightStartBottom);
  1275. Cartographic.Cartesian3.add(adjustHeightEndBottom, normalNudge, adjustHeightEndBottom);
  1276. Cartographic.Cartesian3.add(adjustHeightStartTop, normalNudge, adjustHeightStartTop);
  1277. Cartographic.Cartesian3.add(adjustHeightEndTop, normalNudge, adjustHeightEndTop);
  1278. // If the segment is very close to the XZ plane, nudge the vertices slightly to avoid touching it.
  1279. nudgeXZ(adjustHeightStartBottom, adjustHeightEndBottom);
  1280. nudgeXZ(adjustHeightStartTop, adjustHeightEndTop);
  1281. Cartographic.Cartesian3.pack(adjustHeightStartBottom, positionsArray, vec3sWriteIndex);
  1282. Cartographic.Cartesian3.pack(adjustHeightEndBottom, positionsArray, vec3sWriteIndex + 3);
  1283. Cartographic.Cartesian3.pack(adjustHeightEndTop, positionsArray, vec3sWriteIndex + 6);
  1284. Cartographic.Cartesian3.pack(adjustHeightStartTop, positionsArray, vec3sWriteIndex + 9);
  1285. normalNudge = Cartographic.Cartesian3.multiplyByScalar(rightNormal, -2.0 * _Math.CesiumMath.EPSILON5, normalNudgeScratch);
  1286. Cartographic.Cartesian3.add(adjustHeightStartBottom, normalNudge, adjustHeightStartBottom);
  1287. Cartographic.Cartesian3.add(adjustHeightEndBottom, normalNudge, adjustHeightEndBottom);
  1288. Cartographic.Cartesian3.add(adjustHeightStartTop, normalNudge, adjustHeightStartTop);
  1289. Cartographic.Cartesian3.add(adjustHeightEndTop, normalNudge, adjustHeightEndTop);
  1290. nudgeXZ(adjustHeightStartBottom, adjustHeightEndBottom);
  1291. nudgeXZ(adjustHeightStartTop, adjustHeightEndTop);
  1292. Cartographic.Cartesian3.pack(adjustHeightStartBottom, positionsArray, vec3sWriteIndex + 12);
  1293. Cartographic.Cartesian3.pack(adjustHeightEndBottom, positionsArray, vec3sWriteIndex + 15);
  1294. Cartographic.Cartesian3.pack(adjustHeightEndTop, positionsArray, vec3sWriteIndex + 18);
  1295. Cartographic.Cartesian3.pack(adjustHeightStartTop, positionsArray, vec3sWriteIndex + 21);
  1296. cartographicsIndex += 2;
  1297. index += 3;
  1298. vec2sWriteIndex += 16;
  1299. vec3sWriteIndex += 24;
  1300. vec4sWriteIndex += 32;
  1301. lengthSoFar3D += segmentLength3D;
  1302. lengthSoFar2D += segmentLength2D;
  1303. }
  1304. index = 0;
  1305. var indexOffset = 0;
  1306. for (i = 0; i < segmentCount; i++) {
  1307. for (j = 0; j < REFERENCE_INDICES_LENGTH; j++) {
  1308. indices[index + j] = REFERENCE_INDICES[j] + indexOffset;
  1309. }
  1310. indexOffset += 8;
  1311. index += REFERENCE_INDICES_LENGTH;
  1312. }
  1313. var boundingSpheres = scratchBoundingSpheres;
  1314. BoundingSphere.BoundingSphere.fromVertices(bottomPositionsArray, Cartographic.Cartesian3.ZERO, 3, boundingSpheres[0]);
  1315. BoundingSphere.BoundingSphere.fromVertices(topPositionsArray, Cartographic.Cartesian3.ZERO, 3, boundingSpheres[1]);
  1316. var boundingSphere = BoundingSphere.BoundingSphere.fromBoundingSpheres(boundingSpheres);
  1317. // Adjust bounding sphere height and radius to cover more of the volume
  1318. boundingSphere.radius += sumHeights / (segmentCount * 2.0);
  1319. var attributes = {
  1320. position : new GeometryAttribute.GeometryAttribute({
  1321. componentDatatype : ComponentDatatype.ComponentDatatype.DOUBLE,
  1322. componentsPerAttribute : 3,
  1323. normalize : false,
  1324. values : positionsArray
  1325. }),
  1326. startHiAndForwardOffsetX : getVec4GeometryAttribute(startHiAndForwardOffsetX),
  1327. startLoAndForwardOffsetY : getVec4GeometryAttribute(startLoAndForwardOffsetY),
  1328. startNormalAndForwardOffsetZ : getVec4GeometryAttribute(startNormalAndForwardOffsetZ),
  1329. endNormalAndTextureCoordinateNormalizationX : getVec4GeometryAttribute(endNormalAndTextureCoordinateNormalizationX),
  1330. rightNormalAndTextureCoordinateNormalizationY : getVec4GeometryAttribute(rightNormalAndTextureCoordinateNormalizationY)
  1331. };
  1332. if (compute2dAttributes) {
  1333. attributes.startHiLo2D = getVec4GeometryAttribute(startHiLo2D);
  1334. attributes.offsetAndRight2D = getVec4GeometryAttribute(offsetAndRight2D);
  1335. attributes.startEndNormals2D = getVec4GeometryAttribute(startEndNormals2D);
  1336. attributes.texcoordNormalization2D = new GeometryAttribute.GeometryAttribute({
  1337. componentDatatype : ComponentDatatype.ComponentDatatype.FLOAT,
  1338. componentsPerAttribute : 2,
  1339. normalize : false,
  1340. values : texcoordNormalization2D
  1341. });
  1342. }
  1343. return new GeometryAttribute.Geometry({
  1344. attributes : attributes,
  1345. indices : indices,
  1346. boundingSphere : boundingSphere
  1347. });
  1348. }
  1349. function getVec4GeometryAttribute(typedArray) {
  1350. return new GeometryAttribute.GeometryAttribute({
  1351. componentDatatype : ComponentDatatype.ComponentDatatype.FLOAT,
  1352. componentsPerAttribute : 4,
  1353. normalize : false,
  1354. values : typedArray
  1355. });
  1356. }
  1357. /**
  1358. * Approximates an ellipsoid-tangent vector in 2D by projecting the end point into 2D.
  1359. * Exposed for testing.
  1360. *
  1361. * @param {MapProjection} projection Map Projection for projecting coordinates to 2D.
  1362. * @param {Cartographic} cartographic The cartographic origin point of the normal.
  1363. * Used to check if the normal crosses the IDL during projection.
  1364. * @param {Cartesian3} normal The normal in 3D.
  1365. * @param {Cartesian3} projectedPosition The projected origin point of the normal in 2D.
  1366. * @param {Cartesian3} result Result parameter on which to store the projected normal.
  1367. * @private
  1368. */
  1369. GroundPolylineGeometry._projectNormal = projectNormal;
  1370. function createGroundPolylineGeometry(groundPolylineGeometry, offset) {
  1371. return ApproximateTerrainHeights.initialize()
  1372. .then(function() {
  1373. if (when.defined(offset)) {
  1374. groundPolylineGeometry = GroundPolylineGeometry.unpack(groundPolylineGeometry, offset);
  1375. }
  1376. return GroundPolylineGeometry.createGeometry(groundPolylineGeometry);
  1377. });
  1378. }
  1379. return createGroundPolylineGeometry;
  1380. });