| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 | import earcut from "../ThirdParty/earcut.js";import Cartesian2 from "./Cartesian2.js";import Cartesian3 from "./Cartesian3.js";import Cartographic from "./Cartographic.js";import Check from "./Check.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 CesiumMath from "./Math.js";import PrimitiveType from "./PrimitiveType.js";import WindingOrder from "./WindingOrder.js";const scaleToGeodeticHeightN = new Cartesian3();const scaleToGeodeticHeightP = new Cartesian3();/** * @private */const PolygonPipeline = {};/** * @exception {DeveloperError} At least three positions are required. */PolygonPipeline.computeArea2D = function (positions) {  //>>includeStart('debug', pragmas.debug);  Check.defined("positions", positions);  Check.typeOf.number.greaterThanOrEquals(    "positions.length",    positions.length,    3  );  //>>includeEnd('debug');  const length = positions.length;  let area = 0.0;  for (let i0 = length - 1, i1 = 0; i1 < length; i0 = i1++) {    const v0 = positions[i0];    const v1 = positions[i1];    area += v0.x * v1.y - v1.x * v0.y;  }  return area * 0.5;};/** * @returns {WindingOrder} The winding order. * * @exception {DeveloperError} At least three positions are required. */PolygonPipeline.computeWindingOrder2D = function (positions) {  const area = PolygonPipeline.computeArea2D(positions);  return area > 0.0 ? WindingOrder.COUNTER_CLOCKWISE : WindingOrder.CLOCKWISE;};/** * Triangulate a polygon. * * @param {Cartesian2[]} positions Cartesian2 array containing the vertices of the polygon * @param {Number[]} [holes] An array of the staring indices of the holes. * @returns {Number[]} Index array representing triangles that fill the polygon */PolygonPipeline.triangulate = function (positions, holes) {  //>>includeStart('debug', pragmas.debug);  Check.defined("positions", positions);  //>>includeEnd('debug');  const flattenedPositions = Cartesian2.packArray(positions);  return earcut(flattenedPositions, holes, 2);};const subdivisionV0Scratch = new Cartesian3();const subdivisionV1Scratch = new Cartesian3();const subdivisionV2Scratch = new Cartesian3();const subdivisionS0Scratch = new Cartesian3();const subdivisionS1Scratch = new Cartesian3();const subdivisionS2Scratch = new Cartesian3();const subdivisionMidScratch = new Cartesian3();/** * Subdivides positions and raises points to the surface of the ellipsoid. * * @param {Ellipsoid} ellipsoid The ellipsoid the polygon in on. * @param {Cartesian3[]} positions An array of {@link Cartesian3} positions of the polygon. * @param {Number[]} indices An array of indices that determines the triangles in the polygon. * @param {Number} [granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer. * * @exception {DeveloperError} At least three indices are required. * @exception {DeveloperError} The number of indices must be divisable by three. * @exception {DeveloperError} Granularity must be greater than zero. */PolygonPipeline.computeSubdivision = function (  ellipsoid,  positions,  indices,  granularity) {  granularity = defaultValue(granularity, CesiumMath.RADIANS_PER_DEGREE);  //>>includeStart('debug', pragmas.debug);  Check.typeOf.object("ellipsoid", ellipsoid);  Check.defined("positions", positions);  Check.defined("indices", indices);  Check.typeOf.number.greaterThanOrEquals("indices.length", indices.length, 3);  Check.typeOf.number.equals("indices.length % 3", "0", indices.length % 3, 0);  Check.typeOf.number.greaterThan("granularity", granularity, 0.0);  //>>includeEnd('debug');  // triangles that need (or might need) to be subdivided.  const triangles = indices.slice(0);  // New positions due to edge splits are appended to the positions list.  let i;  const length = positions.length;  const subdividedPositions = new Array(length * 3);  let q = 0;  for (i = 0; i < length; i++) {    const item = positions[i];    subdividedPositions[q++] = item.x;    subdividedPositions[q++] = item.y;    subdividedPositions[q++] = item.z;  }  const subdividedIndices = [];  // Used to make sure shared edges are not split more than once.  const edges = {};  const radius = ellipsoid.maximumRadius;  const minDistance = CesiumMath.chordLength(granularity, radius);  const minDistanceSqrd = minDistance * minDistance;  while (triangles.length > 0) {    const i2 = triangles.pop();    const i1 = triangles.pop();    const i0 = triangles.pop();    const v0 = Cartesian3.fromArray(      subdividedPositions,      i0 * 3,      subdivisionV0Scratch    );    const v1 = Cartesian3.fromArray(      subdividedPositions,      i1 * 3,      subdivisionV1Scratch    );    const v2 = Cartesian3.fromArray(      subdividedPositions,      i2 * 3,      subdivisionV2Scratch    );    const s0 = Cartesian3.multiplyByScalar(      Cartesian3.normalize(v0, subdivisionS0Scratch),      radius,      subdivisionS0Scratch    );    const s1 = Cartesian3.multiplyByScalar(      Cartesian3.normalize(v1, subdivisionS1Scratch),      radius,      subdivisionS1Scratch    );    const s2 = Cartesian3.multiplyByScalar(      Cartesian3.normalize(v2, subdivisionS2Scratch),      radius,      subdivisionS2Scratch    );    const g0 = Cartesian3.magnitudeSquared(      Cartesian3.subtract(s0, s1, subdivisionMidScratch)    );    const g1 = Cartesian3.magnitudeSquared(      Cartesian3.subtract(s1, s2, subdivisionMidScratch)    );    const g2 = Cartesian3.magnitudeSquared(      Cartesian3.subtract(s2, s0, subdivisionMidScratch)    );    const max = Math.max(g0, g1, g2);    let edge;    let mid;    // if the max length squared of a triangle edge is greater than the chord length of squared    // of the granularity, subdivide the triangle    if (max > minDistanceSqrd) {      if (g0 === max) {        edge = `${Math.min(i0, i1)} ${Math.max(i0, i1)}`;        i = edges[edge];        if (!defined(i)) {          mid = Cartesian3.add(v0, v1, subdivisionMidScratch);          Cartesian3.multiplyByScalar(mid, 0.5, mid);          subdividedPositions.push(mid.x, mid.y, mid.z);          i = subdividedPositions.length / 3 - 1;          edges[edge] = i;        }        triangles.push(i0, i, i2);        triangles.push(i, i1, i2);      } else if (g1 === max) {        edge = `${Math.min(i1, i2)} ${Math.max(i1, i2)}`;        i = edges[edge];        if (!defined(i)) {          mid = Cartesian3.add(v1, v2, subdivisionMidScratch);          Cartesian3.multiplyByScalar(mid, 0.5, mid);          subdividedPositions.push(mid.x, mid.y, mid.z);          i = subdividedPositions.length / 3 - 1;          edges[edge] = i;        }        triangles.push(i1, i, i0);        triangles.push(i, i2, i0);      } else if (g2 === max) {        edge = `${Math.min(i2, i0)} ${Math.max(i2, i0)}`;        i = edges[edge];        if (!defined(i)) {          mid = Cartesian3.add(v2, v0, subdivisionMidScratch);          Cartesian3.multiplyByScalar(mid, 0.5, mid);          subdividedPositions.push(mid.x, mid.y, mid.z);          i = subdividedPositions.length / 3 - 1;          edges[edge] = i;        }        triangles.push(i2, i, i1);        triangles.push(i, i0, i1);      }    } else {      subdividedIndices.push(i0);      subdividedIndices.push(i1);      subdividedIndices.push(i2);    }  }  return new Geometry({    attributes: {      position: new GeometryAttribute({        componentDatatype: ComponentDatatype.DOUBLE,        componentsPerAttribute: 3,        values: subdividedPositions,      }),    },    indices: subdividedIndices,    primitiveType: PrimitiveType.TRIANGLES,  });};const subdivisionC0Scratch = new Cartographic();const subdivisionC1Scratch = new Cartographic();const subdivisionC2Scratch = new Cartographic();const subdivisionCartographicScratch = new Cartographic();/** * Subdivides positions on rhumb lines and raises points to the surface of the ellipsoid. * * @param {Ellipsoid} ellipsoid The ellipsoid the polygon in on. * @param {Cartesian3[]} positions An array of {@link Cartesian3} positions of the polygon. * @param {Number[]} indices An array of indices that determines the triangles in the polygon. * @param {Number} [granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer. * * @exception {DeveloperError} At least three indices are required. * @exception {DeveloperError} The number of indices must be divisable by three. * @exception {DeveloperError} Granularity must be greater than zero. */PolygonPipeline.computeRhumbLineSubdivision = function (  ellipsoid,  positions,  indices,  granularity) {  granularity = defaultValue(granularity, CesiumMath.RADIANS_PER_DEGREE);  //>>includeStart('debug', pragmas.debug);  Check.typeOf.object("ellipsoid", ellipsoid);  Check.defined("positions", positions);  Check.defined("indices", indices);  Check.typeOf.number.greaterThanOrEquals("indices.length", indices.length, 3);  Check.typeOf.number.equals("indices.length % 3", "0", indices.length % 3, 0);  Check.typeOf.number.greaterThan("granularity", granularity, 0.0);  //>>includeEnd('debug');  // triangles that need (or might need) to be subdivided.  const triangles = indices.slice(0);  // New positions due to edge splits are appended to the positions list.  let i;  const length = positions.length;  const subdividedPositions = new Array(length * 3);  let q = 0;  for (i = 0; i < length; i++) {    const item = positions[i];    subdividedPositions[q++] = item.x;    subdividedPositions[q++] = item.y;    subdividedPositions[q++] = item.z;  }  const subdividedIndices = [];  // Used to make sure shared edges are not split more than once.  const edges = {};  const radius = ellipsoid.maximumRadius;  const minDistance = CesiumMath.chordLength(granularity, radius);  const rhumb0 = new EllipsoidRhumbLine(undefined, undefined, ellipsoid);  const rhumb1 = new EllipsoidRhumbLine(undefined, undefined, ellipsoid);  const rhumb2 = new EllipsoidRhumbLine(undefined, undefined, ellipsoid);  while (triangles.length > 0) {    const i2 = triangles.pop();    const i1 = triangles.pop();    const i0 = triangles.pop();    const v0 = Cartesian3.fromArray(      subdividedPositions,      i0 * 3,      subdivisionV0Scratch    );    const v1 = Cartesian3.fromArray(      subdividedPositions,      i1 * 3,      subdivisionV1Scratch    );    const v2 = Cartesian3.fromArray(      subdividedPositions,      i2 * 3,      subdivisionV2Scratch    );    const c0 = ellipsoid.cartesianToCartographic(v0, subdivisionC0Scratch);    const c1 = ellipsoid.cartesianToCartographic(v1, subdivisionC1Scratch);    const c2 = ellipsoid.cartesianToCartographic(v2, subdivisionC2Scratch);    rhumb0.setEndPoints(c0, c1);    const g0 = rhumb0.surfaceDistance;    rhumb1.setEndPoints(c1, c2);    const g1 = rhumb1.surfaceDistance;    rhumb2.setEndPoints(c2, c0);    const g2 = rhumb2.surfaceDistance;    const max = Math.max(g0, g1, g2);    let edge;    let mid;    let midHeight;    let midCartesian3;    // if the max length squared of a triangle edge is greater than granularity, subdivide the triangle    if (max > minDistance) {      if (g0 === max) {        edge = `${Math.min(i0, i1)} ${Math.max(i0, i1)}`;        i = edges[edge];        if (!defined(i)) {          mid = rhumb0.interpolateUsingFraction(            0.5,            subdivisionCartographicScratch          );          midHeight = (c0.height + c1.height) * 0.5;          midCartesian3 = Cartesian3.fromRadians(            mid.longitude,            mid.latitude,            midHeight,            ellipsoid,            subdivisionMidScratch          );          subdividedPositions.push(            midCartesian3.x,            midCartesian3.y,            midCartesian3.z          );          i = subdividedPositions.length / 3 - 1;          edges[edge] = i;        }        triangles.push(i0, i, i2);        triangles.push(i, i1, i2);      } else if (g1 === max) {        edge = `${Math.min(i1, i2)} ${Math.max(i1, i2)}`;        i = edges[edge];        if (!defined(i)) {          mid = rhumb1.interpolateUsingFraction(            0.5,            subdivisionCartographicScratch          );          midHeight = (c1.height + c2.height) * 0.5;          midCartesian3 = Cartesian3.fromRadians(            mid.longitude,            mid.latitude,            midHeight,            ellipsoid,            subdivisionMidScratch          );          subdividedPositions.push(            midCartesian3.x,            midCartesian3.y,            midCartesian3.z          );          i = subdividedPositions.length / 3 - 1;          edges[edge] = i;        }        triangles.push(i1, i, i0);        triangles.push(i, i2, i0);      } else if (g2 === max) {        edge = `${Math.min(i2, i0)} ${Math.max(i2, i0)}`;        i = edges[edge];        if (!defined(i)) {          mid = rhumb2.interpolateUsingFraction(            0.5,            subdivisionCartographicScratch          );          midHeight = (c2.height + c0.height) * 0.5;          midCartesian3 = Cartesian3.fromRadians(            mid.longitude,            mid.latitude,            midHeight,            ellipsoid,            subdivisionMidScratch          );          subdividedPositions.push(            midCartesian3.x,            midCartesian3.y,            midCartesian3.z          );          i = subdividedPositions.length / 3 - 1;          edges[edge] = i;        }        triangles.push(i2, i, i1);        triangles.push(i, i0, i1);      }    } else {      subdividedIndices.push(i0);      subdividedIndices.push(i1);      subdividedIndices.push(i2);    }  }  return new Geometry({    attributes: {      position: new GeometryAttribute({        componentDatatype: ComponentDatatype.DOUBLE,        componentsPerAttribute: 3,        values: subdividedPositions,      }),    },    indices: subdividedIndices,    primitiveType: PrimitiveType.TRIANGLES,  });};/** * Scales each position of a geometry's position attribute to a height, in place. * * @param {Number[]} positions The array of numbers representing the positions to be scaled * @param {Number} [height=0.0] The desired height to add to the positions * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the positions lie. * @param {Boolean} [scaleToSurface=true] <code>true</code> if the positions need to be scaled to the surface before the height is added. * @returns {Number[]} The input array of positions, scaled to height */PolygonPipeline.scaleToGeodeticHeight = function (  positions,  height,  ellipsoid,  scaleToSurface) {  ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);  let n = scaleToGeodeticHeightN;  let p = scaleToGeodeticHeightP;  height = defaultValue(height, 0.0);  scaleToSurface = defaultValue(scaleToSurface, true);  if (defined(positions)) {    const length = positions.length;    for (let i = 0; i < length; i += 3) {      Cartesian3.fromArray(positions, i, p);      if (scaleToSurface) {        p = ellipsoid.scaleToGeodeticSurface(p, p);      }      if (height !== 0) {        n = ellipsoid.geodeticSurfaceNormal(p, n);        Cartesian3.multiplyByScalar(n, height, n);        Cartesian3.add(p, n, p);      }      positions[i] = p.x;      positions[i + 1] = p.y;      positions[i + 2] = p.z;    }  }  return positions;};export default PolygonPipeline;
 |