| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 | import AttributeCompression from "../Core/AttributeCompression.js";import Cartesian3 from "../Core/Cartesian3.js";import Cartographic from "../Core/Cartographic.js";import combine from "../Core/combine.js";import Ellipsoid from "../Core/Ellipsoid.js";import IndexDatatype from "../Core/IndexDatatype.js";import CesiumMath from "../Core/Math.js";import Rectangle from "../Core/Rectangle.js";import createTaskProcessorWorker from "./createTaskProcessorWorker.js";const MAX_SHORT = 32767;const MITER_BREAK = Math.cos(CesiumMath.toRadians(150.0));const scratchBVCartographic = new Cartographic();const scratchEncodedPosition = new Cartesian3();function decodePositions(  uBuffer,  vBuffer,  heightBuffer,  rectangle,  minimumHeight,  maximumHeight,  ellipsoid) {  const positionsLength = uBuffer.length;  const decodedPositions = new Float64Array(positionsLength * 3);  for (let i = 0; i < positionsLength; ++i) {    const u = uBuffer[i];    const v = vBuffer[i];    const h = heightBuffer[i];    const lon = CesiumMath.lerp(rectangle.west, rectangle.east, u / MAX_SHORT);    const lat = CesiumMath.lerp(      rectangle.south,      rectangle.north,      v / MAX_SHORT    );    const alt = CesiumMath.lerp(minimumHeight, maximumHeight, h / MAX_SHORT);    const cartographic = Cartographic.fromRadians(      lon,      lat,      alt,      scratchBVCartographic    );    const decodedPosition = ellipsoid.cartographicToCartesian(      cartographic,      scratchEncodedPosition    );    Cartesian3.pack(decodedPosition, decodedPositions, i * 3);  }  return decodedPositions;}function getPositionOffsets(counts) {  const countsLength = counts.length;  const positionOffsets = new Uint32Array(countsLength + 1);  let offset = 0;  for (let i = 0; i < countsLength; ++i) {    positionOffsets[i] = offset;    offset += counts[i];  }  positionOffsets[countsLength] = offset;  return positionOffsets;}const previousCompressedCartographicScratch = new Cartographic();const currentCompressedCartographicScratch = new Cartographic();function removeDuplicates(uBuffer, vBuffer, heightBuffer, counts) {  const countsLength = counts.length;  const positionsLength = uBuffer.length;  const markRemoval = new Uint8Array(positionsLength);  const previous = previousCompressedCartographicScratch;  const current = currentCompressedCartographicScratch;  let offset = 0;  for (let i = 0; i < countsLength; i++) {    const count = counts[i];    let updatedCount = count;    for (let j = 1; j < count; j++) {      const index = offset + j;      const previousIndex = index - 1;      current.longitude = uBuffer[index];      current.latitude = vBuffer[index];      previous.longitude = uBuffer[previousIndex];      previous.latitude = vBuffer[previousIndex];      if (Cartographic.equals(current, previous)) {        updatedCount--;        markRemoval[previousIndex] = 1;      }    }    counts[i] = updatedCount;    offset += count;  }  let nextAvailableIndex = 0;  for (let k = 0; k < positionsLength; k++) {    if (markRemoval[k] !== 1) {      uBuffer[nextAvailableIndex] = uBuffer[k];      vBuffer[nextAvailableIndex] = vBuffer[k];      heightBuffer[nextAvailableIndex] = heightBuffer[k];      nextAvailableIndex++;    }  }}function VertexAttributesAndIndices(volumesCount) {  const vertexCount = volumesCount * 8;  const vec3Floats = vertexCount * 3;  const vec4Floats = vertexCount * 4;  this.startEllipsoidNormals = new Float32Array(vec3Floats);  this.endEllipsoidNormals = new Float32Array(vec3Floats);  this.startPositionAndHeights = new Float32Array(vec4Floats);  this.startFaceNormalAndVertexCornerIds = new Float32Array(vec4Floats);  this.endPositionAndHeights = new Float32Array(vec4Floats);  this.endFaceNormalAndHalfWidths = new Float32Array(vec4Floats);  this.vertexBatchIds = new Uint16Array(vertexCount);  this.indices = IndexDatatype.createTypedArray(vertexCount, 36 * volumesCount);  this.vec3Offset = 0;  this.vec4Offset = 0;  this.batchIdOffset = 0;  this.indexOffset = 0;  this.volumeStartIndex = 0;}const towardCurrScratch = new Cartesian3();const towardNextScratch = new Cartesian3();function computeMiteredNormal(  previousPosition,  position,  nextPosition,  ellipsoidSurfaceNormal,  result) {  const towardNext = Cartesian3.subtract(    nextPosition,    position,    towardNextScratch  );  let towardCurr = Cartesian3.subtract(    position,    previousPosition,    towardCurrScratch  );  Cartesian3.normalize(towardNext, towardNext);  Cartesian3.normalize(towardCurr, towardCurr);  if (Cartesian3.dot(towardNext, towardCurr) < MITER_BREAK) {    towardCurr = Cartesian3.multiplyByScalar(      towardCurr,      -1.0,      towardCurrScratch    );  }  Cartesian3.add(towardNext, towardCurr, result);  if (Cartesian3.equals(result, Cartesian3.ZERO)) {    result = Cartesian3.subtract(previousPosition, position);  }  // Make sure the normal is orthogonal to the ellipsoid surface normal  Cartesian3.cross(result, ellipsoidSurfaceNormal, result);  Cartesian3.cross(ellipsoidSurfaceNormal, result, result);  Cartesian3.normalize(result, result);  return result;}// Winding order is reversed so each segment's volume is inside-out//          3-----------7//         /|   left   /|//        / | 1       / |//       2-----------6  5  end//       | /         | /// start |/  right   |///       0-----------4//const REFERENCE_INDICES = [  0,  2,  6,  0,  6,  4, // right  0,  1,  3,  0,  3,  2, // start face  0,  4,  5,  0,  5,  1, // bottom  5,  3,  1,  5,  7,  3, // left  7,  5,  4,  7,  4,  6, // end face  7,  6,  2,  7,  2,  3, // top];const REFERENCE_INDICES_LENGTH = REFERENCE_INDICES.length;const positionScratch = new Cartesian3();const scratchStartEllipsoidNormal = new Cartesian3();const scratchStartFaceNormal = new Cartesian3();const scratchEndEllipsoidNormal = new Cartesian3();const scratchEndFaceNormal = new Cartesian3();VertexAttributesAndIndices.prototype.addVolume = function (  preStartRTC,  startRTC,  endRTC,  postEndRTC,  startHeight,  endHeight,  halfWidth,  batchId,  center,  ellipsoid) {  let position = Cartesian3.add(startRTC, center, positionScratch);  const startEllipsoidNormal = ellipsoid.geodeticSurfaceNormal(    position,    scratchStartEllipsoidNormal  );  position = Cartesian3.add(endRTC, center, positionScratch);  const endEllipsoidNormal = ellipsoid.geodeticSurfaceNormal(    position,    scratchEndEllipsoidNormal  );  const startFaceNormal = computeMiteredNormal(    preStartRTC,    startRTC,    endRTC,    startEllipsoidNormal,    scratchStartFaceNormal  );  const endFaceNormal = computeMiteredNormal(    postEndRTC,    endRTC,    startRTC,    endEllipsoidNormal,    scratchEndFaceNormal  );  const startEllipsoidNormals = this.startEllipsoidNormals;  const endEllipsoidNormals = this.endEllipsoidNormals;  const startPositionAndHeights = this.startPositionAndHeights;  const startFaceNormalAndVertexCornerIds = this    .startFaceNormalAndVertexCornerIds;  const endPositionAndHeights = this.endPositionAndHeights;  const endFaceNormalAndHalfWidths = this.endFaceNormalAndHalfWidths;  const vertexBatchIds = this.vertexBatchIds;  let batchIdOffset = this.batchIdOffset;  let vec3Offset = this.vec3Offset;  let vec4Offset = this.vec4Offset;  let i;  for (i = 0; i < 8; i++) {    Cartesian3.pack(startEllipsoidNormal, startEllipsoidNormals, vec3Offset);    Cartesian3.pack(endEllipsoidNormal, endEllipsoidNormals, vec3Offset);    Cartesian3.pack(startRTC, startPositionAndHeights, vec4Offset);    startPositionAndHeights[vec4Offset + 3] = startHeight;    Cartesian3.pack(endRTC, endPositionAndHeights, vec4Offset);    endPositionAndHeights[vec4Offset + 3] = endHeight;    Cartesian3.pack(      startFaceNormal,      startFaceNormalAndVertexCornerIds,      vec4Offset    );    startFaceNormalAndVertexCornerIds[vec4Offset + 3] = i;    Cartesian3.pack(endFaceNormal, endFaceNormalAndHalfWidths, vec4Offset);    endFaceNormalAndHalfWidths[vec4Offset + 3] = halfWidth;    vertexBatchIds[batchIdOffset++] = batchId;    vec3Offset += 3;    vec4Offset += 4;  }  this.batchIdOffset = batchIdOffset;  this.vec3Offset = vec3Offset;  this.vec4Offset = vec4Offset;  const indices = this.indices;  const volumeStartIndex = this.volumeStartIndex;  const indexOffset = this.indexOffset;  for (i = 0; i < REFERENCE_INDICES_LENGTH; i++) {    indices[indexOffset + i] = REFERENCE_INDICES[i] + volumeStartIndex;  }  this.volumeStartIndex += 8;  this.indexOffset += REFERENCE_INDICES_LENGTH;};const scratchRectangle = new Rectangle();const scratchEllipsoid = new Ellipsoid();const scratchCenter = new Cartesian3();const scratchPrev = new Cartesian3();const scratchP0 = new Cartesian3();const scratchP1 = new Cartesian3();const scratchNext = new Cartesian3();function createVectorTileClampedPolylines(parameters, transferableObjects) {  const encodedPositions = new Uint16Array(parameters.positions);  const widths = new Uint16Array(parameters.widths);  const counts = new Uint32Array(parameters.counts);  const batchIds = new Uint16Array(parameters.batchIds);  // Unpack tile decoding parameters  const rectangle = scratchRectangle;  const ellipsoid = scratchEllipsoid;  const center = scratchCenter;  const packedBuffer = new Float64Array(parameters.packedBuffer);  let offset = 0;  const minimumHeight = packedBuffer[offset++];  const maximumHeight = packedBuffer[offset++];  Rectangle.unpack(packedBuffer, offset, rectangle);  offset += Rectangle.packedLength;  Ellipsoid.unpack(packedBuffer, offset, ellipsoid);  offset += Ellipsoid.packedLength;  Cartesian3.unpack(packedBuffer, offset, center);  let i;  // Unpack positions and generate volumes  let positionsLength = encodedPositions.length / 3;  const uBuffer = encodedPositions.subarray(0, positionsLength);  const vBuffer = encodedPositions.subarray(    positionsLength,    2 * positionsLength  );  const heightBuffer = encodedPositions.subarray(    2 * positionsLength,    3 * positionsLength  );  AttributeCompression.zigZagDeltaDecode(uBuffer, vBuffer, heightBuffer);  removeDuplicates(uBuffer, vBuffer, heightBuffer, counts);  // Figure out how many volumes and how many vertices there will be.  const countsLength = counts.length;  let volumesCount = 0;  for (i = 0; i < countsLength; i++) {    const polylinePositionCount = counts[i];    volumesCount += polylinePositionCount - 1;  }  const attribsAndIndices = new VertexAttributesAndIndices(volumesCount);  const positions = decodePositions(    uBuffer,    vBuffer,    heightBuffer,    rectangle,    minimumHeight,    maximumHeight,    ellipsoid,    center  );  positionsLength = uBuffer.length;  const positionsRTC = new Float32Array(positionsLength * 3);  for (i = 0; i < positionsLength; ++i) {    positionsRTC[i * 3] = positions[i * 3] - center.x;    positionsRTC[i * 3 + 1] = positions[i * 3 + 1] - center.y;    positionsRTC[i * 3 + 2] = positions[i * 3 + 2] - center.z;  }  let currentPositionIndex = 0;  let currentHeightIndex = 0;  for (i = 0; i < countsLength; i++) {    const polylineVolumeCount = counts[i] - 1;    const halfWidth = widths[i] * 0.5;    const batchId = batchIds[i];    const volumeFirstPositionIndex = currentPositionIndex;    for (let j = 0; j < polylineVolumeCount; j++) {      const volumeStart = Cartesian3.unpack(        positionsRTC,        currentPositionIndex,        scratchP0      );      const volumeEnd = Cartesian3.unpack(        positionsRTC,        currentPositionIndex + 3,        scratchP1      );      let startHeight = heightBuffer[currentHeightIndex];      let endHeight = heightBuffer[currentHeightIndex + 1];      startHeight = CesiumMath.lerp(        minimumHeight,        maximumHeight,        startHeight / MAX_SHORT      );      endHeight = CesiumMath.lerp(        minimumHeight,        maximumHeight,        endHeight / MAX_SHORT      );      currentHeightIndex++;      let preStart = scratchPrev;      let postEnd = scratchNext;      if (j === 0) {        // Check if this volume is like a loop        const finalPositionIndex =          volumeFirstPositionIndex + polylineVolumeCount * 3;        const finalPosition = Cartesian3.unpack(          positionsRTC,          finalPositionIndex,          scratchPrev        );        if (Cartesian3.equals(finalPosition, volumeStart)) {          Cartesian3.unpack(positionsRTC, finalPositionIndex - 3, preStart);        } else {          const offsetPastStart = Cartesian3.subtract(            volumeStart,            volumeEnd,            scratchPrev          );          preStart = Cartesian3.add(offsetPastStart, volumeStart, scratchPrev);        }      } else {        Cartesian3.unpack(positionsRTC, currentPositionIndex - 3, preStart);      }      if (j === polylineVolumeCount - 1) {        // Check if this volume is like a loop        const firstPosition = Cartesian3.unpack(          positionsRTC,          volumeFirstPositionIndex,          scratchNext        );        if (Cartesian3.equals(firstPosition, volumeEnd)) {          Cartesian3.unpack(            positionsRTC,            volumeFirstPositionIndex + 3,            postEnd          );        } else {          const offsetPastEnd = Cartesian3.subtract(            volumeEnd,            volumeStart,            scratchNext          );          postEnd = Cartesian3.add(offsetPastEnd, volumeEnd, scratchNext);        }      } else {        Cartesian3.unpack(positionsRTC, currentPositionIndex + 6, postEnd);      }      attribsAndIndices.addVolume(        preStart,        volumeStart,        volumeEnd,        postEnd,        startHeight,        endHeight,        halfWidth,        batchId,        center,        ellipsoid      );      currentPositionIndex += 3;    }    currentPositionIndex += 3;    currentHeightIndex++;  }  const indices = attribsAndIndices.indices;  transferableObjects.push(attribsAndIndices.startEllipsoidNormals.buffer);  transferableObjects.push(attribsAndIndices.endEllipsoidNormals.buffer);  transferableObjects.push(attribsAndIndices.startPositionAndHeights.buffer);  transferableObjects.push(    attribsAndIndices.startFaceNormalAndVertexCornerIds.buffer  );  transferableObjects.push(attribsAndIndices.endPositionAndHeights.buffer);  transferableObjects.push(attribsAndIndices.endFaceNormalAndHalfWidths.buffer);  transferableObjects.push(attribsAndIndices.vertexBatchIds.buffer);  transferableObjects.push(indices.buffer);  let results = {    indexDatatype:      indices.BYTES_PER_ELEMENT === 2        ? IndexDatatype.UNSIGNED_SHORT        : IndexDatatype.UNSIGNED_INT,    startEllipsoidNormals: attribsAndIndices.startEllipsoidNormals.buffer,    endEllipsoidNormals: attribsAndIndices.endEllipsoidNormals.buffer,    startPositionAndHeights: attribsAndIndices.startPositionAndHeights.buffer,    startFaceNormalAndVertexCornerIds:      attribsAndIndices.startFaceNormalAndVertexCornerIds.buffer,    endPositionAndHeights: attribsAndIndices.endPositionAndHeights.buffer,    endFaceNormalAndHalfWidths:      attribsAndIndices.endFaceNormalAndHalfWidths.buffer,    vertexBatchIds: attribsAndIndices.vertexBatchIds.buffer,    indices: indices.buffer,  };  if (parameters.keepDecodedPositions) {    const positionOffsets = getPositionOffsets(counts);    transferableObjects.push(positions.buffer, positionOffsets.buffer);    results = combine(results, {      decodedPositions: positions.buffer,      decodedPositionOffsets: positionOffsets.buffer,    });  }  return results;}export default createTaskProcessorWorker(createVectorTileClampedPolylines);
 |