| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962 | import BoundingSphere from "../Core/BoundingSphere.js";import Cartesian2 from "../Core/Cartesian2.js";import Cartesian3 from "../Core/Cartesian3.js";import Cartesian4 from "../Core/Cartesian4.js";import Cartographic from "../Core/Cartographic.js";import Color from "../Core/Color.js";import combine from "../Core/combine.js";import ComponentDatatype from "../Core/ComponentDatatype.js";import defaultValue from "../Core/defaultValue.js";import defined from "../Core/defined.js";import destroyObject from "../Core/destroyObject.js";import DeveloperError from "../Core/DeveloperError.js";import EncodedCartesian3 from "../Core/EncodedCartesian3.js";import FeatureDetection from "../Core/FeatureDetection.js";import IndexDatatype from "../Core/IndexDatatype.js";import Intersect from "../Core/Intersect.js";import CesiumMath from "../Core/Math.js";import Matrix4 from "../Core/Matrix4.js";import Plane from "../Core/Plane.js";import RuntimeError from "../Core/RuntimeError.js";import Buffer from "../Renderer/Buffer.js";import BufferUsage from "../Renderer/BufferUsage.js";import ContextLimits from "../Renderer/ContextLimits.js";import DrawCommand from "../Renderer/DrawCommand.js";import Pass from "../Renderer/Pass.js";import RenderState from "../Renderer/RenderState.js";import ShaderProgram from "../Renderer/ShaderProgram.js";import ShaderSource from "../Renderer/ShaderSource.js";import Texture from "../Renderer/Texture.js";import VertexArray from "../Renderer/VertexArray.js";import PolylineCommon from "../Shaders/PolylineCommon.js";import PolylineFS from "../Shaders/PolylineFS.js";import PolylineVS from "../Shaders/PolylineVS.js";import BatchTable from "./BatchTable.js";import BlendingState from "./BlendingState.js";import Material from "./Material.js";import Polyline from "./Polyline.js";import SceneMode from "./SceneMode.js";const SHOW_INDEX = Polyline.SHOW_INDEX;const WIDTH_INDEX = Polyline.WIDTH_INDEX;const POSITION_INDEX = Polyline.POSITION_INDEX;const MATERIAL_INDEX = Polyline.MATERIAL_INDEX;//POSITION_SIZE_INDEX is needed for when the polyline's position array changes size.//When it does, we need to recreate the indicesBuffer.const POSITION_SIZE_INDEX = Polyline.POSITION_SIZE_INDEX;const DISTANCE_DISPLAY_CONDITION = Polyline.DISTANCE_DISPLAY_CONDITION;const NUMBER_OF_PROPERTIES = Polyline.NUMBER_OF_PROPERTIES;const attributeLocations = {  texCoordExpandAndBatchIndex: 0,  position3DHigh: 1,  position3DLow: 2,  position2DHigh: 3,  position2DLow: 4,  prevPosition3DHigh: 5,  prevPosition3DLow: 6,  prevPosition2DHigh: 7,  prevPosition2DLow: 8,  nextPosition3DHigh: 9,  nextPosition3DLow: 10,  nextPosition2DHigh: 11,  nextPosition2DLow: 12,};/** * A renderable collection of polylines. * <br /><br /> * <div align="center"> * <img src="Images/Polyline.png" width="400" height="300" /><br /> * Example polylines * </div> * <br /><br /> * Polylines are added and removed from the collection using {@link PolylineCollection#add} * and {@link PolylineCollection#remove}. * * @alias PolylineCollection * @constructor * * @param {Object} [options] Object with the following properties: * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms each polyline from model to world coordinates. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown. * @param {Boolean} [options.show=true] Determines if the polylines in the collection will be shown. * * @performance For best performance, prefer a few collections, each with many polylines, to * many collections with only a few polylines each.  Organize collections so that polylines * with the same update frequency are in the same collection, i.e., polylines that do not * change should be in one collection; polylines that change every frame should be in another * collection; and so on. * * @see PolylineCollection#add * @see PolylineCollection#remove * @see Polyline * @see LabelCollection * * @example * // Create a polyline collection with two polylines * const polylines = new Cesium.PolylineCollection(); * polylines.add({ *   positions : Cesium.Cartesian3.fromDegreesArray([ *     -75.10, 39.57, *     -77.02, 38.53, *     -80.50, 35.14, *     -80.12, 25.46]), *   width : 2 * }); * * polylines.add({ *   positions : Cesium.Cartesian3.fromDegreesArray([ *     -73.10, 37.57, *     -75.02, 36.53, *     -78.50, 33.14, *     -78.12, 23.46]), *   width : 4 * }); */function PolylineCollection(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  /**   * Determines if polylines in this collection will be shown.   *   * @type {Boolean}   * @default true   */  this.show = defaultValue(options.show, true);  /**   * The 4x4 transformation matrix that transforms each polyline in this collection from model to world coordinates.   * When this is the identity matrix, the polylines are drawn in world coordinates, i.e., Earth's WGS84 coordinates.   * Local reference frames can be used by providing a different transformation matrix, like that returned   * by {@link Transforms.eastNorthUpToFixedFrame}.   *   * @type {Matrix4}   * @default {@link Matrix4.IDENTITY}   */  this.modelMatrix = Matrix4.clone(    defaultValue(options.modelMatrix, Matrix4.IDENTITY)  );  this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY);  /**   * This property is for debugging only; it is not for production use nor is it optimized.   * <p>   * Draws the bounding sphere for each draw command in the primitive.   * </p>   *   * @type {Boolean}   *   * @default false   */  this.debugShowBoundingVolume = defaultValue(    options.debugShowBoundingVolume,    false  );  this._opaqueRS = undefined;  this._translucentRS = undefined;  this._colorCommands = [];  this._polylinesUpdated = false;  this._polylinesRemoved = false;  this._createVertexArray = false;  this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES);  this._polylines = [];  this._polylineBuckets = {};  // The buffer usage is determined based on the usage of the attribute over time.  this._positionBufferUsage = {    bufferUsage: BufferUsage.STATIC_DRAW,    frameCount: 0,  };  this._mode = undefined;  this._polylinesToUpdate = [];  this._vertexArrays = [];  this._positionBuffer = undefined;  this._texCoordExpandAndBatchIndexBuffer = undefined;  this._batchTable = undefined;  this._createBatchTable = false;  // Only used by Vector3DTilePoints  this._useHighlightColor = false;  this._highlightColor = Color.clone(Color.WHITE);  const that = this;  this._uniformMap = {    u_highlightColor: function () {      return that._highlightColor;    },  };}Object.defineProperties(PolylineCollection.prototype, {  /**   * Returns the number of polylines in this collection.  This is commonly used with   * {@link PolylineCollection#get} to iterate over all the polylines   * in the collection.   * @memberof PolylineCollection.prototype   * @type {Number}   */  length: {    get: function () {      removePolylines(this);      return this._polylines.length;    },  },});/**     * Creates and adds a polyline with the specified initial properties to the collection.     * The added polyline is returned so it can be modified or removed from the collection later.     *     * @param {Object}[options] A template describing the polyline's properties as shown in Example 1.     * @returns {Polyline} The polyline that was added to the collection.     *     * @performance After calling <code>add</code>, {@link PolylineCollection#update} is called and     * the collection's vertex buffer is rewritten - an <code>O(n)</code> operation that also incurs CPU to GPU overhead.     * For best performance, add as many polylines as possible before calling <code>update</code>.     *     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.     *     *     * @example     * // Example 1:  Add a polyline, specifying all the default values.     * const p = polylines.add({     *   show : true,     *   positions : ellipsoid.cartographicArrayToCartesianArray([           Cesium.Cartographic.fromDegrees(-75.10, 39.57),           Cesium.Cartographic.fromDegrees(-77.02, 38.53)]),     *   width : 1     * });     *     * @see PolylineCollection#remove     * @see PolylineCollection#removeAll     * @see PolylineCollection#update     */PolylineCollection.prototype.add = function (options) {  const p = new Polyline(options, this);  p._index = this._polylines.length;  this._polylines.push(p);  this._createVertexArray = true;  this._createBatchTable = true;  return p;};/** * Removes a polyline from the collection. * * @param {Polyline} polyline The polyline to remove. * @returns {Boolean} <code>true</code> if the polyline was removed; <code>false</code> if the polyline was not found in the collection. * * @performance After calling <code>remove</code>, {@link PolylineCollection#update} is called and * the collection's vertex buffer is rewritten - an <code>O(n)</code> operation that also incurs CPU to GPU overhead. * For best performance, remove as many polylines as possible before calling <code>update</code>. * If you intend to temporarily hide a polyline, it is usually more efficient to call * {@link Polyline#show} instead of removing and re-adding the polyline. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * const p = polylines.add(...); * polylines.remove(p);  // Returns true * * @see PolylineCollection#add * @see PolylineCollection#removeAll * @see PolylineCollection#update * @see Polyline#show */PolylineCollection.prototype.remove = function (polyline) {  if (this.contains(polyline)) {    this._polylinesRemoved = true;    this._createVertexArray = true;    this._createBatchTable = true;    if (defined(polyline._bucket)) {      const bucket = polyline._bucket;      bucket.shaderProgram =        bucket.shaderProgram && bucket.shaderProgram.destroy();    }    polyline._destroy();    return true;  }  return false;};/** * Removes all polylines from the collection. * * @performance <code>O(n)</code>.  It is more efficient to remove all the polylines * from a collection and then add new ones than to create a new collection entirely. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * polylines.add(...); * polylines.add(...); * polylines.removeAll(); * * @see PolylineCollection#add * @see PolylineCollection#remove * @see PolylineCollection#update */PolylineCollection.prototype.removeAll = function () {  releaseShaders(this);  destroyPolylines(this);  this._polylineBuckets = {};  this._polylinesRemoved = false;  this._polylines.length = 0;  this._polylinesToUpdate.length = 0;  this._createVertexArray = true;};/** * Determines if this collection contains the specified polyline. * * @param {Polyline} polyline The polyline to check for. * @returns {Boolean} true if this collection contains the polyline, false otherwise. * * @see PolylineCollection#get */PolylineCollection.prototype.contains = function (polyline) {  return defined(polyline) && polyline._polylineCollection === this;};/** * Returns the polyline in the collection at the specified index.  Indices are zero-based * and increase as polylines are added.  Removing a polyline shifts all polylines after * it to the left, changing their indices.  This function is commonly used with * {@link PolylineCollection#length} to iterate over all the polylines * in the collection. * * @param {Number} index The zero-based index of the polyline. * @returns {Polyline} The polyline at the specified index. * * @performance If polylines were removed from the collection and * {@link PolylineCollection#update} was not called, an implicit <code>O(n)</code> * operation is performed. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @example * // Toggle the show property of every polyline in the collection * const len = polylines.length; * for (let i = 0; i < len; ++i) { *   const p = polylines.get(i); *   p.show = !p.show; * } * * @see PolylineCollection#length */PolylineCollection.prototype.get = function (index) {  //>>includeStart('debug', pragmas.debug);  if (!defined(index)) {    throw new DeveloperError("index is required.");  }  //>>includeEnd('debug');  removePolylines(this);  return this._polylines[index];};function createBatchTable(collection, context) {  if (defined(collection._batchTable)) {    collection._batchTable.destroy();  }  const attributes = [    {      functionName: "batchTable_getWidthAndShow",      componentDatatype: ComponentDatatype.UNSIGNED_BYTE,      componentsPerAttribute: 2,    },    {      functionName: "batchTable_getPickColor",      componentDatatype: ComponentDatatype.UNSIGNED_BYTE,      componentsPerAttribute: 4,      normalize: true,    },    {      functionName: "batchTable_getCenterHigh",      componentDatatype: ComponentDatatype.FLOAT,      componentsPerAttribute: 3,    },    {      functionName: "batchTable_getCenterLowAndRadius",      componentDatatype: ComponentDatatype.FLOAT,      componentsPerAttribute: 4,    },    {      functionName: "batchTable_getDistanceDisplayCondition",      componentDatatype: ComponentDatatype.FLOAT,      componentsPerAttribute: 2,    },  ];  collection._batchTable = new BatchTable(    context,    attributes,    collection._polylines.length  );}const scratchUpdatePolylineEncodedCartesian = new EncodedCartesian3();const scratchUpdatePolylineCartesian4 = new Cartesian4();const scratchNearFarCartesian2 = new Cartesian2();/** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * get the draw commands needed to render this primitive. * <p> * Do not call this function directly.  This is documented just to * list the exceptions that may be propagated when the scene is rendered: * </p> * * @exception {RuntimeError} Vertex texture fetch support is required to render primitives with per-instance attributes. The maximum number of vertex texture image units must be greater than zero. */PolylineCollection.prototype.update = function (frameState) {  removePolylines(this);  if (this._polylines.length === 0 || !this.show) {    return;  }  updateMode(this, frameState);  const context = frameState.context;  const projection = frameState.mapProjection;  let polyline;  let properties = this._propertiesChanged;  if (this._createBatchTable) {    if (ContextLimits.maximumVertexTextureImageUnits === 0) {      throw new RuntimeError(        "Vertex texture fetch support is required to render polylines. The maximum number of vertex texture image units must be greater than zero."      );    }    createBatchTable(this, context);    this._createBatchTable = false;  }  if (this._createVertexArray || computeNewBuffersUsage(this)) {    createVertexArrays(this, context, projection);  } else if (this._polylinesUpdated) {    // Polylines were modified, but no polylines were added or removed.    const polylinesToUpdate = this._polylinesToUpdate;    if (this._mode !== SceneMode.SCENE3D) {      const updateLength = polylinesToUpdate.length;      for (let i = 0; i < updateLength; ++i) {        polyline = polylinesToUpdate[i];        polyline.update();      }    }    // if a polyline's positions size changes, we need to recreate the vertex arrays and vertex buffers because the indices will be different.    // if a polyline's material changes, we need to recreate the VAOs and VBOs because they will be batched differently.    if (properties[POSITION_SIZE_INDEX] || properties[MATERIAL_INDEX]) {      createVertexArrays(this, context, projection);    } else {      const length = polylinesToUpdate.length;      const polylineBuckets = this._polylineBuckets;      for (let ii = 0; ii < length; ++ii) {        polyline = polylinesToUpdate[ii];        properties = polyline._propertiesChanged;        const bucket = polyline._bucket;        let index = 0;        for (const x in polylineBuckets) {          if (polylineBuckets.hasOwnProperty(x)) {            if (polylineBuckets[x] === bucket) {              if (properties[POSITION_INDEX]) {                bucket.writeUpdate(                  index,                  polyline,                  this._positionBuffer,                  projection                );              }              break;            }            index += polylineBuckets[x].lengthOfPositions;          }        }        if (properties[SHOW_INDEX] || properties[WIDTH_INDEX]) {          this._batchTable.setBatchedAttribute(            polyline._index,            0,            new Cartesian2(polyline._width, polyline._show)          );        }        if (this._batchTable.attributes.length > 2) {          if (properties[POSITION_INDEX] || properties[POSITION_SIZE_INDEX]) {            const boundingSphere =              frameState.mode === SceneMode.SCENE2D                ? polyline._boundingVolume2D                : polyline._boundingVolumeWC;            const encodedCenter = EncodedCartesian3.fromCartesian(              boundingSphere.center,              scratchUpdatePolylineEncodedCartesian            );            const low = Cartesian4.fromElements(              encodedCenter.low.x,              encodedCenter.low.y,              encodedCenter.low.z,              boundingSphere.radius,              scratchUpdatePolylineCartesian4            );            this._batchTable.setBatchedAttribute(              polyline._index,              2,              encodedCenter.high            );            this._batchTable.setBatchedAttribute(polyline._index, 3, low);          }          if (properties[DISTANCE_DISPLAY_CONDITION]) {            const nearFarCartesian = scratchNearFarCartesian2;            nearFarCartesian.x = 0.0;            nearFarCartesian.y = Number.MAX_VALUE;            const distanceDisplayCondition = polyline.distanceDisplayCondition;            if (defined(distanceDisplayCondition)) {              nearFarCartesian.x = distanceDisplayCondition.near;              nearFarCartesian.y = distanceDisplayCondition.far;            }            this._batchTable.setBatchedAttribute(              polyline._index,              4,              nearFarCartesian            );          }        }        polyline._clean();      }    }    polylinesToUpdate.length = 0;    this._polylinesUpdated = false;  }  properties = this._propertiesChanged;  for (let k = 0; k < NUMBER_OF_PROPERTIES; ++k) {    properties[k] = 0;  }  let modelMatrix = Matrix4.IDENTITY;  if (frameState.mode === SceneMode.SCENE3D) {    modelMatrix = this.modelMatrix;  }  const pass = frameState.passes;  const useDepthTest = frameState.morphTime !== 0.0;  if (    !defined(this._opaqueRS) ||    this._opaqueRS.depthTest.enabled !== useDepthTest  ) {    this._opaqueRS = RenderState.fromCache({      depthMask: useDepthTest,      depthTest: {        enabled: useDepthTest,      },    });  }  if (    !defined(this._translucentRS) ||    this._translucentRS.depthTest.enabled !== useDepthTest  ) {    this._translucentRS = RenderState.fromCache({      blending: BlendingState.ALPHA_BLEND,      depthMask: !useDepthTest,      depthTest: {        enabled: useDepthTest,      },    });  }  this._batchTable.update(frameState);  if (pass.render || pass.pick) {    const colorList = this._colorCommands;    createCommandLists(this, frameState, colorList, modelMatrix);  }};const boundingSphereScratch = new BoundingSphere();const boundingSphereScratch2 = new BoundingSphere();function createCommandLists(  polylineCollection,  frameState,  commands,  modelMatrix) {  const context = frameState.context;  const commandList = frameState.commandList;  const commandsLength = commands.length;  let commandIndex = 0;  let cloneBoundingSphere = true;  const vertexArrays = polylineCollection._vertexArrays;  const debugShowBoundingVolume = polylineCollection.debugShowBoundingVolume;  const batchTable = polylineCollection._batchTable;  const uniformCallback = batchTable.getUniformMapCallback();  const length = vertexArrays.length;  for (let m = 0; m < length; ++m) {    const va = vertexArrays[m];    const buckets = va.buckets;    const bucketLength = buckets.length;    for (let n = 0; n < bucketLength; ++n) {      const bucketLocator = buckets[n];      let offset = bucketLocator.offset;      const sp = bucketLocator.bucket.shaderProgram;      const polylines = bucketLocator.bucket.polylines;      const polylineLength = polylines.length;      let currentId;      let currentMaterial;      let count = 0;      let command;      let uniformMap;      for (let s = 0; s < polylineLength; ++s) {        const polyline = polylines[s];        const mId = createMaterialId(polyline._material);        if (mId !== currentId) {          if (defined(currentId) && count > 0) {            const translucent = currentMaterial.isTranslucent();            if (commandIndex >= commandsLength) {              command = new DrawCommand({                owner: polylineCollection,              });              commands.push(command);            } else {              command = commands[commandIndex];            }            ++commandIndex;            uniformMap = combine(              uniformCallback(currentMaterial._uniforms),              polylineCollection._uniformMap            );            command.boundingVolume = BoundingSphere.clone(              boundingSphereScratch,              command.boundingVolume            );            command.modelMatrix = modelMatrix;            command.shaderProgram = sp;            command.vertexArray = va.va;            command.renderState = translucent              ? polylineCollection._translucentRS              : polylineCollection._opaqueRS;            command.pass = translucent ? Pass.TRANSLUCENT : Pass.OPAQUE;            command.debugShowBoundingVolume = debugShowBoundingVolume;            command.pickId = "v_pickColor";            command.uniformMap = uniformMap;            command.count = count;            command.offset = offset;            offset += count;            count = 0;            cloneBoundingSphere = true;            commandList.push(command);          }          currentMaterial = polyline._material;          currentMaterial.update(context);          currentId = mId;        }        const locators = polyline._locatorBuckets;        const locatorLength = locators.length;        for (let t = 0; t < locatorLength; ++t) {          const locator = locators[t];          if (locator.locator === bucketLocator) {            count += locator.count;          }        }        let boundingVolume;        if (frameState.mode === SceneMode.SCENE3D) {          boundingVolume = polyline._boundingVolumeWC;        } else if (frameState.mode === SceneMode.COLUMBUS_VIEW) {          boundingVolume = polyline._boundingVolume2D;        } else if (frameState.mode === SceneMode.SCENE2D) {          if (defined(polyline._boundingVolume2D)) {            boundingVolume = BoundingSphere.clone(              polyline._boundingVolume2D,              boundingSphereScratch2            );            boundingVolume.center.x = 0.0;          }        } else if (          defined(polyline._boundingVolumeWC) &&          defined(polyline._boundingVolume2D)        ) {          boundingVolume = BoundingSphere.union(            polyline._boundingVolumeWC,            polyline._boundingVolume2D,            boundingSphereScratch2          );        }        if (cloneBoundingSphere) {          cloneBoundingSphere = false;          BoundingSphere.clone(boundingVolume, boundingSphereScratch);        } else {          BoundingSphere.union(            boundingVolume,            boundingSphereScratch,            boundingSphereScratch          );        }      }      if (defined(currentId) && count > 0) {        if (commandIndex >= commandsLength) {          command = new DrawCommand({            owner: polylineCollection,          });          commands.push(command);        } else {          command = commands[commandIndex];        }        ++commandIndex;        uniformMap = combine(          uniformCallback(currentMaterial._uniforms),          polylineCollection._uniformMap        );        command.boundingVolume = BoundingSphere.clone(          boundingSphereScratch,          command.boundingVolume        );        command.modelMatrix = modelMatrix;        command.shaderProgram = sp;        command.vertexArray = va.va;        command.renderState = currentMaterial.isTranslucent()          ? polylineCollection._translucentRS          : polylineCollection._opaqueRS;        command.pass = currentMaterial.isTranslucent()          ? Pass.TRANSLUCENT          : Pass.OPAQUE;        command.debugShowBoundingVolume = debugShowBoundingVolume;        command.pickId = "v_pickColor";        command.uniformMap = uniformMap;        command.count = count;        command.offset = offset;        cloneBoundingSphere = true;        commandList.push(command);      }      currentId = undefined;    }  }  commands.length = commandIndex;}/** * Returns true if this object was destroyed; otherwise, false. * <br /><br /> * If this object was destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. * * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>. * * @see PolylineCollection#destroy */PolylineCollection.prototype.isDestroyed = function () {  return false;};/** * Destroys the WebGL resources held by this object.  Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. * <br /><br /> * Once an object is destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.  Therefore, * assign the return value (<code>undefined</code>) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * polylines = polylines && polylines.destroy(); * * @see PolylineCollection#isDestroyed */PolylineCollection.prototype.destroy = function () {  destroyVertexArrays(this);  releaseShaders(this);  destroyPolylines(this);  this._batchTable = this._batchTable && this._batchTable.destroy();  return destroyObject(this);};function computeNewBuffersUsage(collection) {  let usageChanged = false;  const properties = collection._propertiesChanged;  const bufferUsage = collection._positionBufferUsage;  if (properties[POSITION_INDEX]) {    if (bufferUsage.bufferUsage !== BufferUsage.STREAM_DRAW) {      usageChanged = true;      bufferUsage.bufferUsage = BufferUsage.STREAM_DRAW;      bufferUsage.frameCount = 100;    } else {      bufferUsage.frameCount = 100;    }  } else if (bufferUsage.bufferUsage !== BufferUsage.STATIC_DRAW) {    if (bufferUsage.frameCount === 0) {      usageChanged = true;      bufferUsage.bufferUsage = BufferUsage.STATIC_DRAW;    } else {      bufferUsage.frameCount--;    }  }  return usageChanged;}const emptyVertexBuffer = [0.0, 0.0, 0.0];function createVertexArrays(collection, context, projection) {  collection._createVertexArray = false;  releaseShaders(collection);  destroyVertexArrays(collection);  sortPolylinesIntoBuckets(collection);  //stores all of the individual indices arrays.  const totalIndices = [[]];  let indices = totalIndices[0];  const batchTable = collection._batchTable;  const useHighlightColor = collection._useHighlightColor;  //used to determine the vertexBuffer offset if the indicesArray goes over 64k.  //if it's the same polyline while it goes over 64k, the offset needs to backtrack componentsPerAttribute * componentDatatype bytes  //so that the polyline looks contiguous.  //if the polyline ends at the 64k mark, then the offset is just 64k * componentsPerAttribute * componentDatatype  const vertexBufferOffset = [0];  let offset = 0;  const vertexArrayBuckets = [[]];  let totalLength = 0;  const polylineBuckets = collection._polylineBuckets;  let x;  let bucket;  for (x in polylineBuckets) {    if (polylineBuckets.hasOwnProperty(x)) {      bucket = polylineBuckets[x];      bucket.updateShader(context, batchTable, useHighlightColor);      totalLength += bucket.lengthOfPositions;    }  }  if (totalLength > 0) {    const mode = collection._mode;    const positionArray = new Float32Array(6 * totalLength * 3);    const texCoordExpandAndBatchIndexArray = new Float32Array(totalLength * 4);    let position3DArray;    let positionIndex = 0;    let colorIndex = 0;    let texCoordExpandAndBatchIndexIndex = 0;    for (x in polylineBuckets) {      if (polylineBuckets.hasOwnProperty(x)) {        bucket = polylineBuckets[x];        bucket.write(          positionArray,          texCoordExpandAndBatchIndexArray,          positionIndex,          colorIndex,          texCoordExpandAndBatchIndexIndex,          batchTable,          context,          projection        );        if (mode === SceneMode.MORPHING) {          if (!defined(position3DArray)) {            position3DArray = new Float32Array(6 * totalLength * 3);          }          bucket.writeForMorph(position3DArray, positionIndex);        }        const bucketLength = bucket.lengthOfPositions;        positionIndex += 6 * bucketLength * 3;        colorIndex += bucketLength * 4;        texCoordExpandAndBatchIndexIndex += bucketLength * 4;        offset = bucket.updateIndices(          totalIndices,          vertexBufferOffset,          vertexArrayBuckets,          offset        );      }    }    const positionBufferUsage = collection._positionBufferUsage.bufferUsage;    const texCoordExpandAndBatchIndexBufferUsage = BufferUsage.STATIC_DRAW;    collection._positionBuffer = Buffer.createVertexBuffer({      context: context,      typedArray: positionArray,      usage: positionBufferUsage,    });    let position3DBuffer;    if (defined(position3DArray)) {      position3DBuffer = Buffer.createVertexBuffer({        context: context,        typedArray: position3DArray,        usage: positionBufferUsage,      });    }    collection._texCoordExpandAndBatchIndexBuffer = Buffer.createVertexBuffer({      context: context,      typedArray: texCoordExpandAndBatchIndexArray,      usage: texCoordExpandAndBatchIndexBufferUsage,    });    const positionSizeInBytes = 3 * Float32Array.BYTES_PER_ELEMENT;    const texCoordExpandAndBatchIndexSizeInBytes =      4 * Float32Array.BYTES_PER_ELEMENT;    let vbo = 0;    const numberOfIndicesArrays = totalIndices.length;    for (let k = 0; k < numberOfIndicesArrays; ++k) {      indices = totalIndices[k];      if (indices.length > 0) {        const indicesArray = new Uint16Array(indices);        const indexBuffer = Buffer.createIndexBuffer({          context: context,          typedArray: indicesArray,          usage: BufferUsage.STATIC_DRAW,          indexDatatype: IndexDatatype.UNSIGNED_SHORT,        });        vbo += vertexBufferOffset[k];        const positionHighOffset =          6 *          (k * (positionSizeInBytes * CesiumMath.SIXTY_FOUR_KILOBYTES) -            vbo * positionSizeInBytes); //componentsPerAttribute(3) * componentDatatype(4)        const positionLowOffset = positionSizeInBytes + positionHighOffset;        const prevPositionHighOffset = positionSizeInBytes + positionLowOffset;        const prevPositionLowOffset =          positionSizeInBytes + prevPositionHighOffset;        const nextPositionHighOffset =          positionSizeInBytes + prevPositionLowOffset;        const nextPositionLowOffset =          positionSizeInBytes + nextPositionHighOffset;        const vertexTexCoordExpandAndBatchIndexBufferOffset =          k *            (texCoordExpandAndBatchIndexSizeInBytes *              CesiumMath.SIXTY_FOUR_KILOBYTES) -          vbo * texCoordExpandAndBatchIndexSizeInBytes;        const attributes = [          {            index: attributeLocations.position3DHigh,            componentsPerAttribute: 3,            componentDatatype: ComponentDatatype.FLOAT,            offsetInBytes: positionHighOffset,            strideInBytes: 6 * positionSizeInBytes,          },          {            index: attributeLocations.position3DLow,            componentsPerAttribute: 3,            componentDatatype: ComponentDatatype.FLOAT,            offsetInBytes: positionLowOffset,            strideInBytes: 6 * positionSizeInBytes,          },          {            index: attributeLocations.position2DHigh,            componentsPerAttribute: 3,            componentDatatype: ComponentDatatype.FLOAT,            offsetInBytes: positionHighOffset,            strideInBytes: 6 * positionSizeInBytes,          },          {            index: attributeLocations.position2DLow,            componentsPerAttribute: 3,            componentDatatype: ComponentDatatype.FLOAT,            offsetInBytes: positionLowOffset,            strideInBytes: 6 * positionSizeInBytes,          },          {            index: attributeLocations.prevPosition3DHigh,            componentsPerAttribute: 3,            componentDatatype: ComponentDatatype.FLOAT,            offsetInBytes: prevPositionHighOffset,            strideInBytes: 6 * positionSizeInBytes,          },          {            index: attributeLocations.prevPosition3DLow,            componentsPerAttribute: 3,            componentDatatype: ComponentDatatype.FLOAT,            offsetInBytes: prevPositionLowOffset,            strideInBytes: 6 * positionSizeInBytes,          },          {            index: attributeLocations.prevPosition2DHigh,            componentsPerAttribute: 3,            componentDatatype: ComponentDatatype.FLOAT,            offsetInBytes: prevPositionHighOffset,            strideInBytes: 6 * positionSizeInBytes,          },          {            index: attributeLocations.prevPosition2DLow,            componentsPerAttribute: 3,            componentDatatype: ComponentDatatype.FLOAT,            offsetInBytes: prevPositionLowOffset,            strideInBytes: 6 * positionSizeInBytes,          },          {            index: attributeLocations.nextPosition3DHigh,            componentsPerAttribute: 3,            componentDatatype: ComponentDatatype.FLOAT,            offsetInBytes: nextPositionHighOffset,            strideInBytes: 6 * positionSizeInBytes,          },          {            index: attributeLocations.nextPosition3DLow,            componentsPerAttribute: 3,            componentDatatype: ComponentDatatype.FLOAT,            offsetInBytes: nextPositionLowOffset,            strideInBytes: 6 * positionSizeInBytes,          },          {            index: attributeLocations.nextPosition2DHigh,            componentsPerAttribute: 3,            componentDatatype: ComponentDatatype.FLOAT,            offsetInBytes: nextPositionHighOffset,            strideInBytes: 6 * positionSizeInBytes,          },          {            index: attributeLocations.nextPosition2DLow,            componentsPerAttribute: 3,            componentDatatype: ComponentDatatype.FLOAT,            offsetInBytes: nextPositionLowOffset,            strideInBytes: 6 * positionSizeInBytes,          },          {            index: attributeLocations.texCoordExpandAndBatchIndex,            componentsPerAttribute: 4,            componentDatatype: ComponentDatatype.FLOAT,            vertexBuffer: collection._texCoordExpandAndBatchIndexBuffer,            offsetInBytes: vertexTexCoordExpandAndBatchIndexBufferOffset,          },        ];        let bufferProperty3D;        let buffer3D;        let buffer2D;        let bufferProperty2D;        if (mode === SceneMode.SCENE3D) {          buffer3D = collection._positionBuffer;          bufferProperty3D = "vertexBuffer";          buffer2D = emptyVertexBuffer;          bufferProperty2D = "value";        } else if (          mode === SceneMode.SCENE2D ||          mode === SceneMode.COLUMBUS_VIEW        ) {          buffer3D = emptyVertexBuffer;          bufferProperty3D = "value";          buffer2D = collection._positionBuffer;          bufferProperty2D = "vertexBuffer";        } else {          buffer3D = position3DBuffer;          bufferProperty3D = "vertexBuffer";          buffer2D = collection._positionBuffer;          bufferProperty2D = "vertexBuffer";        }        attributes[0][bufferProperty3D] = buffer3D;        attributes[1][bufferProperty3D] = buffer3D;        attributes[2][bufferProperty2D] = buffer2D;        attributes[3][bufferProperty2D] = buffer2D;        attributes[4][bufferProperty3D] = buffer3D;        attributes[5][bufferProperty3D] = buffer3D;        attributes[6][bufferProperty2D] = buffer2D;        attributes[7][bufferProperty2D] = buffer2D;        attributes[8][bufferProperty3D] = buffer3D;        attributes[9][bufferProperty3D] = buffer3D;        attributes[10][bufferProperty2D] = buffer2D;        attributes[11][bufferProperty2D] = buffer2D;        const va = new VertexArray({          context: context,          attributes: attributes,          indexBuffer: indexBuffer,        });        collection._vertexArrays.push({          va: va,          buckets: vertexArrayBuckets[k],        });      }    }  }}function replacer(key, value) {  if (value instanceof Texture) {    return value.id;  }  return value;}const scratchUniformArray = [];function createMaterialId(material) {  const uniforms = Material._uniformList[material.type];  const length = uniforms.length;  scratchUniformArray.length = 2.0 * length;  let index = 0;  for (let i = 0; i < length; ++i) {    const uniform = uniforms[i];    scratchUniformArray[index] = uniform;    scratchUniformArray[index + 1] = material._uniforms[uniform]();    index += 2;  }  return `${material.type}:${JSON.stringify(scratchUniformArray, replacer)}`;}function sortPolylinesIntoBuckets(collection) {  const mode = collection._mode;  const modelMatrix = collection._modelMatrix;  const polylineBuckets = (collection._polylineBuckets = {});  const polylines = collection._polylines;  const length = polylines.length;  for (let i = 0; i < length; ++i) {    const p = polylines[i];    if (p._actualPositions.length > 1) {      p.update();      const material = p.material;      let value = polylineBuckets[material.type];      if (!defined(value)) {        value = polylineBuckets[material.type] = new PolylineBucket(          material,          mode,          modelMatrix        );      }      value.addPolyline(p);    }  }}function updateMode(collection, frameState) {  const mode = frameState.mode;  if (    collection._mode !== mode ||    !Matrix4.equals(collection._modelMatrix, collection.modelMatrix)  ) {    collection._mode = mode;    collection._modelMatrix = Matrix4.clone(collection.modelMatrix);    collection._createVertexArray = true;  }}function removePolylines(collection) {  if (collection._polylinesRemoved) {    collection._polylinesRemoved = false;    const definedPolylines = [];    const definedPolylinesToUpdate = [];    let polyIndex = 0;    let polyline;    const length = collection._polylines.length;    for (let i = 0; i < length; ++i) {      polyline = collection._polylines[i];      if (!polyline.isDestroyed) {        polyline._index = polyIndex++;        definedPolylinesToUpdate.push(polyline);        definedPolylines.push(polyline);      }    }    collection._polylines = definedPolylines;    collection._polylinesToUpdate = definedPolylinesToUpdate;  }}function releaseShaders(collection) {  const polylines = collection._polylines;  const length = polylines.length;  for (let i = 0; i < length; ++i) {    if (!polylines[i].isDestroyed) {      const bucket = polylines[i]._bucket;      if (defined(bucket)) {        bucket.shaderProgram =          bucket.shaderProgram && bucket.shaderProgram.destroy();      }    }  }}function destroyVertexArrays(collection) {  const length = collection._vertexArrays.length;  for (let t = 0; t < length; ++t) {    collection._vertexArrays[t].va.destroy();  }  collection._vertexArrays.length = 0;}PolylineCollection.prototype._updatePolyline = function (  polyline,  propertyChanged) {  this._polylinesUpdated = true;  if (!polyline._dirty) {    this._polylinesToUpdate.push(polyline);  }  ++this._propertiesChanged[propertyChanged];};function destroyPolylines(collection) {  const polylines = collection._polylines;  const length = polylines.length;  for (let i = 0; i < length; ++i) {    if (!polylines[i].isDestroyed) {      polylines[i]._destroy();    }  }}function VertexArrayBucketLocator(count, offset, bucket) {  this.count = count;  this.offset = offset;  this.bucket = bucket;}function PolylineBucket(material, mode, modelMatrix) {  this.polylines = [];  this.lengthOfPositions = 0;  this.material = material;  this.shaderProgram = undefined;  this.mode = mode;  this.modelMatrix = modelMatrix;}PolylineBucket.prototype.addPolyline = function (p) {  const polylines = this.polylines;  polylines.push(p);  p._actualLength = this.getPolylinePositionsLength(p);  this.lengthOfPositions += p._actualLength;  p._bucket = this;};PolylineBucket.prototype.updateShader = function (  context,  batchTable,  useHighlightColor) {  if (defined(this.shaderProgram)) {    return;  }  const defines = ["DISTANCE_DISPLAY_CONDITION"];  if (useHighlightColor) {    defines.push("VECTOR_TILE");  }  // Check for use of v_polylineAngle in material shader  if (    this.material.shaderSource.search(/varying\s+float\s+v_polylineAngle;/g) !==    -1  ) {    defines.push("POLYLINE_DASH");  }  if (!FeatureDetection.isInternetExplorer()) {    defines.push("CLIP_POLYLINE");  }  const fs = new ShaderSource({    defines: defines,    sources: [      "varying vec4 v_pickColor;\n",      this.material.shaderSource,      PolylineFS,    ],  });  const vsSource = batchTable.getVertexShaderCallback()(PolylineVS);  const vs = new ShaderSource({    defines: defines,    sources: [PolylineCommon, vsSource],  });  this.shaderProgram = ShaderProgram.fromCache({    context: context,    vertexShaderSource: vs,    fragmentShaderSource: fs,    attributeLocations: attributeLocations,  });};function intersectsIDL(polyline) {  return (    Cartesian3.dot(Cartesian3.UNIT_X, polyline._boundingVolume.center) < 0 ||    polyline._boundingVolume.intersectPlane(Plane.ORIGIN_ZX_PLANE) ===      Intersect.INTERSECTING  );}PolylineBucket.prototype.getPolylinePositionsLength = function (polyline) {  let length;  if (this.mode === SceneMode.SCENE3D || !intersectsIDL(polyline)) {    length = polyline._actualPositions.length;    return length * 4.0 - 4.0;  }  let count = 0;  const segmentLengths = polyline._segments.lengths;  length = segmentLengths.length;  for (let i = 0; i < length; ++i) {    count += segmentLengths[i] * 4.0 - 4.0;  }  return count;};const scratchWritePosition = new Cartesian3();const scratchWritePrevPosition = new Cartesian3();const scratchWriteNextPosition = new Cartesian3();const scratchWriteVector = new Cartesian3();const scratchPickColorCartesian = new Cartesian4();const scratchWidthShowCartesian = new Cartesian2();PolylineBucket.prototype.write = function (  positionArray,  texCoordExpandAndBatchIndexArray,  positionIndex,  colorIndex,  texCoordExpandAndBatchIndexIndex,  batchTable,  context,  projection) {  const mode = this.mode;  const maxLon = projection.ellipsoid.maximumRadius * CesiumMath.PI;  const polylines = this.polylines;  const length = polylines.length;  for (let i = 0; i < length; ++i) {    const polyline = polylines[i];    const width = polyline.width;    const show = polyline.show && width > 0.0;    const polylineBatchIndex = polyline._index;    const segments = this.getSegments(polyline, projection);    const positions = segments.positions;    const lengths = segments.lengths;    const positionsLength = positions.length;    const pickColor = polyline.getPickId(context).color;    let segmentIndex = 0;    let count = 0;    let position;    for (let j = 0; j < positionsLength; ++j) {      if (j === 0) {        if (polyline._loop) {          position = positions[positionsLength - 2];        } else {          position = scratchWriteVector;          Cartesian3.subtract(positions[0], positions[1], position);          Cartesian3.add(positions[0], position, position);        }      } else {        position = positions[j - 1];      }      Cartesian3.clone(position, scratchWritePrevPosition);      Cartesian3.clone(positions[j], scratchWritePosition);      if (j === positionsLength - 1) {        if (polyline._loop) {          position = positions[1];        } else {          position = scratchWriteVector;          Cartesian3.subtract(            positions[positionsLength - 1],            positions[positionsLength - 2],            position          );          Cartesian3.add(positions[positionsLength - 1], position, position);        }      } else {        position = positions[j + 1];      }      Cartesian3.clone(position, scratchWriteNextPosition);      const segmentLength = lengths[segmentIndex];      if (j === count + segmentLength) {        count += segmentLength;        ++segmentIndex;      }      const segmentStart = j - count === 0;      const segmentEnd = j === count + lengths[segmentIndex] - 1;      if (mode === SceneMode.SCENE2D) {        scratchWritePrevPosition.z = 0.0;        scratchWritePosition.z = 0.0;        scratchWriteNextPosition.z = 0.0;      }      if (mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) {        if (          (segmentStart || segmentEnd) &&          maxLon - Math.abs(scratchWritePosition.x) < 1.0        ) {          if (            (scratchWritePosition.x < 0.0 &&              scratchWritePrevPosition.x > 0.0) ||            (scratchWritePosition.x > 0.0 && scratchWritePrevPosition.x < 0.0)          ) {            Cartesian3.clone(scratchWritePosition, scratchWritePrevPosition);          }          if (            (scratchWritePosition.x < 0.0 &&              scratchWriteNextPosition.x > 0.0) ||            (scratchWritePosition.x > 0.0 && scratchWriteNextPosition.x < 0.0)          ) {            Cartesian3.clone(scratchWritePosition, scratchWriteNextPosition);          }        }      }      const startK = segmentStart ? 2 : 0;      const endK = segmentEnd ? 2 : 4;      for (let k = startK; k < endK; ++k) {        EncodedCartesian3.writeElements(          scratchWritePosition,          positionArray,          positionIndex        );        EncodedCartesian3.writeElements(          scratchWritePrevPosition,          positionArray,          positionIndex + 6        );        EncodedCartesian3.writeElements(          scratchWriteNextPosition,          positionArray,          positionIndex + 12        );        const direction = k - 2 < 0 ? -1.0 : 1.0;        texCoordExpandAndBatchIndexArray[texCoordExpandAndBatchIndexIndex] =          j / (positionsLength - 1); // s tex coord        texCoordExpandAndBatchIndexArray[texCoordExpandAndBatchIndexIndex + 1] =          2 * (k % 2) - 1; // expand direction        texCoordExpandAndBatchIndexArray[          texCoordExpandAndBatchIndexIndex + 2        ] = direction;        texCoordExpandAndBatchIndexArray[          texCoordExpandAndBatchIndexIndex + 3        ] = polylineBatchIndex;        positionIndex += 6 * 3;        texCoordExpandAndBatchIndexIndex += 4;      }    }    const colorCartesian = scratchPickColorCartesian;    colorCartesian.x = Color.floatToByte(pickColor.red);    colorCartesian.y = Color.floatToByte(pickColor.green);    colorCartesian.z = Color.floatToByte(pickColor.blue);    colorCartesian.w = Color.floatToByte(pickColor.alpha);    const widthShowCartesian = scratchWidthShowCartesian;    widthShowCartesian.x = width;    widthShowCartesian.y = show ? 1.0 : 0.0;    const boundingSphere =      mode === SceneMode.SCENE2D        ? polyline._boundingVolume2D        : polyline._boundingVolumeWC;    const encodedCenter = EncodedCartesian3.fromCartesian(      boundingSphere.center,      scratchUpdatePolylineEncodedCartesian    );    const high = encodedCenter.high;    const low = Cartesian4.fromElements(      encodedCenter.low.x,      encodedCenter.low.y,      encodedCenter.low.z,      boundingSphere.radius,      scratchUpdatePolylineCartesian4    );    const nearFarCartesian = scratchNearFarCartesian2;    nearFarCartesian.x = 0.0;    nearFarCartesian.y = Number.MAX_VALUE;    const distanceDisplayCondition = polyline.distanceDisplayCondition;    if (defined(distanceDisplayCondition)) {      nearFarCartesian.x = distanceDisplayCondition.near;      nearFarCartesian.y = distanceDisplayCondition.far;    }    batchTable.setBatchedAttribute(polylineBatchIndex, 0, widthShowCartesian);    batchTable.setBatchedAttribute(polylineBatchIndex, 1, colorCartesian);    if (batchTable.attributes.length > 2) {      batchTable.setBatchedAttribute(polylineBatchIndex, 2, high);      batchTable.setBatchedAttribute(polylineBatchIndex, 3, low);      batchTable.setBatchedAttribute(polylineBatchIndex, 4, nearFarCartesian);    }  }};const morphPositionScratch = new Cartesian3();const morphPrevPositionScratch = new Cartesian3();const morphNextPositionScratch = new Cartesian3();const morphVectorScratch = new Cartesian3();PolylineBucket.prototype.writeForMorph = function (  positionArray,  positionIndex) {  const modelMatrix = this.modelMatrix;  const polylines = this.polylines;  const length = polylines.length;  for (let i = 0; i < length; ++i) {    const polyline = polylines[i];    const positions = polyline._segments.positions;    const lengths = polyline._segments.lengths;    const positionsLength = positions.length;    let segmentIndex = 0;    let count = 0;    for (let j = 0; j < positionsLength; ++j) {      let prevPosition;      if (j === 0) {        if (polyline._loop) {          prevPosition = positions[positionsLength - 2];        } else {          prevPosition = morphVectorScratch;          Cartesian3.subtract(positions[0], positions[1], prevPosition);          Cartesian3.add(positions[0], prevPosition, prevPosition);        }      } else {        prevPosition = positions[j - 1];      }      prevPosition = Matrix4.multiplyByPoint(        modelMatrix,        prevPosition,        morphPrevPositionScratch      );      const position = Matrix4.multiplyByPoint(        modelMatrix,        positions[j],        morphPositionScratch      );      let nextPosition;      if (j === positionsLength - 1) {        if (polyline._loop) {          nextPosition = positions[1];        } else {          nextPosition = morphVectorScratch;          Cartesian3.subtract(            positions[positionsLength - 1],            positions[positionsLength - 2],            nextPosition          );          Cartesian3.add(            positions[positionsLength - 1],            nextPosition,            nextPosition          );        }      } else {        nextPosition = positions[j + 1];      }      nextPosition = Matrix4.multiplyByPoint(        modelMatrix,        nextPosition,        morphNextPositionScratch      );      const segmentLength = lengths[segmentIndex];      if (j === count + segmentLength) {        count += segmentLength;        ++segmentIndex;      }      const segmentStart = j - count === 0;      const segmentEnd = j === count + lengths[segmentIndex] - 1;      const startK = segmentStart ? 2 : 0;      const endK = segmentEnd ? 2 : 4;      for (let k = startK; k < endK; ++k) {        EncodedCartesian3.writeElements(position, positionArray, positionIndex);        EncodedCartesian3.writeElements(          prevPosition,          positionArray,          positionIndex + 6        );        EncodedCartesian3.writeElements(          nextPosition,          positionArray,          positionIndex + 12        );        positionIndex += 6 * 3;      }    }  }};const scratchSegmentLengths = new Array(1);PolylineBucket.prototype.updateIndices = function (  totalIndices,  vertexBufferOffset,  vertexArrayBuckets,  offset) {  let vaCount = vertexArrayBuckets.length - 1;  let bucketLocator = new VertexArrayBucketLocator(0, offset, this);  vertexArrayBuckets[vaCount].push(bucketLocator);  let count = 0;  let indices = totalIndices[totalIndices.length - 1];  let indicesCount = 0;  if (indices.length > 0) {    indicesCount = indices[indices.length - 1] + 1;  }  const polylines = this.polylines;  const length = polylines.length;  for (let i = 0; i < length; ++i) {    const polyline = polylines[i];    polyline._locatorBuckets = [];    let segments;    if (this.mode === SceneMode.SCENE3D) {      segments = scratchSegmentLengths;      const positionsLength = polyline._actualPositions.length;      if (positionsLength > 0) {        segments[0] = positionsLength;      } else {        continue;      }    } else {      segments = polyline._segments.lengths;    }    const numberOfSegments = segments.length;    if (numberOfSegments > 0) {      let segmentIndexCount = 0;      for (let j = 0; j < numberOfSegments; ++j) {        const segmentLength = segments[j] - 1.0;        for (let k = 0; k < segmentLength; ++k) {          if (indicesCount + 4 > CesiumMath.SIXTY_FOUR_KILOBYTES) {            polyline._locatorBuckets.push({              locator: bucketLocator,              count: segmentIndexCount,            });            segmentIndexCount = 0;            vertexBufferOffset.push(4);            indices = [];            totalIndices.push(indices);            indicesCount = 0;            bucketLocator.count = count;            count = 0;            offset = 0;            bucketLocator = new VertexArrayBucketLocator(0, 0, this);            vertexArrayBuckets[++vaCount] = [bucketLocator];          }          indices.push(indicesCount, indicesCount + 2, indicesCount + 1);          indices.push(indicesCount + 1, indicesCount + 2, indicesCount + 3);          segmentIndexCount += 6;          count += 6;          offset += 6;          indicesCount += 4;        }      }      polyline._locatorBuckets.push({        locator: bucketLocator,        count: segmentIndexCount,      });      if (indicesCount + 4 > CesiumMath.SIXTY_FOUR_KILOBYTES) {        vertexBufferOffset.push(0);        indices = [];        totalIndices.push(indices);        indicesCount = 0;        bucketLocator.count = count;        offset = 0;        count = 0;        bucketLocator = new VertexArrayBucketLocator(0, 0, this);        vertexArrayBuckets[++vaCount] = [bucketLocator];      }    }    polyline._clean();  }  bucketLocator.count = count;  return offset;};PolylineBucket.prototype.getPolylineStartIndex = function (polyline) {  const polylines = this.polylines;  let positionIndex = 0;  const length = polylines.length;  for (let i = 0; i < length; ++i) {    const p = polylines[i];    if (p === polyline) {      break;    }    positionIndex += p._actualLength;  }  return positionIndex;};const scratchSegments = {  positions: undefined,  lengths: undefined,};const scratchLengths = new Array(1);const pscratch = new Cartesian3();const scratchCartographic = new Cartographic();PolylineBucket.prototype.getSegments = function (polyline, projection) {  let positions = polyline._actualPositions;  if (this.mode === SceneMode.SCENE3D) {    scratchLengths[0] = positions.length;    scratchSegments.positions = positions;    scratchSegments.lengths = scratchLengths;    return scratchSegments;  }  if (intersectsIDL(polyline)) {    positions = polyline._segments.positions;  }  const ellipsoid = projection.ellipsoid;  const newPositions = [];  const modelMatrix = this.modelMatrix;  const length = positions.length;  let position;  let p = pscratch;  for (let n = 0; n < length; ++n) {    position = positions[n];    p = Matrix4.multiplyByPoint(modelMatrix, position, p);    newPositions.push(      projection.project(        ellipsoid.cartesianToCartographic(p, scratchCartographic)      )    );  }  if (newPositions.length > 0) {    polyline._boundingVolume2D = BoundingSphere.fromPoints(      newPositions,      polyline._boundingVolume2D    );    const center2D = polyline._boundingVolume2D.center;    polyline._boundingVolume2D.center = new Cartesian3(      center2D.z,      center2D.x,      center2D.y    );  }  scratchSegments.positions = newPositions;  scratchSegments.lengths = polyline._segments.lengths;  return scratchSegments;};let scratchPositionsArray;PolylineBucket.prototype.writeUpdate = function (  index,  polyline,  positionBuffer,  projection) {  const mode = this.mode;  const maxLon = projection.ellipsoid.maximumRadius * CesiumMath.PI;  let positionsLength = polyline._actualLength;  if (positionsLength) {    index += this.getPolylineStartIndex(polyline);    let positionArray = scratchPositionsArray;    const positionsArrayLength = 6 * positionsLength * 3;    if (      !defined(positionArray) ||      positionArray.length < positionsArrayLength    ) {      positionArray = scratchPositionsArray = new Float32Array(        positionsArrayLength      );    } else if (positionArray.length > positionsArrayLength) {      positionArray = new Float32Array(        positionArray.buffer,        0,        positionsArrayLength      );    }    const segments = this.getSegments(polyline, projection);    const positions = segments.positions;    const lengths = segments.lengths;    let positionIndex = 0;    let segmentIndex = 0;    let count = 0;    let position;    positionsLength = positions.length;    for (let i = 0; i < positionsLength; ++i) {      if (i === 0) {        if (polyline._loop) {          position = positions[positionsLength - 2];        } else {          position = scratchWriteVector;          Cartesian3.subtract(positions[0], positions[1], position);          Cartesian3.add(positions[0], position, position);        }      } else {        position = positions[i - 1];      }      Cartesian3.clone(position, scratchWritePrevPosition);      Cartesian3.clone(positions[i], scratchWritePosition);      if (i === positionsLength - 1) {        if (polyline._loop) {          position = positions[1];        } else {          position = scratchWriteVector;          Cartesian3.subtract(            positions[positionsLength - 1],            positions[positionsLength - 2],            position          );          Cartesian3.add(positions[positionsLength - 1], position, position);        }      } else {        position = positions[i + 1];      }      Cartesian3.clone(position, scratchWriteNextPosition);      const segmentLength = lengths[segmentIndex];      if (i === count + segmentLength) {        count += segmentLength;        ++segmentIndex;      }      const segmentStart = i - count === 0;      const segmentEnd = i === count + lengths[segmentIndex] - 1;      if (mode === SceneMode.SCENE2D) {        scratchWritePrevPosition.z = 0.0;        scratchWritePosition.z = 0.0;        scratchWriteNextPosition.z = 0.0;      }      if (mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) {        if (          (segmentStart || segmentEnd) &&          maxLon - Math.abs(scratchWritePosition.x) < 1.0        ) {          if (            (scratchWritePosition.x < 0.0 &&              scratchWritePrevPosition.x > 0.0) ||            (scratchWritePosition.x > 0.0 && scratchWritePrevPosition.x < 0.0)          ) {            Cartesian3.clone(scratchWritePosition, scratchWritePrevPosition);          }          if (            (scratchWritePosition.x < 0.0 &&              scratchWriteNextPosition.x > 0.0) ||            (scratchWritePosition.x > 0.0 && scratchWriteNextPosition.x < 0.0)          ) {            Cartesian3.clone(scratchWritePosition, scratchWriteNextPosition);          }        }      }      const startJ = segmentStart ? 2 : 0;      const endJ = segmentEnd ? 2 : 4;      for (let j = startJ; j < endJ; ++j) {        EncodedCartesian3.writeElements(          scratchWritePosition,          positionArray,          positionIndex        );        EncodedCartesian3.writeElements(          scratchWritePrevPosition,          positionArray,          positionIndex + 6        );        EncodedCartesian3.writeElements(          scratchWriteNextPosition,          positionArray,          positionIndex + 12        );        positionIndex += 6 * 3;      }    }    positionBuffer.copyFromArrayView(      positionArray,      6 * 3 * Float32Array.BYTES_PER_ELEMENT * index    );  }};export default PolylineCollection;
 |