| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741 | import ArcType from "./ArcType.js";import arrayRemoveDuplicates from "./arrayRemoveDuplicates.js";import Cartesian2 from "./Cartesian2.js";import Cartesian3 from "./Cartesian3.js";import Cartographic from "./Cartographic.js";import ComponentDatatype from "./ComponentDatatype.js";import defaultValue from "./defaultValue.js";import defined from "./defined.js";import Ellipsoid from "./Ellipsoid.js";import EllipsoidRhumbLine from "./EllipsoidRhumbLine.js";import Geometry from "./Geometry.js";import GeometryAttribute from "./GeometryAttribute.js";import GeometryAttributes from "./GeometryAttributes.js";import GeometryPipeline from "./GeometryPipeline.js";import IndexDatatype from "./IndexDatatype.js";import CesiumMath from "./Math.js";import Matrix3 from "./Matrix3.js";import PolygonPipeline from "./PolygonPipeline.js";import PrimitiveType from "./PrimitiveType.js";import Quaternion from "./Quaternion.js";import Queue from "./Queue.js";import WindingOrder from "./WindingOrder.js";/** * @private */const PolygonGeometryLibrary = {};PolygonGeometryLibrary.computeHierarchyPackedLength = function (  polygonHierarchy) {  let numComponents = 0;  const stack = [polygonHierarchy];  while (stack.length > 0) {    const hierarchy = stack.pop();    if (!defined(hierarchy)) {      continue;    }    numComponents += 2;    const positions = hierarchy.positions;    const holes = hierarchy.holes;    if (defined(positions)) {      numComponents += positions.length * Cartesian3.packedLength;    }    if (defined(holes)) {      const length = holes.length;      for (let i = 0; i < length; ++i) {        stack.push(holes[i]);      }    }  }  return numComponents;};PolygonGeometryLibrary.packPolygonHierarchy = function (  polygonHierarchy,  array,  startingIndex) {  const stack = [polygonHierarchy];  while (stack.length > 0) {    const hierarchy = stack.pop();    if (!defined(hierarchy)) {      continue;    }    const positions = hierarchy.positions;    const holes = hierarchy.holes;    array[startingIndex++] = defined(positions) ? positions.length : 0;    array[startingIndex++] = defined(holes) ? holes.length : 0;    if (defined(positions)) {      const positionsLength = positions.length;      for (let i = 0; i < positionsLength; ++i, startingIndex += 3) {        Cartesian3.pack(positions[i], array, startingIndex);      }    }    if (defined(holes)) {      const holesLength = holes.length;      for (let j = 0; j < holesLength; ++j) {        stack.push(holes[j]);      }    }  }  return startingIndex;};PolygonGeometryLibrary.unpackPolygonHierarchy = function (  array,  startingIndex) {  const positionsLength = array[startingIndex++];  const holesLength = array[startingIndex++];  const positions = new Array(positionsLength);  const holes = holesLength > 0 ? new Array(holesLength) : undefined;  for (    let i = 0;    i < positionsLength;    ++i, startingIndex += Cartesian3.packedLength  ) {    positions[i] = Cartesian3.unpack(array, startingIndex);  }  for (let j = 0; j < holesLength; ++j) {    holes[j] = PolygonGeometryLibrary.unpackPolygonHierarchy(      array,      startingIndex    );    startingIndex = holes[j].startingIndex;    delete holes[j].startingIndex;  }  return {    positions: positions,    holes: holes,    startingIndex: startingIndex,  };};const distanceScratch = new Cartesian3();function getPointAtDistance(p0, p1, distance, length) {  Cartesian3.subtract(p1, p0, distanceScratch);  Cartesian3.multiplyByScalar(    distanceScratch,    distance / length,    distanceScratch  );  Cartesian3.add(p0, distanceScratch, distanceScratch);  return [distanceScratch.x, distanceScratch.y, distanceScratch.z];}PolygonGeometryLibrary.subdivideLineCount = function (p0, p1, minDistance) {  const distance = Cartesian3.distance(p0, p1);  const n = distance / minDistance;  const countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n)));  return Math.pow(2, countDivide);};const scratchCartographic0 = new Cartographic();const scratchCartographic1 = new Cartographic();const scratchCartographic2 = new Cartographic();const scratchCartesian0 = new Cartesian3();PolygonGeometryLibrary.subdivideRhumbLineCount = function (  ellipsoid,  p0,  p1,  minDistance) {  const c0 = ellipsoid.cartesianToCartographic(p0, scratchCartographic0);  const c1 = ellipsoid.cartesianToCartographic(p1, scratchCartographic1);  const rhumb = new EllipsoidRhumbLine(c0, c1, ellipsoid);  const n = rhumb.surfaceDistance / minDistance;  const countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n)));  return Math.pow(2, countDivide);};PolygonGeometryLibrary.subdivideLine = function (p0, p1, minDistance, result) {  const numVertices = PolygonGeometryLibrary.subdivideLineCount(    p0,    p1,    minDistance  );  const length = Cartesian3.distance(p0, p1);  const distanceBetweenVertices = length / numVertices;  if (!defined(result)) {    result = [];  }  const positions = result;  positions.length = numVertices * 3;  let index = 0;  for (let i = 0; i < numVertices; i++) {    const p = getPointAtDistance(p0, p1, i * distanceBetweenVertices, length);    positions[index++] = p[0];    positions[index++] = p[1];    positions[index++] = p[2];  }  return positions;};PolygonGeometryLibrary.subdivideRhumbLine = function (  ellipsoid,  p0,  p1,  minDistance,  result) {  const c0 = ellipsoid.cartesianToCartographic(p0, scratchCartographic0);  const c1 = ellipsoid.cartesianToCartographic(p1, scratchCartographic1);  const rhumb = new EllipsoidRhumbLine(c0, c1, ellipsoid);  const n = rhumb.surfaceDistance / minDistance;  const countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n)));  const numVertices = Math.pow(2, countDivide);  const distanceBetweenVertices = rhumb.surfaceDistance / numVertices;  if (!defined(result)) {    result = [];  }  const positions = result;  positions.length = numVertices * 3;  let index = 0;  for (let i = 0; i < numVertices; i++) {    const c = rhumb.interpolateUsingSurfaceDistance(      i * distanceBetweenVertices,      scratchCartographic2    );    const p = ellipsoid.cartographicToCartesian(c, scratchCartesian0);    positions[index++] = p.x;    positions[index++] = p.y;    positions[index++] = p.z;  }  return positions;};const scaleToGeodeticHeightN1 = new Cartesian3();const scaleToGeodeticHeightN2 = new Cartesian3();const scaleToGeodeticHeightP1 = new Cartesian3();const scaleToGeodeticHeightP2 = new Cartesian3();PolygonGeometryLibrary.scaleToGeodeticHeightExtruded = function (  geometry,  maxHeight,  minHeight,  ellipsoid,  perPositionHeight) {  ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);  const n1 = scaleToGeodeticHeightN1;  let n2 = scaleToGeodeticHeightN2;  const p = scaleToGeodeticHeightP1;  let p2 = scaleToGeodeticHeightP2;  if (    defined(geometry) &&    defined(geometry.attributes) &&    defined(geometry.attributes.position)  ) {    const positions = geometry.attributes.position.values;    const length = positions.length / 2;    for (let i = 0; i < length; i += 3) {      Cartesian3.fromArray(positions, i, p);      ellipsoid.geodeticSurfaceNormal(p, n1);      p2 = ellipsoid.scaleToGeodeticSurface(p, p2);      n2 = Cartesian3.multiplyByScalar(n1, minHeight, n2);      n2 = Cartesian3.add(p2, n2, n2);      positions[i + length] = n2.x;      positions[i + 1 + length] = n2.y;      positions[i + 2 + length] = n2.z;      if (perPositionHeight) {        p2 = Cartesian3.clone(p, p2);      }      n2 = Cartesian3.multiplyByScalar(n1, maxHeight, n2);      n2 = Cartesian3.add(p2, n2, n2);      positions[i] = n2.x;      positions[i + 1] = n2.y;      positions[i + 2] = n2.z;    }  }  return geometry;};PolygonGeometryLibrary.polygonOutlinesFromHierarchy = function (  polygonHierarchy,  scaleToEllipsoidSurface,  ellipsoid) {  // create from a polygon hierarchy  // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf  const polygons = [];  const queue = new Queue();  queue.enqueue(polygonHierarchy);  let i;  let j;  let length;  while (queue.length !== 0) {    const outerNode = queue.dequeue();    let outerRing = outerNode.positions;    if (scaleToEllipsoidSurface) {      length = outerRing.length;      for (i = 0; i < length; i++) {        ellipsoid.scaleToGeodeticSurface(outerRing[i], outerRing[i]);      }    }    outerRing = arrayRemoveDuplicates(      outerRing,      Cartesian3.equalsEpsilon,      true    );    if (outerRing.length < 3) {      continue;    }    const numChildren = outerNode.holes ? outerNode.holes.length : 0;    // The outer polygon contains inner polygons    for (i = 0; i < numChildren; i++) {      const hole = outerNode.holes[i];      let holePositions = hole.positions;      if (scaleToEllipsoidSurface) {        length = holePositions.length;        for (j = 0; j < length; ++j) {          ellipsoid.scaleToGeodeticSurface(holePositions[j], holePositions[j]);        }      }      holePositions = arrayRemoveDuplicates(        holePositions,        Cartesian3.equalsEpsilon,        true      );      if (holePositions.length < 3) {        continue;      }      polygons.push(holePositions);      let numGrandchildren = 0;      if (defined(hole.holes)) {        numGrandchildren = hole.holes.length;      }      for (j = 0; j < numGrandchildren; j++) {        queue.enqueue(hole.holes[j]);      }    }    polygons.push(outerRing);  }  return polygons;};PolygonGeometryLibrary.polygonsFromHierarchy = function (  polygonHierarchy,  projectPointsTo2D,  scaleToEllipsoidSurface,  ellipsoid) {  // create from a polygon hierarchy  // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf  const hierarchy = [];  const polygons = [];  const queue = new Queue();  queue.enqueue(polygonHierarchy);  while (queue.length !== 0) {    const outerNode = queue.dequeue();    let outerRing = outerNode.positions;    const holes = outerNode.holes;    let i;    let length;    if (scaleToEllipsoidSurface) {      length = outerRing.length;      for (i = 0; i < length; i++) {        ellipsoid.scaleToGeodeticSurface(outerRing[i], outerRing[i]);      }    }    outerRing = arrayRemoveDuplicates(      outerRing,      Cartesian3.equalsEpsilon,      true    );    if (outerRing.length < 3) {      continue;    }    let positions2D = projectPointsTo2D(outerRing);    if (!defined(positions2D)) {      continue;    }    const holeIndices = [];    let originalWindingOrder = PolygonPipeline.computeWindingOrder2D(      positions2D    );    if (originalWindingOrder === WindingOrder.CLOCKWISE) {      positions2D.reverse();      outerRing = outerRing.slice().reverse();    }    let positions = outerRing.slice();    const numChildren = defined(holes) ? holes.length : 0;    const polygonHoles = [];    let j;    for (i = 0; i < numChildren; i++) {      const hole = holes[i];      let holePositions = hole.positions;      if (scaleToEllipsoidSurface) {        length = holePositions.length;        for (j = 0; j < length; ++j) {          ellipsoid.scaleToGeodeticSurface(holePositions[j], holePositions[j]);        }      }      holePositions = arrayRemoveDuplicates(        holePositions,        Cartesian3.equalsEpsilon,        true      );      if (holePositions.length < 3) {        continue;      }      const holePositions2D = projectPointsTo2D(holePositions);      if (!defined(holePositions2D)) {        continue;      }      originalWindingOrder = PolygonPipeline.computeWindingOrder2D(        holePositions2D      );      if (originalWindingOrder === WindingOrder.CLOCKWISE) {        holePositions2D.reverse();        holePositions = holePositions.slice().reverse();      }      polygonHoles.push(holePositions);      holeIndices.push(positions.length);      positions = positions.concat(holePositions);      positions2D = positions2D.concat(holePositions2D);      let numGrandchildren = 0;      if (defined(hole.holes)) {        numGrandchildren = hole.holes.length;      }      for (j = 0; j < numGrandchildren; j++) {        queue.enqueue(hole.holes[j]);      }    }    hierarchy.push({      outerRing: outerRing,      holes: polygonHoles,    });    polygons.push({      positions: positions,      positions2D: positions2D,      holes: holeIndices,    });  }  return {    hierarchy: hierarchy,    polygons: polygons,  };};const computeBoundingRectangleCartesian2 = new Cartesian2();const computeBoundingRectangleCartesian3 = new Cartesian3();const computeBoundingRectangleQuaternion = new Quaternion();const computeBoundingRectangleMatrix3 = new Matrix3();PolygonGeometryLibrary.computeBoundingRectangle = function (  planeNormal,  projectPointTo2D,  positions,  angle,  result) {  const rotation = Quaternion.fromAxisAngle(    planeNormal,    angle,    computeBoundingRectangleQuaternion  );  const textureMatrix = Matrix3.fromQuaternion(    rotation,    computeBoundingRectangleMatrix3  );  let minX = Number.POSITIVE_INFINITY;  let maxX = Number.NEGATIVE_INFINITY;  let minY = Number.POSITIVE_INFINITY;  let maxY = Number.NEGATIVE_INFINITY;  const length = positions.length;  for (let i = 0; i < length; ++i) {    const p = Cartesian3.clone(      positions[i],      computeBoundingRectangleCartesian3    );    Matrix3.multiplyByVector(textureMatrix, p, p);    const st = projectPointTo2D(p, computeBoundingRectangleCartesian2);    if (defined(st)) {      minX = Math.min(minX, st.x);      maxX = Math.max(maxX, st.x);      minY = Math.min(minY, st.y);      maxY = Math.max(maxY, st.y);    }  }  result.x = minX;  result.y = minY;  result.width = maxX - minX;  result.height = maxY - minY;  return result;};PolygonGeometryLibrary.createGeometryFromPositions = function (  ellipsoid,  polygon,  granularity,  perPositionHeight,  vertexFormat,  arcType) {  let indices = PolygonPipeline.triangulate(polygon.positions2D, polygon.holes);  /* If polygon is completely unrenderable, just use the first three vertices */  if (indices.length < 3) {    indices = [0, 1, 2];  }  const positions = polygon.positions;  if (perPositionHeight) {    const length = positions.length;    const flattenedPositions = new Array(length * 3);    let index = 0;    for (let i = 0; i < length; i++) {      const p = positions[i];      flattenedPositions[index++] = p.x;      flattenedPositions[index++] = p.y;      flattenedPositions[index++] = p.z;    }    const geometry = new Geometry({      attributes: {        position: new GeometryAttribute({          componentDatatype: ComponentDatatype.DOUBLE,          componentsPerAttribute: 3,          values: flattenedPositions,        }),      },      indices: indices,      primitiveType: PrimitiveType.TRIANGLES,    });    if (vertexFormat.normal) {      return GeometryPipeline.computeNormal(geometry);    }    return geometry;  }  if (arcType === ArcType.GEODESIC) {    return PolygonPipeline.computeSubdivision(      ellipsoid,      positions,      indices,      granularity    );  } else if (arcType === ArcType.RHUMB) {    return PolygonPipeline.computeRhumbLineSubdivision(      ellipsoid,      positions,      indices,      granularity    );  }};const computeWallIndicesSubdivided = [];const p1Scratch = new Cartesian3();const p2Scratch = new Cartesian3();PolygonGeometryLibrary.computeWallGeometry = function (  positions,  ellipsoid,  granularity,  perPositionHeight,  arcType) {  let edgePositions;  let topEdgeLength;  let i;  let p1;  let p2;  let length = positions.length;  let index = 0;  if (!perPositionHeight) {    const minDistance = CesiumMath.chordLength(      granularity,      ellipsoid.maximumRadius    );    let numVertices = 0;    if (arcType === ArcType.GEODESIC) {      for (i = 0; i < length; i++) {        numVertices += PolygonGeometryLibrary.subdivideLineCount(          positions[i],          positions[(i + 1) % length],          minDistance        );      }    } else if (arcType === ArcType.RHUMB) {      for (i = 0; i < length; i++) {        numVertices += PolygonGeometryLibrary.subdivideRhumbLineCount(          ellipsoid,          positions[i],          positions[(i + 1) % length],          minDistance        );      }    }    topEdgeLength = (numVertices + length) * 3;    edgePositions = new Array(topEdgeLength * 2);    for (i = 0; i < length; i++) {      p1 = positions[i];      p2 = positions[(i + 1) % length];      let tempPositions;      if (arcType === ArcType.GEODESIC) {        tempPositions = PolygonGeometryLibrary.subdivideLine(          p1,          p2,          minDistance,          computeWallIndicesSubdivided        );      } else if (arcType === ArcType.RHUMB) {        tempPositions = PolygonGeometryLibrary.subdivideRhumbLine(          ellipsoid,          p1,          p2,          minDistance,          computeWallIndicesSubdivided        );      }      const tempPositionsLength = tempPositions.length;      for (let j = 0; j < tempPositionsLength; ++j, ++index) {        edgePositions[index] = tempPositions[j];        edgePositions[index + topEdgeLength] = tempPositions[j];      }      edgePositions[index] = p2.x;      edgePositions[index + topEdgeLength] = p2.x;      ++index;      edgePositions[index] = p2.y;      edgePositions[index + topEdgeLength] = p2.y;      ++index;      edgePositions[index] = p2.z;      edgePositions[index + topEdgeLength] = p2.z;      ++index;    }  } else {    topEdgeLength = length * 3 * 2;    edgePositions = new Array(topEdgeLength * 2);    for (i = 0; i < length; i++) {      p1 = positions[i];      p2 = positions[(i + 1) % length];      edgePositions[index] = edgePositions[index + topEdgeLength] = p1.x;      ++index;      edgePositions[index] = edgePositions[index + topEdgeLength] = p1.y;      ++index;      edgePositions[index] = edgePositions[index + topEdgeLength] = p1.z;      ++index;      edgePositions[index] = edgePositions[index + topEdgeLength] = p2.x;      ++index;      edgePositions[index] = edgePositions[index + topEdgeLength] = p2.y;      ++index;      edgePositions[index] = edgePositions[index + topEdgeLength] = p2.z;      ++index;    }  }  length = edgePositions.length;  const indices = IndexDatatype.createTypedArray(    length / 3,    length - positions.length * 6  );  let edgeIndex = 0;  length /= 6;  for (i = 0; i < length; i++) {    const UL = i;    const UR = UL + 1;    const LL = UL + length;    const LR = LL + 1;    p1 = Cartesian3.fromArray(edgePositions, UL * 3, p1Scratch);    p2 = Cartesian3.fromArray(edgePositions, UR * 3, p2Scratch);    if (      Cartesian3.equalsEpsilon(        p1,        p2,        CesiumMath.EPSILON10,        CesiumMath.EPSILON10      )    ) {      //skip corner      continue;    }    indices[edgeIndex++] = UL;    indices[edgeIndex++] = LL;    indices[edgeIndex++] = UR;    indices[edgeIndex++] = UR;    indices[edgeIndex++] = LL;    indices[edgeIndex++] = LR;  }  return new Geometry({    attributes: new GeometryAttributes({      position: new GeometryAttribute({        componentDatatype: ComponentDatatype.DOUBLE,        componentsPerAttribute: 3,        values: edgePositions,      }),    }),    indices: indices,    primitiveType: PrimitiveType.TRIANGLES,  });};export default PolygonGeometryLibrary;
 |