createGroundPolylineGeometry.js 81 KB

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