12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135 |
- /* This file is automatically rebuilt by the Cesium build process. */
- define(['./Transforms-a076dbe6', './Matrix2-fc7e9822', './RuntimeError-c581ca93', './defaultValue-94c3e563', './ComponentDatatype-4a60b8d6', './ArcType-0cf52f8c', './arrayRemoveDuplicates-06991c15', './EllipsoidGeodesic-dc94f381', './EllipsoidRhumbLine-daebc75b', './EncodedCartesian3-d3e254ea', './GeometryAttribute-2ecf73f6', './IntersectionTests-5deed78b', './Plane-e20fba8c', './WebMercatorProjection-843df830', './_commonjsHelpers-3aae1032-f55dc0c4', './combine-761d9c3f', './WebGLConstants-7dccdc96'], (function (Transforms, Matrix2, RuntimeError, defaultValue, ComponentDatatype, ArcType, arrayRemoveDuplicates, EllipsoidGeodesic, EllipsoidRhumbLine, EncodedCartesian3, GeometryAttribute, IntersectionTests, Plane, WebMercatorProjection, _commonjsHelpers3aae1032, combine, WebGLConstants) { 'use strict';
- /**
- * A tiling scheme for geometry referenced to a simple {@link GeographicProjection} where
- * longitude and latitude are directly mapped to X and Y. This projection is commonly
- * known as geographic, equirectangular, equidistant cylindrical, or plate carrée.
- *
- * @alias GeographicTilingScheme
- * @constructor
- *
- * @param {Object} [options] Object with the following properties:
- * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid whose surface is being tiled. Defaults to
- * the WGS84 ellipsoid.
- * @param {Rectangle} [options.rectangle=Rectangle.MAX_VALUE] The rectangle, in radians, covered by the tiling scheme.
- * @param {Number} [options.numberOfLevelZeroTilesX=2] The number of tiles in the X direction at level zero of
- * the tile tree.
- * @param {Number} [options.numberOfLevelZeroTilesY=1] The number of tiles in the Y direction at level zero of
- * the tile tree.
- */
- function GeographicTilingScheme(options) {
- options = defaultValue.defaultValue(options, defaultValue.defaultValue.EMPTY_OBJECT);
- this._ellipsoid = defaultValue.defaultValue(options.ellipsoid, Matrix2.Ellipsoid.WGS84);
- this._rectangle = defaultValue.defaultValue(options.rectangle, Matrix2.Rectangle.MAX_VALUE);
- this._projection = new Transforms.GeographicProjection(this._ellipsoid);
- this._numberOfLevelZeroTilesX = defaultValue.defaultValue(
- options.numberOfLevelZeroTilesX,
- 2
- );
- this._numberOfLevelZeroTilesY = defaultValue.defaultValue(
- options.numberOfLevelZeroTilesY,
- 1
- );
- }
- Object.defineProperties(GeographicTilingScheme.prototype, {
- /**
- * Gets the ellipsoid that is tiled by this tiling scheme.
- * @memberof GeographicTilingScheme.prototype
- * @type {Ellipsoid}
- */
- ellipsoid: {
- get: function () {
- return this._ellipsoid;
- },
- },
- /**
- * Gets the rectangle, in radians, covered by this tiling scheme.
- * @memberof GeographicTilingScheme.prototype
- * @type {Rectangle}
- */
- rectangle: {
- get: function () {
- return this._rectangle;
- },
- },
- /**
- * Gets the map projection used by this tiling scheme.
- * @memberof GeographicTilingScheme.prototype
- * @type {MapProjection}
- */
- projection: {
- get: function () {
- return this._projection;
- },
- },
- });
- /**
- * Gets the total number of tiles in the X direction at a specified level-of-detail.
- *
- * @param {Number} level The level-of-detail.
- * @returns {Number} The number of tiles in the X direction at the given level.
- */
- GeographicTilingScheme.prototype.getNumberOfXTilesAtLevel = function (level) {
- return this._numberOfLevelZeroTilesX << level;
- };
- /**
- * Gets the total number of tiles in the Y direction at a specified level-of-detail.
- *
- * @param {Number} level The level-of-detail.
- * @returns {Number} The number of tiles in the Y direction at the given level.
- */
- GeographicTilingScheme.prototype.getNumberOfYTilesAtLevel = function (level) {
- return this._numberOfLevelZeroTilesY << level;
- };
- /**
- * Transforms a rectangle specified in geodetic radians to the native coordinate system
- * of this tiling scheme.
- *
- * @param {Rectangle} rectangle The rectangle to transform.
- * @param {Rectangle} [result] The instance to which to copy the result, or undefined if a new instance
- * should be created.
- * @returns {Rectangle} The specified 'result', or a new object containing the native rectangle if 'result'
- * is undefined.
- */
- GeographicTilingScheme.prototype.rectangleToNativeRectangle = function (
- rectangle,
- result
- ) {
- //>>includeStart('debug', pragmas.debug);
- RuntimeError.Check.defined("rectangle", rectangle);
- //>>includeEnd('debug');
- const west = ComponentDatatype.CesiumMath.toDegrees(rectangle.west);
- const south = ComponentDatatype.CesiumMath.toDegrees(rectangle.south);
- const east = ComponentDatatype.CesiumMath.toDegrees(rectangle.east);
- const north = ComponentDatatype.CesiumMath.toDegrees(rectangle.north);
- if (!defaultValue.defined(result)) {
- return new Matrix2.Rectangle(west, south, east, north);
- }
- result.west = west;
- result.south = south;
- result.east = east;
- result.north = north;
- return result;
- };
- /**
- * Converts tile x, y coordinates and level to a rectangle expressed in the native coordinates
- * of the tiling scheme.
- *
- * @param {Number} x The integer x coordinate of the tile.
- * @param {Number} y The integer y coordinate of the tile.
- * @param {Number} level The tile level-of-detail. Zero is the least detailed.
- * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance
- * should be created.
- * @returns {Rectangle} The specified 'result', or a new object containing the rectangle
- * if 'result' is undefined.
- */
- GeographicTilingScheme.prototype.tileXYToNativeRectangle = function (
- x,
- y,
- level,
- result
- ) {
- const rectangleRadians = this.tileXYToRectangle(x, y, level, result);
- rectangleRadians.west = ComponentDatatype.CesiumMath.toDegrees(rectangleRadians.west);
- rectangleRadians.south = ComponentDatatype.CesiumMath.toDegrees(rectangleRadians.south);
- rectangleRadians.east = ComponentDatatype.CesiumMath.toDegrees(rectangleRadians.east);
- rectangleRadians.north = ComponentDatatype.CesiumMath.toDegrees(rectangleRadians.north);
- return rectangleRadians;
- };
- /**
- * Converts tile x, y coordinates and level to a cartographic rectangle in radians.
- *
- * @param {Number} x The integer x coordinate of the tile.
- * @param {Number} y The integer y coordinate of the tile.
- * @param {Number} level The tile level-of-detail. Zero is the least detailed.
- * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance
- * should be created.
- * @returns {Rectangle} The specified 'result', or a new object containing the rectangle
- * if 'result' is undefined.
- */
- GeographicTilingScheme.prototype.tileXYToRectangle = function (
- x,
- y,
- level,
- result
- ) {
- const rectangle = this._rectangle;
- const xTiles = this.getNumberOfXTilesAtLevel(level);
- const yTiles = this.getNumberOfYTilesAtLevel(level);
- const xTileWidth = rectangle.width / xTiles;
- const west = x * xTileWidth + rectangle.west;
- const east = (x + 1) * xTileWidth + rectangle.west;
- const yTileHeight = rectangle.height / yTiles;
- const north = rectangle.north - y * yTileHeight;
- const south = rectangle.north - (y + 1) * yTileHeight;
- if (!defaultValue.defined(result)) {
- result = new Matrix2.Rectangle(west, south, east, north);
- }
- result.west = west;
- result.south = south;
- result.east = east;
- result.north = north;
- return result;
- };
- /**
- * Calculates the tile x, y coordinates of the tile containing
- * a given cartographic position.
- *
- * @param {Cartographic} position The position.
- * @param {Number} level The tile level-of-detail. Zero is the least detailed.
- * @param {Cartesian2} [result] The instance to which to copy the result, or undefined if a new instance
- * should be created.
- * @returns {Cartesian2} The specified 'result', or a new object containing the tile x, y coordinates
- * if 'result' is undefined.
- */
- GeographicTilingScheme.prototype.positionToTileXY = function (
- position,
- level,
- result
- ) {
- const rectangle = this._rectangle;
- if (!Matrix2.Rectangle.contains(rectangle, position)) {
- // outside the bounds of the tiling scheme
- return undefined;
- }
- const xTiles = this.getNumberOfXTilesAtLevel(level);
- const yTiles = this.getNumberOfYTilesAtLevel(level);
- const xTileWidth = rectangle.width / xTiles;
- const yTileHeight = rectangle.height / yTiles;
- let longitude = position.longitude;
- if (rectangle.east < rectangle.west) {
- longitude += ComponentDatatype.CesiumMath.TWO_PI;
- }
- let xTileCoordinate = ((longitude - rectangle.west) / xTileWidth) | 0;
- if (xTileCoordinate >= xTiles) {
- xTileCoordinate = xTiles - 1;
- }
- let yTileCoordinate =
- ((rectangle.north - position.latitude) / yTileHeight) | 0;
- if (yTileCoordinate >= yTiles) {
- yTileCoordinate = yTiles - 1;
- }
- if (!defaultValue.defined(result)) {
- return new Matrix2.Cartesian2(xTileCoordinate, yTileCoordinate);
- }
- result.x = xTileCoordinate;
- result.y = yTileCoordinate;
- return result;
- };
- const scratchDiagonalCartesianNE = new Matrix2.Cartesian3();
- const scratchDiagonalCartesianSW = new Matrix2.Cartesian3();
- const scratchDiagonalCartographic = new Matrix2.Cartographic();
- const scratchCenterCartesian = new Matrix2.Cartesian3();
- const scratchSurfaceCartesian = new Matrix2.Cartesian3();
- const scratchBoundingSphere = new Transforms.BoundingSphere();
- const tilingScheme = new GeographicTilingScheme();
- const scratchCorners = [
- new Matrix2.Cartographic(),
- new Matrix2.Cartographic(),
- new Matrix2.Cartographic(),
- new Matrix2.Cartographic(),
- ];
- const scratchTileXY = new Matrix2.Cartesian2();
- /**
- * A collection of functions for approximating terrain height
- * @private
- */
- const ApproximateTerrainHeights = {};
- /**
- * Initializes the minimum and maximum terrain heights
- * @return {Promise.<void>}
- */
- ApproximateTerrainHeights.initialize = function () {
- let initPromise = ApproximateTerrainHeights._initPromise;
- if (defaultValue.defined(initPromise)) {
- return initPromise;
- }
- initPromise = Transforms.Resource.fetchJson(
- Transforms.buildModuleUrl("Assets/approximateTerrainHeights.json")
- ).then(function (json) {
- ApproximateTerrainHeights._terrainHeights = json;
- });
- ApproximateTerrainHeights._initPromise = initPromise;
- return initPromise;
- };
- /**
- * Computes the minimum and maximum terrain heights for a given rectangle
- * @param {Rectangle} rectangle The bounding rectangle
- * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid
- * @return {{minimumTerrainHeight: Number, maximumTerrainHeight: Number}}
- */
- ApproximateTerrainHeights.getMinimumMaximumHeights = function (
- rectangle,
- ellipsoid
- ) {
- //>>includeStart('debug', pragmas.debug);
- RuntimeError.Check.defined("rectangle", rectangle);
- if (!defaultValue.defined(ApproximateTerrainHeights._terrainHeights)) {
- throw new RuntimeError.DeveloperError(
- "You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function"
- );
- }
- //>>includeEnd('debug');
- ellipsoid = defaultValue.defaultValue(ellipsoid, Matrix2.Ellipsoid.WGS84);
- const xyLevel = getTileXYLevel(rectangle);
- // Get the terrain min/max for that tile
- let minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
- let maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
- if (defaultValue.defined(xyLevel)) {
- const key = `${xyLevel.level}-${xyLevel.x}-${xyLevel.y}`;
- const heights = ApproximateTerrainHeights._terrainHeights[key];
- if (defaultValue.defined(heights)) {
- minTerrainHeight = heights[0];
- maxTerrainHeight = heights[1];
- }
- // Compute min by taking the center of the NE->SW diagonal and finding distance to the surface
- ellipsoid.cartographicToCartesian(
- Matrix2.Rectangle.northeast(rectangle, scratchDiagonalCartographic),
- scratchDiagonalCartesianNE
- );
- ellipsoid.cartographicToCartesian(
- Matrix2.Rectangle.southwest(rectangle, scratchDiagonalCartographic),
- scratchDiagonalCartesianSW
- );
- Matrix2.Cartesian3.midpoint(
- scratchDiagonalCartesianSW,
- scratchDiagonalCartesianNE,
- scratchCenterCartesian
- );
- const surfacePosition = ellipsoid.scaleToGeodeticSurface(
- scratchCenterCartesian,
- scratchSurfaceCartesian
- );
- if (defaultValue.defined(surfacePosition)) {
- const distance = Matrix2.Cartesian3.distance(
- scratchCenterCartesian,
- surfacePosition
- );
- minTerrainHeight = Math.min(minTerrainHeight, -distance);
- } else {
- minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
- }
- }
- minTerrainHeight = Math.max(
- ApproximateTerrainHeights._defaultMinTerrainHeight,
- minTerrainHeight
- );
- return {
- minimumTerrainHeight: minTerrainHeight,
- maximumTerrainHeight: maxTerrainHeight,
- };
- };
- /**
- * Computes the bounding sphere based on the tile heights in the rectangle
- * @param {Rectangle} rectangle The bounding rectangle
- * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid
- * @return {BoundingSphere} The result bounding sphere
- */
- ApproximateTerrainHeights.getBoundingSphere = function (rectangle, ellipsoid) {
- //>>includeStart('debug', pragmas.debug);
- RuntimeError.Check.defined("rectangle", rectangle);
- if (!defaultValue.defined(ApproximateTerrainHeights._terrainHeights)) {
- throw new RuntimeError.DeveloperError(
- "You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function"
- );
- }
- //>>includeEnd('debug');
- ellipsoid = defaultValue.defaultValue(ellipsoid, Matrix2.Ellipsoid.WGS84);
- const xyLevel = getTileXYLevel(rectangle);
- // Get the terrain max for that tile
- let maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
- if (defaultValue.defined(xyLevel)) {
- const key = `${xyLevel.level}-${xyLevel.x}-${xyLevel.y}`;
- const heights = ApproximateTerrainHeights._terrainHeights[key];
- if (defaultValue.defined(heights)) {
- maxTerrainHeight = heights[1];
- }
- }
- const result = Transforms.BoundingSphere.fromRectangle3D(rectangle, ellipsoid, 0.0);
- Transforms.BoundingSphere.fromRectangle3D(
- rectangle,
- ellipsoid,
- maxTerrainHeight,
- scratchBoundingSphere
- );
- return Transforms.BoundingSphere.union(result, scratchBoundingSphere, result);
- };
- function getTileXYLevel(rectangle) {
- Matrix2.Cartographic.fromRadians(
- rectangle.east,
- rectangle.north,
- 0.0,
- scratchCorners[0]
- );
- Matrix2.Cartographic.fromRadians(
- rectangle.west,
- rectangle.north,
- 0.0,
- scratchCorners[1]
- );
- Matrix2.Cartographic.fromRadians(
- rectangle.east,
- rectangle.south,
- 0.0,
- scratchCorners[2]
- );
- Matrix2.Cartographic.fromRadians(
- rectangle.west,
- rectangle.south,
- 0.0,
- scratchCorners[3]
- );
- // Determine which tile the bounding rectangle is in
- let lastLevelX = 0,
- lastLevelY = 0;
- let currentX = 0,
- currentY = 0;
- const maxLevel = ApproximateTerrainHeights._terrainHeightsMaxLevel;
- let i;
- for (i = 0; i <= maxLevel; ++i) {
- let failed = false;
- for (let j = 0; j < 4; ++j) {
- const corner = scratchCorners[j];
- tilingScheme.positionToTileXY(corner, i, scratchTileXY);
- if (j === 0) {
- currentX = scratchTileXY.x;
- currentY = scratchTileXY.y;
- } else if (currentX !== scratchTileXY.x || currentY !== scratchTileXY.y) {
- failed = true;
- break;
- }
- }
- if (failed) {
- break;
- }
- lastLevelX = currentX;
- lastLevelY = currentY;
- }
- if (i === 0) {
- return undefined;
- }
- return {
- x: lastLevelX,
- y: lastLevelY,
- level: i > maxLevel ? maxLevel : i - 1,
- };
- }
- ApproximateTerrainHeights._terrainHeightsMaxLevel = 6;
- ApproximateTerrainHeights._defaultMaxTerrainHeight = 9000.0;
- ApproximateTerrainHeights._defaultMinTerrainHeight = -100000.0;
- ApproximateTerrainHeights._terrainHeights = undefined;
- ApproximateTerrainHeights._initPromise = undefined;
- Object.defineProperties(ApproximateTerrainHeights, {
- /**
- * Determines if the terrain heights are initialized and ready to use. To initialize the terrain heights,
- * call {@link ApproximateTerrainHeights#initialize} and wait for the returned promise to resolve.
- * @type {Boolean}
- * @readonly
- * @memberof ApproximateTerrainHeights
- */
- initialized: {
- get: function () {
- return defaultValue.defined(ApproximateTerrainHeights._terrainHeights);
- },
- },
- });
- const PROJECTIONS = [Transforms.GeographicProjection, WebMercatorProjection.WebMercatorProjection];
- const PROJECTION_COUNT = PROJECTIONS.length;
- const MITER_BREAK_SMALL = Math.cos(ComponentDatatype.CesiumMath.toRadians(30.0));
- const MITER_BREAK_LARGE = Math.cos(ComponentDatatype.CesiumMath.toRadians(150.0));
- // Initial heights for constructing the wall.
- // Keeping WALL_INITIAL_MIN_HEIGHT near the ellipsoid surface helps
- // prevent precision problems with planes in the shader.
- // Putting the start point of a plane at ApproximateTerrainHeights._defaultMinTerrainHeight,
- // which is a highly conservative bound, usually puts the plane origin several thousands
- // of meters away from the actual terrain, causing floating point problems when checking
- // fragments on terrain against the plane.
- // Ellipsoid height is generally much closer.
- // The initial max height is arbitrary.
- // Both heights are corrected using ApproximateTerrainHeights for computing the actual volume geometry.
- const WALL_INITIAL_MIN_HEIGHT = 0.0;
- const WALL_INITIAL_MAX_HEIGHT = 1000.0;
- /**
- * A description of a polyline on terrain or 3D Tiles. Only to be used with {@link GroundPolylinePrimitive}.
- *
- * @alias GroundPolylineGeometry
- * @constructor
- *
- * @param {Object} options Options with the following properties:
- * @param {Cartesian3[]} options.positions An array of {@link Cartesian3} defining the polyline's points. Heights above the ellipsoid will be ignored.
- * @param {Number} [options.width=1.0] The screen space width in pixels.
- * @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.
- * @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.
- * @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}.
- *
- * @exception {DeveloperError} At least two positions are required.
- *
- * @see GroundPolylinePrimitive
- *
- * @example
- * const positions = Cesium.Cartesian3.fromDegreesArray([
- * -112.1340164450331, 36.05494287836128,
- * -112.08821010582645, 36.097804071380715,
- * -112.13296079730024, 36.168769146801104
- * ]);
- *
- * const geometry = new Cesium.GroundPolylineGeometry({
- * positions : positions
- * });
- */
- function GroundPolylineGeometry(options) {
- options = defaultValue.defaultValue(options, defaultValue.defaultValue.EMPTY_OBJECT);
- const positions = options.positions;
- //>>includeStart('debug', pragmas.debug);
- if (!defaultValue.defined(positions) || positions.length < 2) {
- throw new RuntimeError.DeveloperError("At least two positions are required.");
- }
- if (
- defaultValue.defined(options.arcType) &&
- options.arcType !== ArcType.ArcType.GEODESIC &&
- options.arcType !== ArcType.ArcType.RHUMB
- ) {
- throw new RuntimeError.DeveloperError(
- "Valid options for arcType are ArcType.GEODESIC and ArcType.RHUMB."
- );
- }
- //>>includeEnd('debug');
- /**
- * The screen space width in pixels.
- * @type {Number}
- */
- this.width = defaultValue.defaultValue(options.width, 1.0); // Doesn't get packed, not necessary for computing geometry.
- this._positions = positions;
- /**
- * The distance interval used for interpolating options.points. Zero indicates no interpolation.
- * Default of 9999.0 allows centimeter accuracy with 32 bit floating point.
- * @type {Boolean}
- * @default 9999.0
- */
- this.granularity = defaultValue.defaultValue(options.granularity, 9999.0);
- /**
- * Whether during geometry creation a line segment will be added between the last and first line positions to make this Polyline a loop.
- * If the geometry has two positions this parameter will be ignored.
- * @type {Boolean}
- * @default false
- */
- this.loop = defaultValue.defaultValue(options.loop, false);
- /**
- * The type of path the polyline must follow. Valid options are {@link ArcType.GEODESIC} and {@link ArcType.RHUMB}.
- * @type {ArcType}
- * @default ArcType.GEODESIC
- */
- this.arcType = defaultValue.defaultValue(options.arcType, ArcType.ArcType.GEODESIC);
- this._ellipsoid = Matrix2.Ellipsoid.WGS84;
- // MapProjections can't be packed, so store the index to a known MapProjection.
- this._projectionIndex = 0;
- this._workerName = "createGroundPolylineGeometry";
- // Used by GroundPolylinePrimitive to signal worker that scenemode is 3D only.
- this._scene3DOnly = false;
- }
- Object.defineProperties(GroundPolylineGeometry.prototype, {
- /**
- * The number of elements used to pack the object into an array.
- * @memberof GroundPolylineGeometry.prototype
- * @type {Number}
- * @readonly
- * @private
- */
- packedLength: {
- get: function () {
- return (
- 1.0 +
- this._positions.length * 3 +
- 1.0 +
- 1.0 +
- 1.0 +
- Matrix2.Ellipsoid.packedLength +
- 1.0 +
- 1.0
- );
- },
- },
- });
- /**
- * Set the GroundPolylineGeometry's projection and ellipsoid.
- * Used by GroundPolylinePrimitive to signal scene information to the geometry for generating 2D attributes.
- *
- * @param {GroundPolylineGeometry} groundPolylineGeometry GroundPolylinGeometry describing a polyline on terrain or 3D Tiles.
- * @param {Projection} mapProjection A MapProjection used for projecting cartographic coordinates to 2D.
- * @private
- */
- GroundPolylineGeometry.setProjectionAndEllipsoid = function (
- groundPolylineGeometry,
- mapProjection
- ) {
- let projectionIndex = 0;
- for (let i = 0; i < PROJECTION_COUNT; i++) {
- if (mapProjection instanceof PROJECTIONS[i]) {
- projectionIndex = i;
- break;
- }
- }
- groundPolylineGeometry._projectionIndex = projectionIndex;
- groundPolylineGeometry._ellipsoid = mapProjection.ellipsoid;
- };
- const cart3Scratch1 = new Matrix2.Cartesian3();
- const cart3Scratch2 = new Matrix2.Cartesian3();
- const cart3Scratch3 = new Matrix2.Cartesian3();
- function computeRightNormal(start, end, maxHeight, ellipsoid, result) {
- const startBottom = getPosition(ellipsoid, start, 0.0, cart3Scratch1);
- const startTop = getPosition(ellipsoid, start, maxHeight, cart3Scratch2);
- const endBottom = getPosition(ellipsoid, end, 0.0, cart3Scratch3);
- const up = direction(startTop, startBottom, cart3Scratch2);
- const forward = direction(endBottom, startBottom, cart3Scratch3);
- Matrix2.Cartesian3.cross(forward, up, result);
- return Matrix2.Cartesian3.normalize(result, result);
- }
- const interpolatedCartographicScratch = new Matrix2.Cartographic();
- const interpolatedBottomScratch = new Matrix2.Cartesian3();
- const interpolatedTopScratch = new Matrix2.Cartesian3();
- const interpolatedNormalScratch = new Matrix2.Cartesian3();
- function interpolateSegment(
- start,
- end,
- minHeight,
- maxHeight,
- granularity,
- arcType,
- ellipsoid,
- normalsArray,
- bottomPositionsArray,
- topPositionsArray,
- cartographicsArray
- ) {
- if (granularity === 0.0) {
- return;
- }
- let ellipsoidLine;
- if (arcType === ArcType.ArcType.GEODESIC) {
- ellipsoidLine = new EllipsoidGeodesic.EllipsoidGeodesic(start, end, ellipsoid);
- } else if (arcType === ArcType.ArcType.RHUMB) {
- ellipsoidLine = new EllipsoidRhumbLine.EllipsoidRhumbLine(start, end, ellipsoid);
- }
- const surfaceDistance = ellipsoidLine.surfaceDistance;
- if (surfaceDistance < granularity) {
- return;
- }
- // Compute rightwards normal applicable at all interpolated points
- const interpolatedNormal = computeRightNormal(
- start,
- end,
- maxHeight,
- ellipsoid,
- interpolatedNormalScratch
- );
- const segments = Math.ceil(surfaceDistance / granularity);
- const interpointDistance = surfaceDistance / segments;
- let distanceFromStart = interpointDistance;
- const pointsToAdd = segments - 1;
- let packIndex = normalsArray.length;
- for (let i = 0; i < pointsToAdd; i++) {
- const interpolatedCartographic = ellipsoidLine.interpolateUsingSurfaceDistance(
- distanceFromStart,
- interpolatedCartographicScratch
- );
- const interpolatedBottom = getPosition(
- ellipsoid,
- interpolatedCartographic,
- minHeight,
- interpolatedBottomScratch
- );
- const interpolatedTop = getPosition(
- ellipsoid,
- interpolatedCartographic,
- maxHeight,
- interpolatedTopScratch
- );
- Matrix2.Cartesian3.pack(interpolatedNormal, normalsArray, packIndex);
- Matrix2.Cartesian3.pack(interpolatedBottom, bottomPositionsArray, packIndex);
- Matrix2.Cartesian3.pack(interpolatedTop, topPositionsArray, packIndex);
- cartographicsArray.push(interpolatedCartographic.latitude);
- cartographicsArray.push(interpolatedCartographic.longitude);
- packIndex += 3;
- distanceFromStart += interpointDistance;
- }
- }
- const heightlessCartographicScratch = new Matrix2.Cartographic();
- function getPosition(ellipsoid, cartographic, height, result) {
- Matrix2.Cartographic.clone(cartographic, heightlessCartographicScratch);
- heightlessCartographicScratch.height = height;
- return Matrix2.Cartographic.toCartesian(
- heightlessCartographicScratch,
- ellipsoid,
- result
- );
- }
- /**
- * Stores the provided instance into the provided array.
- *
- * @param {PolygonGeometry} value The value to pack.
- * @param {Number[]} array The array to pack into.
- * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements.
- *
- * @returns {Number[]} The array that was packed into
- */
- GroundPolylineGeometry.pack = function (value, array, startingIndex) {
- //>>includeStart('debug', pragmas.debug);
- RuntimeError.Check.typeOf.object("value", value);
- RuntimeError.Check.defined("array", array);
- //>>includeEnd('debug');
- let index = defaultValue.defaultValue(startingIndex, 0);
- const positions = value._positions;
- const positionsLength = positions.length;
- array[index++] = positionsLength;
- for (let i = 0; i < positionsLength; ++i) {
- const cartesian = positions[i];
- Matrix2.Cartesian3.pack(cartesian, array, index);
- index += 3;
- }
- array[index++] = value.granularity;
- array[index++] = value.loop ? 1.0 : 0.0;
- array[index++] = value.arcType;
- Matrix2.Ellipsoid.pack(value._ellipsoid, array, index);
- index += Matrix2.Ellipsoid.packedLength;
- array[index++] = value._projectionIndex;
- array[index++] = value._scene3DOnly ? 1.0 : 0.0;
- return array;
- };
- /**
- * Retrieves an instance from a packed array.
- *
- * @param {Number[]} array The packed array.
- * @param {Number} [startingIndex=0] The starting index of the element to be unpacked.
- * @param {PolygonGeometry} [result] The object into which to store the result.
- */
- GroundPolylineGeometry.unpack = function (array, startingIndex, result) {
- //>>includeStart('debug', pragmas.debug);
- RuntimeError.Check.defined("array", array);
- //>>includeEnd('debug');
- let index = defaultValue.defaultValue(startingIndex, 0);
- const positionsLength = array[index++];
- const positions = new Array(positionsLength);
- for (let i = 0; i < positionsLength; i++) {
- positions[i] = Matrix2.Cartesian3.unpack(array, index);
- index += 3;
- }
- const granularity = array[index++];
- const loop = array[index++] === 1.0;
- const arcType = array[index++];
- const ellipsoid = Matrix2.Ellipsoid.unpack(array, index);
- index += Matrix2.Ellipsoid.packedLength;
- const projectionIndex = array[index++];
- const scene3DOnly = array[index++] === 1.0;
- if (!defaultValue.defined(result)) {
- result = new GroundPolylineGeometry({
- positions: positions,
- });
- }
- result._positions = positions;
- result.granularity = granularity;
- result.loop = loop;
- result.arcType = arcType;
- result._ellipsoid = ellipsoid;
- result._projectionIndex = projectionIndex;
- result._scene3DOnly = scene3DOnly;
- return result;
- };
- function direction(target, origin, result) {
- Matrix2.Cartesian3.subtract(target, origin, result);
- Matrix2.Cartesian3.normalize(result, result);
- return result;
- }
- function tangentDirection(target, origin, up, result) {
- result = direction(target, origin, result);
- // orthogonalize
- result = Matrix2.Cartesian3.cross(result, up, result);
- result = Matrix2.Cartesian3.normalize(result, result);
- result = Matrix2.Cartesian3.cross(up, result, result);
- return result;
- }
- const toPreviousScratch = new Matrix2.Cartesian3();
- const toNextScratch = new Matrix2.Cartesian3();
- const forwardScratch = new Matrix2.Cartesian3();
- const vertexUpScratch = new Matrix2.Cartesian3();
- const cosine90 = 0.0;
- const cosine180 = -1.0;
- function computeVertexMiterNormal(
- previousBottom,
- vertexBottom,
- vertexTop,
- nextBottom,
- result
- ) {
- const up = direction(vertexTop, vertexBottom, vertexUpScratch);
- // Compute vectors pointing towards neighboring points but tangent to this point on the ellipsoid
- const toPrevious = tangentDirection(
- previousBottom,
- vertexBottom,
- up,
- toPreviousScratch
- );
- const toNext = tangentDirection(nextBottom, vertexBottom, up, toNextScratch);
- // Check if tangents are almost opposite - if so, no need to miter.
- if (
- ComponentDatatype.CesiumMath.equalsEpsilon(
- Matrix2.Cartesian3.dot(toPrevious, toNext),
- cosine180,
- ComponentDatatype.CesiumMath.EPSILON5
- )
- ) {
- result = Matrix2.Cartesian3.cross(up, toPrevious, result);
- result = Matrix2.Cartesian3.normalize(result, result);
- return result;
- }
- // Average directions to previous and to next in the plane of Up
- result = Matrix2.Cartesian3.add(toNext, toPrevious, result);
- result = Matrix2.Cartesian3.normalize(result, result);
- // Flip the normal if it isn't pointing roughly bound right (aka if forward is pointing more "backwards")
- const forward = Matrix2.Cartesian3.cross(up, result, forwardScratch);
- if (Matrix2.Cartesian3.dot(toNext, forward) < cosine90) {
- result = Matrix2.Cartesian3.negate(result, result);
- }
- return result;
- }
- const XZ_PLANE = Plane.Plane.fromPointNormal(Matrix2.Cartesian3.ZERO, Matrix2.Cartesian3.UNIT_Y);
- const previousBottomScratch = new Matrix2.Cartesian3();
- const vertexBottomScratch = new Matrix2.Cartesian3();
- const vertexTopScratch = new Matrix2.Cartesian3();
- const nextBottomScratch = new Matrix2.Cartesian3();
- const vertexNormalScratch = new Matrix2.Cartesian3();
- const intersectionScratch = new Matrix2.Cartesian3();
- const cartographicScratch0 = new Matrix2.Cartographic();
- const cartographicScratch1 = new Matrix2.Cartographic();
- const cartographicIntersectionScratch = new Matrix2.Cartographic();
- /**
- * Computes shadow volumes for the ground polyline, consisting of its vertices, indices, and a bounding sphere.
- * Vertices are "fat," packing all the data needed in each volume to describe a line on terrain or 3D Tiles.
- * Should not be called independent of {@link GroundPolylinePrimitive}.
- *
- * @param {GroundPolylineGeometry} groundPolylineGeometry
- * @private
- */
- GroundPolylineGeometry.createGeometry = function (groundPolylineGeometry) {
- const compute2dAttributes = !groundPolylineGeometry._scene3DOnly;
- let loop = groundPolylineGeometry.loop;
- const ellipsoid = groundPolylineGeometry._ellipsoid;
- const granularity = groundPolylineGeometry.granularity;
- const arcType = groundPolylineGeometry.arcType;
- const projection = new PROJECTIONS[groundPolylineGeometry._projectionIndex](
- ellipsoid
- );
- const minHeight = WALL_INITIAL_MIN_HEIGHT;
- const maxHeight = WALL_INITIAL_MAX_HEIGHT;
- let index;
- let i;
- const positions = groundPolylineGeometry._positions;
- const positionsLength = positions.length;
- if (positionsLength === 2) {
- loop = false;
- }
- // Split positions across the IDL and the Prime Meridian as well.
- // Split across prime meridian because very large geometries crossing the Prime Meridian but not the IDL
- // may get split by the plane of IDL + Prime Meridian.
- let p0;
- let p1;
- let c0;
- let c1;
- const rhumbLine = new EllipsoidRhumbLine.EllipsoidRhumbLine(undefined, undefined, ellipsoid);
- let intersection;
- let intersectionCartographic;
- let intersectionLongitude;
- const splitPositions = [positions[0]];
- for (i = 0; i < positionsLength - 1; i++) {
- p0 = positions[i];
- p1 = positions[i + 1];
- intersection = IntersectionTests.IntersectionTests.lineSegmentPlane(
- p0,
- p1,
- XZ_PLANE,
- intersectionScratch
- );
- if (
- defaultValue.defined(intersection) &&
- !Matrix2.Cartesian3.equalsEpsilon(intersection, p0, ComponentDatatype.CesiumMath.EPSILON7) &&
- !Matrix2.Cartesian3.equalsEpsilon(intersection, p1, ComponentDatatype.CesiumMath.EPSILON7)
- ) {
- if (groundPolylineGeometry.arcType === ArcType.ArcType.GEODESIC) {
- splitPositions.push(Matrix2.Cartesian3.clone(intersection));
- } else if (groundPolylineGeometry.arcType === ArcType.ArcType.RHUMB) {
- intersectionLongitude = ellipsoid.cartesianToCartographic(
- intersection,
- cartographicScratch0
- ).longitude;
- c0 = ellipsoid.cartesianToCartographic(p0, cartographicScratch0);
- c1 = ellipsoid.cartesianToCartographic(p1, cartographicScratch1);
- rhumbLine.setEndPoints(c0, c1);
- intersectionCartographic = rhumbLine.findIntersectionWithLongitude(
- intersectionLongitude,
- cartographicIntersectionScratch
- );
- intersection = ellipsoid.cartographicToCartesian(
- intersectionCartographic,
- intersectionScratch
- );
- if (
- defaultValue.defined(intersection) &&
- !Matrix2.Cartesian3.equalsEpsilon(intersection, p0, ComponentDatatype.CesiumMath.EPSILON7) &&
- !Matrix2.Cartesian3.equalsEpsilon(intersection, p1, ComponentDatatype.CesiumMath.EPSILON7)
- ) {
- splitPositions.push(Matrix2.Cartesian3.clone(intersection));
- }
- }
- }
- splitPositions.push(p1);
- }
- if (loop) {
- p0 = positions[positionsLength - 1];
- p1 = positions[0];
- intersection = IntersectionTests.IntersectionTests.lineSegmentPlane(
- p0,
- p1,
- XZ_PLANE,
- intersectionScratch
- );
- if (
- defaultValue.defined(intersection) &&
- !Matrix2.Cartesian3.equalsEpsilon(intersection, p0, ComponentDatatype.CesiumMath.EPSILON7) &&
- !Matrix2.Cartesian3.equalsEpsilon(intersection, p1, ComponentDatatype.CesiumMath.EPSILON7)
- ) {
- if (groundPolylineGeometry.arcType === ArcType.ArcType.GEODESIC) {
- splitPositions.push(Matrix2.Cartesian3.clone(intersection));
- } else if (groundPolylineGeometry.arcType === ArcType.ArcType.RHUMB) {
- intersectionLongitude = ellipsoid.cartesianToCartographic(
- intersection,
- cartographicScratch0
- ).longitude;
- c0 = ellipsoid.cartesianToCartographic(p0, cartographicScratch0);
- c1 = ellipsoid.cartesianToCartographic(p1, cartographicScratch1);
- rhumbLine.setEndPoints(c0, c1);
- intersectionCartographic = rhumbLine.findIntersectionWithLongitude(
- intersectionLongitude,
- cartographicIntersectionScratch
- );
- intersection = ellipsoid.cartographicToCartesian(
- intersectionCartographic,
- intersectionScratch
- );
- if (
- defaultValue.defined(intersection) &&
- !Matrix2.Cartesian3.equalsEpsilon(intersection, p0, ComponentDatatype.CesiumMath.EPSILON7) &&
- !Matrix2.Cartesian3.equalsEpsilon(intersection, p1, ComponentDatatype.CesiumMath.EPSILON7)
- ) {
- splitPositions.push(Matrix2.Cartesian3.clone(intersection));
- }
- }
- }
- }
- let cartographicsLength = splitPositions.length;
- let cartographics = new Array(cartographicsLength);
- for (i = 0; i < cartographicsLength; i++) {
- const cartographic = Matrix2.Cartographic.fromCartesian(
- splitPositions[i],
- ellipsoid
- );
- cartographic.height = 0.0;
- cartographics[i] = cartographic;
- }
- cartographics = arrayRemoveDuplicates.arrayRemoveDuplicates(
- cartographics,
- Matrix2.Cartographic.equalsEpsilon
- );
- cartographicsLength = cartographics.length;
- if (cartographicsLength < 2) {
- return undefined;
- }
- /**** Build heap-side arrays for positions, interpolated cartographics, and normals from which to compute vertices ****/
- // We build a "wall" and then decompose it into separately connected component "volumes" because we need a lot
- // of information about the wall. Also, this simplifies interpolation.
- // Convention: "next" and "end" are locally forward to each segment of the wall,
- // and we are computing normals pointing towards the local right side of the vertices in each segment.
- const cartographicsArray = [];
- const normalsArray = [];
- const bottomPositionsArray = [];
- const topPositionsArray = [];
- let previousBottom = previousBottomScratch;
- let vertexBottom = vertexBottomScratch;
- let vertexTop = vertexTopScratch;
- let nextBottom = nextBottomScratch;
- let vertexNormal = vertexNormalScratch;
- // First point - either loop or attach a "perpendicular" normal
- const startCartographic = cartographics[0];
- const nextCartographic = cartographics[1];
- const prestartCartographic = cartographics[cartographicsLength - 1];
- previousBottom = getPosition(
- ellipsoid,
- prestartCartographic,
- minHeight,
- previousBottom
- );
- nextBottom = getPosition(ellipsoid, nextCartographic, minHeight, nextBottom);
- vertexBottom = getPosition(
- ellipsoid,
- startCartographic,
- minHeight,
- vertexBottom
- );
- vertexTop = getPosition(ellipsoid, startCartographic, maxHeight, vertexTop);
- if (loop) {
- vertexNormal = computeVertexMiterNormal(
- previousBottom,
- vertexBottom,
- vertexTop,
- nextBottom,
- vertexNormal
- );
- } else {
- vertexNormal = computeRightNormal(
- startCartographic,
- nextCartographic,
- maxHeight,
- ellipsoid,
- vertexNormal
- );
- }
- Matrix2.Cartesian3.pack(vertexNormal, normalsArray, 0);
- Matrix2.Cartesian3.pack(vertexBottom, bottomPositionsArray, 0);
- Matrix2.Cartesian3.pack(vertexTop, topPositionsArray, 0);
- cartographicsArray.push(startCartographic.latitude);
- cartographicsArray.push(startCartographic.longitude);
- interpolateSegment(
- startCartographic,
- nextCartographic,
- minHeight,
- maxHeight,
- granularity,
- arcType,
- ellipsoid,
- normalsArray,
- bottomPositionsArray,
- topPositionsArray,
- cartographicsArray
- );
- // All inbetween points
- for (i = 1; i < cartographicsLength - 1; ++i) {
- previousBottom = Matrix2.Cartesian3.clone(vertexBottom, previousBottom);
- vertexBottom = Matrix2.Cartesian3.clone(nextBottom, vertexBottom);
- const vertexCartographic = cartographics[i];
- getPosition(ellipsoid, vertexCartographic, maxHeight, vertexTop);
- getPosition(ellipsoid, cartographics[i + 1], minHeight, nextBottom);
- computeVertexMiterNormal(
- previousBottom,
- vertexBottom,
- vertexTop,
- nextBottom,
- vertexNormal
- );
- index = normalsArray.length;
- Matrix2.Cartesian3.pack(vertexNormal, normalsArray, index);
- Matrix2.Cartesian3.pack(vertexBottom, bottomPositionsArray, index);
- Matrix2.Cartesian3.pack(vertexTop, topPositionsArray, index);
- cartographicsArray.push(vertexCartographic.latitude);
- cartographicsArray.push(vertexCartographic.longitude);
- interpolateSegment(
- cartographics[i],
- cartographics[i + 1],
- minHeight,
- maxHeight,
- granularity,
- arcType,
- ellipsoid,
- normalsArray,
- bottomPositionsArray,
- topPositionsArray,
- cartographicsArray
- );
- }
- // Last point - either loop or attach a normal "perpendicular" to the wall.
- const endCartographic = cartographics[cartographicsLength - 1];
- const preEndCartographic = cartographics[cartographicsLength - 2];
- vertexBottom = getPosition(
- ellipsoid,
- endCartographic,
- minHeight,
- vertexBottom
- );
- vertexTop = getPosition(ellipsoid, endCartographic, maxHeight, vertexTop);
- if (loop) {
- const postEndCartographic = cartographics[0];
- previousBottom = getPosition(
- ellipsoid,
- preEndCartographic,
- minHeight,
- previousBottom
- );
- nextBottom = getPosition(
- ellipsoid,
- postEndCartographic,
- minHeight,
- nextBottom
- );
- vertexNormal = computeVertexMiterNormal(
- previousBottom,
- vertexBottom,
- vertexTop,
- nextBottom,
- vertexNormal
- );
- } else {
- vertexNormal = computeRightNormal(
- preEndCartographic,
- endCartographic,
- maxHeight,
- ellipsoid,
- vertexNormal
- );
- }
- index = normalsArray.length;
- Matrix2.Cartesian3.pack(vertexNormal, normalsArray, index);
- Matrix2.Cartesian3.pack(vertexBottom, bottomPositionsArray, index);
- Matrix2.Cartesian3.pack(vertexTop, topPositionsArray, index);
- cartographicsArray.push(endCartographic.latitude);
- cartographicsArray.push(endCartographic.longitude);
- if (loop) {
- interpolateSegment(
- endCartographic,
- startCartographic,
- minHeight,
- maxHeight,
- granularity,
- arcType,
- ellipsoid,
- normalsArray,
- bottomPositionsArray,
- topPositionsArray,
- cartographicsArray
- );
- index = normalsArray.length;
- for (i = 0; i < 3; ++i) {
- normalsArray[index + i] = normalsArray[i];
- bottomPositionsArray[index + i] = bottomPositionsArray[i];
- topPositionsArray[index + i] = topPositionsArray[i];
- }
- cartographicsArray.push(startCartographic.latitude);
- cartographicsArray.push(startCartographic.longitude);
- }
- return generateGeometryAttributes(
- loop,
- projection,
- bottomPositionsArray,
- topPositionsArray,
- normalsArray,
- cartographicsArray,
- compute2dAttributes
- );
- };
- // If the end normal angle is too steep compared to the direction of the line segment,
- // "break" the miter by rotating the normal 90 degrees around the "up" direction at the point
- // For ultra precision we would want to project into a plane, but in practice this is sufficient.
- const lineDirectionScratch = new Matrix2.Cartesian3();
- const matrix3Scratch = new Matrix2.Matrix3();
- const quaternionScratch = new Transforms.Quaternion();
- function breakMiter(endGeometryNormal, startBottom, endBottom, endTop) {
- const lineDirection = direction(endBottom, startBottom, lineDirectionScratch);
- const dot = Matrix2.Cartesian3.dot(lineDirection, endGeometryNormal);
- if (dot > MITER_BREAK_SMALL || dot < MITER_BREAK_LARGE) {
- const vertexUp = direction(endTop, endBottom, vertexUpScratch);
- const angle =
- dot < MITER_BREAK_LARGE
- ? ComponentDatatype.CesiumMath.PI_OVER_TWO
- : -ComponentDatatype.CesiumMath.PI_OVER_TWO;
- const quaternion = Transforms.Quaternion.fromAxisAngle(
- vertexUp,
- angle,
- quaternionScratch
- );
- const rotationMatrix = Matrix2.Matrix3.fromQuaternion(quaternion, matrix3Scratch);
- Matrix2.Matrix3.multiplyByVector(
- rotationMatrix,
- endGeometryNormal,
- endGeometryNormal
- );
- return true;
- }
- return false;
- }
- const endPosCartographicScratch = new Matrix2.Cartographic();
- const normalStartpointScratch = new Matrix2.Cartesian3();
- const normalEndpointScratch = new Matrix2.Cartesian3();
- function projectNormal(
- projection,
- cartographic,
- normal,
- projectedPosition,
- result
- ) {
- const position = Matrix2.Cartographic.toCartesian(
- cartographic,
- projection._ellipsoid,
- normalStartpointScratch
- );
- let normalEndpoint = Matrix2.Cartesian3.add(position, normal, normalEndpointScratch);
- let flipNormal = false;
- const ellipsoid = projection._ellipsoid;
- let normalEndpointCartographic = ellipsoid.cartesianToCartographic(
- normalEndpoint,
- endPosCartographicScratch
- );
- // If normal crosses the IDL, go the other way and flip the result.
- // In practice this almost never happens because the cartographic start
- // and end points of each segment are "nudged" to be on the same side
- // of the IDL and slightly away from the IDL.
- if (
- Math.abs(cartographic.longitude - normalEndpointCartographic.longitude) >
- ComponentDatatype.CesiumMath.PI_OVER_TWO
- ) {
- flipNormal = true;
- normalEndpoint = Matrix2.Cartesian3.subtract(
- position,
- normal,
- normalEndpointScratch
- );
- normalEndpointCartographic = ellipsoid.cartesianToCartographic(
- normalEndpoint,
- endPosCartographicScratch
- );
- }
- normalEndpointCartographic.height = 0.0;
- const normalEndpointProjected = projection.project(
- normalEndpointCartographic,
- result
- );
- result = Matrix2.Cartesian3.subtract(
- normalEndpointProjected,
- projectedPosition,
- result
- );
- result.z = 0.0;
- result = Matrix2.Cartesian3.normalize(result, result);
- if (flipNormal) {
- Matrix2.Cartesian3.negate(result, result);
- }
- return result;
- }
- const adjustHeightNormalScratch = new Matrix2.Cartesian3();
- const adjustHeightOffsetScratch = new Matrix2.Cartesian3();
- function adjustHeights(
- bottom,
- top,
- minHeight,
- maxHeight,
- adjustHeightBottom,
- adjustHeightTop
- ) {
- // bottom and top should be at WALL_INITIAL_MIN_HEIGHT and WALL_INITIAL_MAX_HEIGHT, respectively
- const adjustHeightNormal = Matrix2.Cartesian3.subtract(
- top,
- bottom,
- adjustHeightNormalScratch
- );
- Matrix2.Cartesian3.normalize(adjustHeightNormal, adjustHeightNormal);
- const distanceForBottom = minHeight - WALL_INITIAL_MIN_HEIGHT;
- let adjustHeightOffset = Matrix2.Cartesian3.multiplyByScalar(
- adjustHeightNormal,
- distanceForBottom,
- adjustHeightOffsetScratch
- );
- Matrix2.Cartesian3.add(bottom, adjustHeightOffset, adjustHeightBottom);
- const distanceForTop = maxHeight - WALL_INITIAL_MAX_HEIGHT;
- adjustHeightOffset = Matrix2.Cartesian3.multiplyByScalar(
- adjustHeightNormal,
- distanceForTop,
- adjustHeightOffsetScratch
- );
- Matrix2.Cartesian3.add(top, adjustHeightOffset, adjustHeightTop);
- }
- const nudgeDirectionScratch = new Matrix2.Cartesian3();
- function nudgeXZ(start, end) {
- const startToXZdistance = Plane.Plane.getPointDistance(XZ_PLANE, start);
- const endToXZdistance = Plane.Plane.getPointDistance(XZ_PLANE, end);
- let offset = nudgeDirectionScratch;
- // Larger epsilon than what's used in GeometryPipeline, a centimeter in world space
- if (ComponentDatatype.CesiumMath.equalsEpsilon(startToXZdistance, 0.0, ComponentDatatype.CesiumMath.EPSILON2)) {
- offset = direction(end, start, offset);
- Matrix2.Cartesian3.multiplyByScalar(offset, ComponentDatatype.CesiumMath.EPSILON2, offset);
- Matrix2.Cartesian3.add(start, offset, start);
- } else if (
- ComponentDatatype.CesiumMath.equalsEpsilon(endToXZdistance, 0.0, ComponentDatatype.CesiumMath.EPSILON2)
- ) {
- offset = direction(start, end, offset);
- Matrix2.Cartesian3.multiplyByScalar(offset, ComponentDatatype.CesiumMath.EPSILON2, offset);
- Matrix2.Cartesian3.add(end, offset, end);
- }
- }
- // "Nudge" cartographic coordinates so start and end are on the same side of the IDL.
- // Nudge amounts are tiny, basically just an IDL flip.
- // Only used for 2D/CV.
- function nudgeCartographic(start, end) {
- const absStartLon = Math.abs(start.longitude);
- const absEndLon = Math.abs(end.longitude);
- if (
- ComponentDatatype.CesiumMath.equalsEpsilon(absStartLon, ComponentDatatype.CesiumMath.PI, ComponentDatatype.CesiumMath.EPSILON11)
- ) {
- const endSign = ComponentDatatype.CesiumMath.sign(end.longitude);
- start.longitude = endSign * (absStartLon - ComponentDatatype.CesiumMath.EPSILON11);
- return 1;
- } else if (
- ComponentDatatype.CesiumMath.equalsEpsilon(absEndLon, ComponentDatatype.CesiumMath.PI, ComponentDatatype.CesiumMath.EPSILON11)
- ) {
- const startSign = ComponentDatatype.CesiumMath.sign(start.longitude);
- end.longitude = startSign * (absEndLon - ComponentDatatype.CesiumMath.EPSILON11);
- return 2;
- }
- return 0;
- }
- const startCartographicScratch = new Matrix2.Cartographic();
- const endCartographicScratch = new Matrix2.Cartographic();
- const segmentStartTopScratch = new Matrix2.Cartesian3();
- const segmentEndTopScratch = new Matrix2.Cartesian3();
- const segmentStartBottomScratch = new Matrix2.Cartesian3();
- const segmentEndBottomScratch = new Matrix2.Cartesian3();
- const segmentStartNormalScratch = new Matrix2.Cartesian3();
- const segmentEndNormalScratch = new Matrix2.Cartesian3();
- const getHeightCartographics = [
- startCartographicScratch,
- endCartographicScratch,
- ];
- const getHeightRectangleScratch = new Matrix2.Rectangle();
- const adjustHeightStartTopScratch = new Matrix2.Cartesian3();
- const adjustHeightEndTopScratch = new Matrix2.Cartesian3();
- const adjustHeightStartBottomScratch = new Matrix2.Cartesian3();
- const adjustHeightEndBottomScratch = new Matrix2.Cartesian3();
- const segmentStart2DScratch = new Matrix2.Cartesian3();
- const segmentEnd2DScratch = new Matrix2.Cartesian3();
- const segmentStartNormal2DScratch = new Matrix2.Cartesian3();
- const segmentEndNormal2DScratch = new Matrix2.Cartesian3();
- const offsetScratch = new Matrix2.Cartesian3();
- const startUpScratch = new Matrix2.Cartesian3();
- const endUpScratch = new Matrix2.Cartesian3();
- const rightScratch = new Matrix2.Cartesian3();
- const startPlaneNormalScratch = new Matrix2.Cartesian3();
- const endPlaneNormalScratch = new Matrix2.Cartesian3();
- const encodeScratch = new EncodedCartesian3.EncodedCartesian3();
- const encodeScratch2D = new EncodedCartesian3.EncodedCartesian3();
- const forwardOffset2DScratch = new Matrix2.Cartesian3();
- const right2DScratch = new Matrix2.Cartesian3();
- const normalNudgeScratch = new Matrix2.Cartesian3();
- const scratchBoundingSpheres = [new Transforms.BoundingSphere(), new Transforms.BoundingSphere()];
- // Winding order is reversed so each segment's volume is inside-out
- const REFERENCE_INDICES = [
- 0,
- 2,
- 1,
- 0,
- 3,
- 2, // right
- 0,
- 7,
- 3,
- 0,
- 4,
- 7, // start
- 0,
- 5,
- 4,
- 0,
- 1,
- 5, // bottom
- 5,
- 7,
- 4,
- 5,
- 6,
- 7, // left
- 5,
- 2,
- 6,
- 5,
- 1,
- 2, // end
- 3,
- 6,
- 2,
- 3,
- 7,
- 6, // top
- ];
- const REFERENCE_INDICES_LENGTH = REFERENCE_INDICES.length;
- // Decompose the "wall" into a series of shadow volumes.
- // Each shadow volume's vertices encode a description of the line it contains,
- // including mitering planes at the end points, a plane along the line itself,
- // and attributes for computing length-wise texture coordinates.
- function generateGeometryAttributes(
- loop,
- projection,
- bottomPositionsArray,
- topPositionsArray,
- normalsArray,
- cartographicsArray,
- compute2dAttributes
- ) {
- let i;
- let index;
- const ellipsoid = projection._ellipsoid;
- // Each segment will have 8 vertices
- const segmentCount = bottomPositionsArray.length / 3 - 1;
- const vertexCount = segmentCount * 8;
- const arraySizeVec4 = vertexCount * 4;
- const indexCount = segmentCount * 36;
- const indices =
- vertexCount > 65535
- ? new Uint32Array(indexCount)
- : new Uint16Array(indexCount);
- const positionsArray = new Float64Array(vertexCount * 3);
- const startHiAndForwardOffsetX = new Float32Array(arraySizeVec4);
- const startLoAndForwardOffsetY = new Float32Array(arraySizeVec4);
- const startNormalAndForwardOffsetZ = new Float32Array(arraySizeVec4);
- const endNormalAndTextureCoordinateNormalizationX = new Float32Array(
- arraySizeVec4
- );
- const rightNormalAndTextureCoordinateNormalizationY = new Float32Array(
- arraySizeVec4
- );
- let startHiLo2D;
- let offsetAndRight2D;
- let startEndNormals2D;
- let texcoordNormalization2D;
- if (compute2dAttributes) {
- startHiLo2D = new Float32Array(arraySizeVec4);
- offsetAndRight2D = new Float32Array(arraySizeVec4);
- startEndNormals2D = new Float32Array(arraySizeVec4);
- texcoordNormalization2D = new Float32Array(vertexCount * 2);
- }
- /*** Compute total lengths for texture coordinate normalization ***/
- // 2D
- const cartographicsLength = cartographicsArray.length / 2;
- let length2D = 0.0;
- const startCartographic = startCartographicScratch;
- startCartographic.height = 0.0;
- const endCartographic = endCartographicScratch;
- endCartographic.height = 0.0;
- let segmentStartCartesian = segmentStartTopScratch;
- let segmentEndCartesian = segmentEndTopScratch;
- if (compute2dAttributes) {
- index = 0;
- for (i = 1; i < cartographicsLength; i++) {
- // Don't clone anything from previous segment b/c possible IDL touch
- startCartographic.latitude = cartographicsArray[index];
- startCartographic.longitude = cartographicsArray[index + 1];
- endCartographic.latitude = cartographicsArray[index + 2];
- endCartographic.longitude = cartographicsArray[index + 3];
- segmentStartCartesian = projection.project(
- startCartographic,
- segmentStartCartesian
- );
- segmentEndCartesian = projection.project(
- endCartographic,
- segmentEndCartesian
- );
- length2D += Matrix2.Cartesian3.distance(
- segmentStartCartesian,
- segmentEndCartesian
- );
- index += 2;
- }
- }
- // 3D
- const positionsLength = topPositionsArray.length / 3;
- segmentEndCartesian = Matrix2.Cartesian3.unpack(
- topPositionsArray,
- 0,
- segmentEndCartesian
- );
- let length3D = 0.0;
- index = 3;
- for (i = 1; i < positionsLength; i++) {
- segmentStartCartesian = Matrix2.Cartesian3.clone(
- segmentEndCartesian,
- segmentStartCartesian
- );
- segmentEndCartesian = Matrix2.Cartesian3.unpack(
- topPositionsArray,
- index,
- segmentEndCartesian
- );
- length3D += Matrix2.Cartesian3.distance(segmentStartCartesian, segmentEndCartesian);
- index += 3;
- }
- /*** Generate segments ***/
- let j;
- index = 3;
- let cartographicsIndex = 0;
- let vec2sWriteIndex = 0;
- let vec3sWriteIndex = 0;
- let vec4sWriteIndex = 0;
- let miterBroken = false;
- let endBottom = Matrix2.Cartesian3.unpack(
- bottomPositionsArray,
- 0,
- segmentEndBottomScratch
- );
- let endTop = Matrix2.Cartesian3.unpack(topPositionsArray, 0, segmentEndTopScratch);
- let endGeometryNormal = Matrix2.Cartesian3.unpack(
- normalsArray,
- 0,
- segmentEndNormalScratch
- );
- if (loop) {
- const preEndBottom = Matrix2.Cartesian3.unpack(
- bottomPositionsArray,
- bottomPositionsArray.length - 6,
- segmentStartBottomScratch
- );
- if (breakMiter(endGeometryNormal, preEndBottom, endBottom, endTop)) {
- // Miter broken as if for the last point in the loop, needs to be inverted for first point (clone of endBottom)
- endGeometryNormal = Matrix2.Cartesian3.negate(
- endGeometryNormal,
- endGeometryNormal
- );
- }
- }
- let lengthSoFar3D = 0.0;
- let lengthSoFar2D = 0.0;
- // For translating bounding volume
- let sumHeights = 0.0;
- for (i = 0; i < segmentCount; i++) {
- const startBottom = Matrix2.Cartesian3.clone(endBottom, segmentStartBottomScratch);
- const startTop = Matrix2.Cartesian3.clone(endTop, segmentStartTopScratch);
- let startGeometryNormal = Matrix2.Cartesian3.clone(
- endGeometryNormal,
- segmentStartNormalScratch
- );
- if (miterBroken) {
- startGeometryNormal = Matrix2.Cartesian3.negate(
- startGeometryNormal,
- startGeometryNormal
- );
- }
- endBottom = Matrix2.Cartesian3.unpack(
- bottomPositionsArray,
- index,
- segmentEndBottomScratch
- );
- endTop = Matrix2.Cartesian3.unpack(topPositionsArray, index, segmentEndTopScratch);
- endGeometryNormal = Matrix2.Cartesian3.unpack(
- normalsArray,
- index,
- segmentEndNormalScratch
- );
- miterBroken = breakMiter(endGeometryNormal, startBottom, endBottom, endTop);
- // 2D - don't clone anything from previous segment b/c possible IDL touch
- startCartographic.latitude = cartographicsArray[cartographicsIndex];
- startCartographic.longitude = cartographicsArray[cartographicsIndex + 1];
- endCartographic.latitude = cartographicsArray[cartographicsIndex + 2];
- endCartographic.longitude = cartographicsArray[cartographicsIndex + 3];
- let start2D;
- let end2D;
- let startGeometryNormal2D;
- let endGeometryNormal2D;
- if (compute2dAttributes) {
- const nudgeResult = nudgeCartographic(startCartographic, endCartographic);
- start2D = projection.project(startCartographic, segmentStart2DScratch);
- end2D = projection.project(endCartographic, segmentEnd2DScratch);
- const direction2D = direction(end2D, start2D, forwardOffset2DScratch);
- direction2D.y = Math.abs(direction2D.y);
- startGeometryNormal2D = segmentStartNormal2DScratch;
- endGeometryNormal2D = segmentEndNormal2DScratch;
- if (
- nudgeResult === 0 ||
- Matrix2.Cartesian3.dot(direction2D, Matrix2.Cartesian3.UNIT_Y) > MITER_BREAK_SMALL
- ) {
- // No nudge - project the original normal
- // Or, if the line's angle relative to the IDL is very acute,
- // in which case snapping will produce oddly shaped volumes.
- startGeometryNormal2D = projectNormal(
- projection,
- startCartographic,
- startGeometryNormal,
- start2D,
- segmentStartNormal2DScratch
- );
- endGeometryNormal2D = projectNormal(
- projection,
- endCartographic,
- endGeometryNormal,
- end2D,
- segmentEndNormal2DScratch
- );
- } else if (nudgeResult === 1) {
- // Start is close to IDL - snap start normal to align with IDL
- endGeometryNormal2D = projectNormal(
- projection,
- endCartographic,
- endGeometryNormal,
- end2D,
- segmentEndNormal2DScratch
- );
- startGeometryNormal2D.x = 0.0;
- // If start longitude is negative and end longitude is less negative, relative right is unit -Y
- // If start longitude is positive and end longitude is less positive, relative right is unit +Y
- startGeometryNormal2D.y = ComponentDatatype.CesiumMath.sign(
- startCartographic.longitude - Math.abs(endCartographic.longitude)
- );
- startGeometryNormal2D.z = 0.0;
- } else {
- // End is close to IDL - snap end normal to align with IDL
- startGeometryNormal2D = projectNormal(
- projection,
- startCartographic,
- startGeometryNormal,
- start2D,
- segmentStartNormal2DScratch
- );
- endGeometryNormal2D.x = 0.0;
- // If end longitude is negative and start longitude is less negative, relative right is unit Y
- // If end longitude is positive and start longitude is less positive, relative right is unit -Y
- endGeometryNormal2D.y = ComponentDatatype.CesiumMath.sign(
- startCartographic.longitude - endCartographic.longitude
- );
- endGeometryNormal2D.z = 0.0;
- }
- }
- /****************************************
- * Geometry descriptors of a "line on terrain,"
- * as opposed to the "shadow volume used to draw
- * the line on terrain":
- * - position of start + offset to end
- * - start, end, and right-facing planes
- * - encoded texture coordinate offsets
- ****************************************/
- /* 3D */
- const segmentLength3D = Matrix2.Cartesian3.distance(startTop, endTop);
- const encodedStart = EncodedCartesian3.EncodedCartesian3.fromCartesian(
- startBottom,
- encodeScratch
- );
- const forwardOffset = Matrix2.Cartesian3.subtract(
- endBottom,
- startBottom,
- offsetScratch
- );
- const forward = Matrix2.Cartesian3.normalize(forwardOffset, rightScratch);
- let startUp = Matrix2.Cartesian3.subtract(startTop, startBottom, startUpScratch);
- startUp = Matrix2.Cartesian3.normalize(startUp, startUp);
- let rightNormal = Matrix2.Cartesian3.cross(forward, startUp, rightScratch);
- rightNormal = Matrix2.Cartesian3.normalize(rightNormal, rightNormal);
- let startPlaneNormal = Matrix2.Cartesian3.cross(
- startUp,
- startGeometryNormal,
- startPlaneNormalScratch
- );
- startPlaneNormal = Matrix2.Cartesian3.normalize(startPlaneNormal, startPlaneNormal);
- let endUp = Matrix2.Cartesian3.subtract(endTop, endBottom, endUpScratch);
- endUp = Matrix2.Cartesian3.normalize(endUp, endUp);
- let endPlaneNormal = Matrix2.Cartesian3.cross(
- endGeometryNormal,
- endUp,
- endPlaneNormalScratch
- );
- endPlaneNormal = Matrix2.Cartesian3.normalize(endPlaneNormal, endPlaneNormal);
- const texcoordNormalization3DX = segmentLength3D / length3D;
- const texcoordNormalization3DY = lengthSoFar3D / length3D;
- /* 2D */
- let segmentLength2D = 0.0;
- let encodedStart2D;
- let forwardOffset2D;
- let right2D;
- let texcoordNormalization2DX = 0.0;
- let texcoordNormalization2DY = 0.0;
- if (compute2dAttributes) {
- segmentLength2D = Matrix2.Cartesian3.distance(start2D, end2D);
- encodedStart2D = EncodedCartesian3.EncodedCartesian3.fromCartesian(
- start2D,
- encodeScratch2D
- );
- forwardOffset2D = Matrix2.Cartesian3.subtract(
- end2D,
- start2D,
- forwardOffset2DScratch
- );
- // Right direction is just forward direction rotated by -90 degrees around Z
- // Similarly with plane normals
- right2D = Matrix2.Cartesian3.normalize(forwardOffset2D, right2DScratch);
- const swap = right2D.x;
- right2D.x = right2D.y;
- right2D.y = -swap;
- texcoordNormalization2DX = segmentLength2D / length2D;
- texcoordNormalization2DY = lengthSoFar2D / length2D;
- }
- /** Pack **/
- for (j = 0; j < 8; j++) {
- const vec4Index = vec4sWriteIndex + j * 4;
- const vec2Index = vec2sWriteIndex + j * 2;
- const wIndex = vec4Index + 3;
- // Encode sidedness of vertex relative to right plane in texture coordinate normalization X,
- // whether vertex is top or bottom of volume in sign/magnitude of normalization Y.
- const rightPlaneSide = j < 4 ? 1.0 : -1.0;
- const topBottomSide =
- j === 2 || j === 3 || j === 6 || j === 7 ? 1.0 : -1.0;
- // 3D
- Matrix2.Cartesian3.pack(encodedStart.high, startHiAndForwardOffsetX, vec4Index);
- startHiAndForwardOffsetX[wIndex] = forwardOffset.x;
- Matrix2.Cartesian3.pack(encodedStart.low, startLoAndForwardOffsetY, vec4Index);
- startLoAndForwardOffsetY[wIndex] = forwardOffset.y;
- Matrix2.Cartesian3.pack(
- startPlaneNormal,
- startNormalAndForwardOffsetZ,
- vec4Index
- );
- startNormalAndForwardOffsetZ[wIndex] = forwardOffset.z;
- Matrix2.Cartesian3.pack(
- endPlaneNormal,
- endNormalAndTextureCoordinateNormalizationX,
- vec4Index
- );
- endNormalAndTextureCoordinateNormalizationX[wIndex] =
- texcoordNormalization3DX * rightPlaneSide;
- Matrix2.Cartesian3.pack(
- rightNormal,
- rightNormalAndTextureCoordinateNormalizationY,
- vec4Index
- );
- let texcoordNormalization = texcoordNormalization3DY * topBottomSide;
- if (texcoordNormalization === 0.0 && topBottomSide < 0.0) {
- texcoordNormalization = 9.0; // some value greater than 1.0
- }
- rightNormalAndTextureCoordinateNormalizationY[
- wIndex
- ] = texcoordNormalization;
- // 2D
- if (compute2dAttributes) {
- startHiLo2D[vec4Index] = encodedStart2D.high.x;
- startHiLo2D[vec4Index + 1] = encodedStart2D.high.y;
- startHiLo2D[vec4Index + 2] = encodedStart2D.low.x;
- startHiLo2D[vec4Index + 3] = encodedStart2D.low.y;
- startEndNormals2D[vec4Index] = -startGeometryNormal2D.y;
- startEndNormals2D[vec4Index + 1] = startGeometryNormal2D.x;
- startEndNormals2D[vec4Index + 2] = endGeometryNormal2D.y;
- startEndNormals2D[vec4Index + 3] = -endGeometryNormal2D.x;
- offsetAndRight2D[vec4Index] = forwardOffset2D.x;
- offsetAndRight2D[vec4Index + 1] = forwardOffset2D.y;
- offsetAndRight2D[vec4Index + 2] = right2D.x;
- offsetAndRight2D[vec4Index + 3] = right2D.y;
- texcoordNormalization2D[vec2Index] =
- texcoordNormalization2DX * rightPlaneSide;
- texcoordNormalization = texcoordNormalization2DY * topBottomSide;
- if (texcoordNormalization === 0.0 && topBottomSide < 0.0) {
- texcoordNormalization = 9.0; // some value greater than 1.0
- }
- texcoordNormalization2D[vec2Index + 1] = texcoordNormalization;
- }
- }
- // Adjust height of volume in 3D
- const adjustHeightStartBottom = adjustHeightStartBottomScratch;
- const adjustHeightEndBottom = adjustHeightEndBottomScratch;
- const adjustHeightStartTop = adjustHeightStartTopScratch;
- const adjustHeightEndTop = adjustHeightEndTopScratch;
- const getHeightsRectangle = Matrix2.Rectangle.fromCartographicArray(
- getHeightCartographics,
- getHeightRectangleScratch
- );
- const minMaxHeights = ApproximateTerrainHeights.getMinimumMaximumHeights(
- getHeightsRectangle,
- ellipsoid
- );
- const minHeight = minMaxHeights.minimumTerrainHeight;
- const maxHeight = minMaxHeights.maximumTerrainHeight;
- sumHeights += minHeight;
- sumHeights += maxHeight;
- adjustHeights(
- startBottom,
- startTop,
- minHeight,
- maxHeight,
- adjustHeightStartBottom,
- adjustHeightStartTop
- );
- adjustHeights(
- endBottom,
- endTop,
- minHeight,
- maxHeight,
- adjustHeightEndBottom,
- adjustHeightEndTop
- );
- // Nudge the positions away from the "polyline" a little bit to prevent errors in GeometryPipeline
- let normalNudge = Matrix2.Cartesian3.multiplyByScalar(
- rightNormal,
- ComponentDatatype.CesiumMath.EPSILON5,
- normalNudgeScratch
- );
- Matrix2.Cartesian3.add(
- adjustHeightStartBottom,
- normalNudge,
- adjustHeightStartBottom
- );
- Matrix2.Cartesian3.add(adjustHeightEndBottom, normalNudge, adjustHeightEndBottom);
- Matrix2.Cartesian3.add(adjustHeightStartTop, normalNudge, adjustHeightStartTop);
- Matrix2.Cartesian3.add(adjustHeightEndTop, normalNudge, adjustHeightEndTop);
- // If the segment is very close to the XZ plane, nudge the vertices slightly to avoid touching it.
- nudgeXZ(adjustHeightStartBottom, adjustHeightEndBottom);
- nudgeXZ(adjustHeightStartTop, adjustHeightEndTop);
- Matrix2.Cartesian3.pack(adjustHeightStartBottom, positionsArray, vec3sWriteIndex);
- Matrix2.Cartesian3.pack(adjustHeightEndBottom, positionsArray, vec3sWriteIndex + 3);
- Matrix2.Cartesian3.pack(adjustHeightEndTop, positionsArray, vec3sWriteIndex + 6);
- Matrix2.Cartesian3.pack(adjustHeightStartTop, positionsArray, vec3sWriteIndex + 9);
- normalNudge = Matrix2.Cartesian3.multiplyByScalar(
- rightNormal,
- -2.0 * ComponentDatatype.CesiumMath.EPSILON5,
- normalNudgeScratch
- );
- Matrix2.Cartesian3.add(
- adjustHeightStartBottom,
- normalNudge,
- adjustHeightStartBottom
- );
- Matrix2.Cartesian3.add(adjustHeightEndBottom, normalNudge, adjustHeightEndBottom);
- Matrix2.Cartesian3.add(adjustHeightStartTop, normalNudge, adjustHeightStartTop);
- Matrix2.Cartesian3.add(adjustHeightEndTop, normalNudge, adjustHeightEndTop);
- nudgeXZ(adjustHeightStartBottom, adjustHeightEndBottom);
- nudgeXZ(adjustHeightStartTop, adjustHeightEndTop);
- Matrix2.Cartesian3.pack(
- adjustHeightStartBottom,
- positionsArray,
- vec3sWriteIndex + 12
- );
- Matrix2.Cartesian3.pack(
- adjustHeightEndBottom,
- positionsArray,
- vec3sWriteIndex + 15
- );
- Matrix2.Cartesian3.pack(adjustHeightEndTop, positionsArray, vec3sWriteIndex + 18);
- Matrix2.Cartesian3.pack(adjustHeightStartTop, positionsArray, vec3sWriteIndex + 21);
- cartographicsIndex += 2;
- index += 3;
- vec2sWriteIndex += 16;
- vec3sWriteIndex += 24;
- vec4sWriteIndex += 32;
- lengthSoFar3D += segmentLength3D;
- lengthSoFar2D += segmentLength2D;
- }
- index = 0;
- let indexOffset = 0;
- for (i = 0; i < segmentCount; i++) {
- for (j = 0; j < REFERENCE_INDICES_LENGTH; j++) {
- indices[index + j] = REFERENCE_INDICES[j] + indexOffset;
- }
- indexOffset += 8;
- index += REFERENCE_INDICES_LENGTH;
- }
- const boundingSpheres = scratchBoundingSpheres;
- Transforms.BoundingSphere.fromVertices(
- bottomPositionsArray,
- Matrix2.Cartesian3.ZERO,
- 3,
- boundingSpheres[0]
- );
- Transforms.BoundingSphere.fromVertices(
- topPositionsArray,
- Matrix2.Cartesian3.ZERO,
- 3,
- boundingSpheres[1]
- );
- const boundingSphere = Transforms.BoundingSphere.fromBoundingSpheres(boundingSpheres);
- // Adjust bounding sphere height and radius to cover more of the volume
- boundingSphere.radius += sumHeights / (segmentCount * 2.0);
- const attributes = {
- position: new GeometryAttribute.GeometryAttribute({
- componentDatatype: ComponentDatatype.ComponentDatatype.DOUBLE,
- componentsPerAttribute: 3,
- normalize: false,
- values: positionsArray,
- }),
- startHiAndForwardOffsetX: getVec4GeometryAttribute(
- startHiAndForwardOffsetX
- ),
- startLoAndForwardOffsetY: getVec4GeometryAttribute(
- startLoAndForwardOffsetY
- ),
- startNormalAndForwardOffsetZ: getVec4GeometryAttribute(
- startNormalAndForwardOffsetZ
- ),
- endNormalAndTextureCoordinateNormalizationX: getVec4GeometryAttribute(
- endNormalAndTextureCoordinateNormalizationX
- ),
- rightNormalAndTextureCoordinateNormalizationY: getVec4GeometryAttribute(
- rightNormalAndTextureCoordinateNormalizationY
- ),
- };
- if (compute2dAttributes) {
- attributes.startHiLo2D = getVec4GeometryAttribute(startHiLo2D);
- attributes.offsetAndRight2D = getVec4GeometryAttribute(offsetAndRight2D);
- attributes.startEndNormals2D = getVec4GeometryAttribute(startEndNormals2D);
- attributes.texcoordNormalization2D = new GeometryAttribute.GeometryAttribute({
- componentDatatype: ComponentDatatype.ComponentDatatype.FLOAT,
- componentsPerAttribute: 2,
- normalize: false,
- values: texcoordNormalization2D,
- });
- }
- return new GeometryAttribute.Geometry({
- attributes: attributes,
- indices: indices,
- boundingSphere: boundingSphere,
- });
- }
- function getVec4GeometryAttribute(typedArray) {
- return new GeometryAttribute.GeometryAttribute({
- componentDatatype: ComponentDatatype.ComponentDatatype.FLOAT,
- componentsPerAttribute: 4,
- normalize: false,
- values: typedArray,
- });
- }
- /**
- * Approximates an ellipsoid-tangent vector in 2D by projecting the end point into 2D.
- * Exposed for testing.
- *
- * @param {MapProjection} projection Map Projection for projecting coordinates to 2D.
- * @param {Cartographic} cartographic The cartographic origin point of the normal.
- * Used to check if the normal crosses the IDL during projection.
- * @param {Cartesian3} normal The normal in 3D.
- * @param {Cartesian3} projectedPosition The projected origin point of the normal in 2D.
- * @param {Cartesian3} result Result parameter on which to store the projected normal.
- * @private
- */
- GroundPolylineGeometry._projectNormal = projectNormal;
- function createGroundPolylineGeometry(groundPolylineGeometry, offset) {
- return ApproximateTerrainHeights.initialize().then(function () {
- if (defaultValue.defined(offset)) {
- groundPolylineGeometry = GroundPolylineGeometry.unpack(
- groundPolylineGeometry,
- offset
- );
- }
- return GroundPolylineGeometry.createGeometry(groundPolylineGeometry);
- });
- }
- return createGroundPolylineGeometry;
- }));
|