| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 | import Cartesian3 from "./Cartesian3.js";import Cartographic from "./Cartographic.js";import defaultValue from "./defaultValue.js";import defined from "./defined.js";import DeveloperError from "./DeveloperError.js";import Ellipsoid from "./Ellipsoid.js";import EllipsoidGeodesic from "./EllipsoidGeodesic.js";import EllipsoidRhumbLine from "./EllipsoidRhumbLine.js";import IntersectionTests from "./IntersectionTests.js";import CesiumMath from "./Math.js";import Matrix4 from "./Matrix4.js";import Plane from "./Plane.js";/** * @private */const PolylinePipeline = {};PolylinePipeline.numberOfPoints = function (p0, p1, minDistance) {  const distance = Cartesian3.distance(p0, p1);  return Math.ceil(distance / minDistance);};PolylinePipeline.numberOfPointsRhumbLine = function (p0, p1, granularity) {  const radiansDistanceSquared =    Math.pow(p0.longitude - p1.longitude, 2) +    Math.pow(p0.latitude - p1.latitude, 2);  return Math.max(    1,    Math.ceil(Math.sqrt(radiansDistanceSquared / (granularity * granularity)))  );};const cartoScratch = new Cartographic();PolylinePipeline.extractHeights = function (positions, ellipsoid) {  const length = positions.length;  const heights = new Array(length);  for (let i = 0; i < length; i++) {    const p = positions[i];    heights[i] = ellipsoid.cartesianToCartographic(p, cartoScratch).height;  }  return heights;};const wrapLongitudeInversMatrix = new Matrix4();const wrapLongitudeOrigin = new Cartesian3();const wrapLongitudeXZNormal = new Cartesian3();const wrapLongitudeXZPlane = new Plane(Cartesian3.UNIT_X, 0.0);const wrapLongitudeYZNormal = new Cartesian3();const wrapLongitudeYZPlane = new Plane(Cartesian3.UNIT_X, 0.0);const wrapLongitudeIntersection = new Cartesian3();const wrapLongitudeOffset = new Cartesian3();const subdivideHeightsScratchArray = [];function subdivideHeights(numPoints, h0, h1) {  const heights = subdivideHeightsScratchArray;  heights.length = numPoints;  let i;  if (h0 === h1) {    for (i = 0; i < numPoints; i++) {      heights[i] = h0;    }    return heights;  }  const dHeight = h1 - h0;  const heightPerVertex = dHeight / numPoints;  for (i = 0; i < numPoints; i++) {    const h = h0 + i * heightPerVertex;    heights[i] = h;  }  return heights;}const carto1 = new Cartographic();const carto2 = new Cartographic();const cartesian = new Cartesian3();const scaleFirst = new Cartesian3();const scaleLast = new Cartesian3();const ellipsoidGeodesic = new EllipsoidGeodesic();let ellipsoidRhumb = new EllipsoidRhumbLine();//Returns subdivided line scaled to ellipsoid surface starting at p1 and ending at p2.//Result includes p1, but not include p2.  This function is called for a sequence of line segments,//and this prevents duplication of end point.function generateCartesianArc(  p0,  p1,  minDistance,  ellipsoid,  h0,  h1,  array,  offset) {  const first = ellipsoid.scaleToGeodeticSurface(p0, scaleFirst);  const last = ellipsoid.scaleToGeodeticSurface(p1, scaleLast);  const numPoints = PolylinePipeline.numberOfPoints(p0, p1, minDistance);  const start = ellipsoid.cartesianToCartographic(first, carto1);  const end = ellipsoid.cartesianToCartographic(last, carto2);  const heights = subdivideHeights(numPoints, h0, h1);  ellipsoidGeodesic.setEndPoints(start, end);  const surfaceDistanceBetweenPoints =    ellipsoidGeodesic.surfaceDistance / numPoints;  let index = offset;  start.height = h0;  let cart = ellipsoid.cartographicToCartesian(start, cartesian);  Cartesian3.pack(cart, array, index);  index += 3;  for (let i = 1; i < numPoints; i++) {    const carto = ellipsoidGeodesic.interpolateUsingSurfaceDistance(      i * surfaceDistanceBetweenPoints,      carto2    );    carto.height = heights[i];    cart = ellipsoid.cartographicToCartesian(carto, cartesian);    Cartesian3.pack(cart, array, index);    index += 3;  }  return index;}//Returns subdivided line scaled to ellipsoid surface starting at p1 and ending at p2.//Result includes p1, but not include p2.  This function is called for a sequence of line segments,//and this prevents duplication of end point.function generateCartesianRhumbArc(  p0,  p1,  granularity,  ellipsoid,  h0,  h1,  array,  offset) {  const start = ellipsoid.cartesianToCartographic(p0, carto1);  const end = ellipsoid.cartesianToCartographic(p1, carto2);  const numPoints = PolylinePipeline.numberOfPointsRhumbLine(    start,    end,    granularity  );  start.height = 0.0;  end.height = 0.0;  const heights = subdivideHeights(numPoints, h0, h1);  if (!ellipsoidRhumb.ellipsoid.equals(ellipsoid)) {    ellipsoidRhumb = new EllipsoidRhumbLine(undefined, undefined, ellipsoid);  }  ellipsoidRhumb.setEndPoints(start, end);  const surfaceDistanceBetweenPoints =    ellipsoidRhumb.surfaceDistance / numPoints;  let index = offset;  start.height = h0;  let cart = ellipsoid.cartographicToCartesian(start, cartesian);  Cartesian3.pack(cart, array, index);  index += 3;  for (let i = 1; i < numPoints; i++) {    const carto = ellipsoidRhumb.interpolateUsingSurfaceDistance(      i * surfaceDistanceBetweenPoints,      carto2    );    carto.height = heights[i];    cart = ellipsoid.cartographicToCartesian(carto, cartesian);    Cartesian3.pack(cart, array, index);    index += 3;  }  return index;}/** * Breaks a {@link Polyline} into segments such that it does not cross the ±180 degree meridian of an ellipsoid. * * @param {Cartesian3[]} positions The polyline's Cartesian positions. * @param {Matrix4} [modelMatrix=Matrix4.IDENTITY] The polyline's model matrix. Assumed to be an affine * transformation matrix, where the upper left 3x3 elements are a rotation matrix, and * the upper three elements in the fourth column are the translation.  The bottom row is assumed to be [0, 0, 0, 1]. * The matrix is not verified to be in the proper form. * @returns {Object} An object with a <code>positions</code> property that is an array of positions and a * <code>segments</code> property. * * * @example * const polylines = new Cesium.PolylineCollection(); * const polyline = polylines.add(...); * const positions = polyline.positions; * const modelMatrix = polylines.modelMatrix; * const segments = Cesium.PolylinePipeline.wrapLongitude(positions, modelMatrix); * * @see PolygonPipeline.wrapLongitude * @see Polyline * @see PolylineCollection */PolylinePipeline.wrapLongitude = function (positions, modelMatrix) {  const cartesians = [];  const segments = [];  if (defined(positions) && positions.length > 0) {    modelMatrix = defaultValue(modelMatrix, Matrix4.IDENTITY);    const inverseModelMatrix = Matrix4.inverseTransformation(      modelMatrix,      wrapLongitudeInversMatrix    );    const origin = Matrix4.multiplyByPoint(      inverseModelMatrix,      Cartesian3.ZERO,      wrapLongitudeOrigin    );    const xzNormal = Cartesian3.normalize(      Matrix4.multiplyByPointAsVector(        inverseModelMatrix,        Cartesian3.UNIT_Y,        wrapLongitudeXZNormal      ),      wrapLongitudeXZNormal    );    const xzPlane = Plane.fromPointNormal(      origin,      xzNormal,      wrapLongitudeXZPlane    );    const yzNormal = Cartesian3.normalize(      Matrix4.multiplyByPointAsVector(        inverseModelMatrix,        Cartesian3.UNIT_X,        wrapLongitudeYZNormal      ),      wrapLongitudeYZNormal    );    const yzPlane = Plane.fromPointNormal(      origin,      yzNormal,      wrapLongitudeYZPlane    );    let count = 1;    cartesians.push(Cartesian3.clone(positions[0]));    let prev = cartesians[0];    const length = positions.length;    for (let i = 1; i < length; ++i) {      const cur = positions[i];      // intersects the IDL if either endpoint is on the negative side of the yz-plane      if (        Plane.getPointDistance(yzPlane, prev) < 0.0 ||        Plane.getPointDistance(yzPlane, cur) < 0.0      ) {        // and intersects the xz-plane        const intersection = IntersectionTests.lineSegmentPlane(          prev,          cur,          xzPlane,          wrapLongitudeIntersection        );        if (defined(intersection)) {          // move point on the xz-plane slightly away from the plane          const offset = Cartesian3.multiplyByScalar(            xzNormal,            5.0e-9,            wrapLongitudeOffset          );          if (Plane.getPointDistance(xzPlane, prev) < 0.0) {            Cartesian3.negate(offset, offset);          }          cartesians.push(            Cartesian3.add(intersection, offset, new Cartesian3())          );          segments.push(count + 1);          Cartesian3.negate(offset, offset);          cartesians.push(            Cartesian3.add(intersection, offset, new Cartesian3())          );          count = 1;        }      }      cartesians.push(Cartesian3.clone(positions[i]));      count++;      prev = cur;    }    segments.push(count);  }  return {    positions: cartesians,    lengths: segments,  };};/** * Subdivides polyline and raises all points to the specified height.  Returns an array of numbers to represent the positions. * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions The array of type {Cartesian3} representing positions. * @param {Number|Number[]} [options.height=0.0] A number or array of numbers representing the heights of each position. * @param {Number} [options.granularity = CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the positions lie. * @returns {Number[]} A new array of positions of type {Number} that have been subdivided and raised to the surface of the ellipsoid. * * @example * const positions = Cesium.Cartesian3.fromDegreesArray([ *   -105.0, 40.0, *   -100.0, 38.0, *   -105.0, 35.0, *   -100.0, 32.0 * ]); * const surfacePositions = Cesium.PolylinePipeline.generateArc({ *   positons: positions * }); */PolylinePipeline.generateArc = function (options) {  if (!defined(options)) {    options = {};  }  const positions = options.positions;  //>>includeStart('debug', pragmas.debug);  if (!defined(positions)) {    throw new DeveloperError("options.positions is required.");  }  //>>includeEnd('debug');  const length = positions.length;  const ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);  let height = defaultValue(options.height, 0);  const hasHeightArray = Array.isArray(height);  if (length < 1) {    return [];  } else if (length === 1) {    const p = ellipsoid.scaleToGeodeticSurface(positions[0], scaleFirst);    height = hasHeightArray ? height[0] : height;    if (height !== 0) {      const n = ellipsoid.geodeticSurfaceNormal(p, cartesian);      Cartesian3.multiplyByScalar(n, height, n);      Cartesian3.add(p, n, p);    }    return [p.x, p.y, p.z];  }  let minDistance = options.minDistance;  if (!defined(minDistance)) {    const granularity = defaultValue(      options.granularity,      CesiumMath.RADIANS_PER_DEGREE    );    minDistance = CesiumMath.chordLength(granularity, ellipsoid.maximumRadius);  }  let numPoints = 0;  let i;  for (i = 0; i < length - 1; i++) {    numPoints += PolylinePipeline.numberOfPoints(      positions[i],      positions[i + 1],      minDistance    );  }  const arrayLength = (numPoints + 1) * 3;  const newPositions = new Array(arrayLength);  let offset = 0;  for (i = 0; i < length - 1; i++) {    const p0 = positions[i];    const p1 = positions[i + 1];    const h0 = hasHeightArray ? height[i] : height;    const h1 = hasHeightArray ? height[i + 1] : height;    offset = generateCartesianArc(      p0,      p1,      minDistance,      ellipsoid,      h0,      h1,      newPositions,      offset    );  }  subdivideHeightsScratchArray.length = 0;  const lastPoint = positions[length - 1];  const carto = ellipsoid.cartesianToCartographic(lastPoint, carto1);  carto.height = hasHeightArray ? height[length - 1] : height;  const cart = ellipsoid.cartographicToCartesian(carto, cartesian);  Cartesian3.pack(cart, newPositions, arrayLength - 3);  return newPositions;};const scratchCartographic0 = new Cartographic();const scratchCartographic1 = new Cartographic();/** * Subdivides polyline and raises all points to the specified height using Rhumb lines.  Returns an array of numbers to represent the positions. * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions The array of type {Cartesian3} representing positions. * @param {Number|Number[]} [options.height=0.0] A number or array of numbers representing the heights of each position. * @param {Number} [options.granularity = CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the positions lie. * @returns {Number[]} A new array of positions of type {Number} that have been subdivided and raised to the surface of the ellipsoid. * * @example * const positions = Cesium.Cartesian3.fromDegreesArray([ *   -105.0, 40.0, *   -100.0, 38.0, *   -105.0, 35.0, *   -100.0, 32.0 * ]); * const surfacePositions = Cesium.PolylinePipeline.generateRhumbArc({ *   positons: positions * }); */PolylinePipeline.generateRhumbArc = function (options) {  if (!defined(options)) {    options = {};  }  const positions = options.positions;  //>>includeStart('debug', pragmas.debug);  if (!defined(positions)) {    throw new DeveloperError("options.positions is required.");  }  //>>includeEnd('debug');  const length = positions.length;  const ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);  let height = defaultValue(options.height, 0);  const hasHeightArray = Array.isArray(height);  if (length < 1) {    return [];  } else if (length === 1) {    const p = ellipsoid.scaleToGeodeticSurface(positions[0], scaleFirst);    height = hasHeightArray ? height[0] : height;    if (height !== 0) {      const n = ellipsoid.geodeticSurfaceNormal(p, cartesian);      Cartesian3.multiplyByScalar(n, height, n);      Cartesian3.add(p, n, p);    }    return [p.x, p.y, p.z];  }  const granularity = defaultValue(    options.granularity,    CesiumMath.RADIANS_PER_DEGREE  );  let numPoints = 0;  let i;  let c0 = ellipsoid.cartesianToCartographic(    positions[0],    scratchCartographic0  );  let c1;  for (i = 0; i < length - 1; i++) {    c1 = ellipsoid.cartesianToCartographic(      positions[i + 1],      scratchCartographic1    );    numPoints += PolylinePipeline.numberOfPointsRhumbLine(c0, c1, granularity);    c0 = Cartographic.clone(c1, scratchCartographic0);  }  const arrayLength = (numPoints + 1) * 3;  const newPositions = new Array(arrayLength);  let offset = 0;  for (i = 0; i < length - 1; i++) {    const p0 = positions[i];    const p1 = positions[i + 1];    const h0 = hasHeightArray ? height[i] : height;    const h1 = hasHeightArray ? height[i + 1] : height;    offset = generateCartesianRhumbArc(      p0,      p1,      granularity,      ellipsoid,      h0,      h1,      newPositions,      offset    );  }  subdivideHeightsScratchArray.length = 0;  const lastPoint = positions[length - 1];  const carto = ellipsoid.cartesianToCartographic(lastPoint, carto1);  carto.height = hasHeightArray ? height[length - 1] : height;  const cart = ellipsoid.cartographicToCartesian(carto, cartesian);  Cartesian3.pack(cart, newPositions, arrayLength - 3);  return newPositions;};/** * Subdivides polyline and raises all points to the specified height. Returns an array of new {Cartesian3} positions. * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions The array of type {Cartesian3} representing positions. * @param {Number|Number[]} [options.height=0.0] A number or array of numbers representing the heights of each position. * @param {Number} [options.granularity = CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the positions lie. * @returns {Cartesian3[]} A new array of cartesian3 positions that have been subdivided and raised to the surface of the ellipsoid. * * @example * const positions = Cesium.Cartesian3.fromDegreesArray([ *   -105.0, 40.0, *   -100.0, 38.0, *   -105.0, 35.0, *   -100.0, 32.0 * ]); * const surfacePositions = Cesium.PolylinePipeline.generateCartesianArc({ *   positons: positions * }); */PolylinePipeline.generateCartesianArc = function (options) {  const numberArray = PolylinePipeline.generateArc(options);  const size = numberArray.length / 3;  const newPositions = new Array(size);  for (let i = 0; i < size; i++) {    newPositions[i] = Cartesian3.unpack(numberArray, i * 3);  }  return newPositions;};/** * Subdivides polyline and raises all points to the specified height using Rhumb Lines. Returns an array of new {Cartesian3} positions. * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions The array of type {Cartesian3} representing positions. * @param {Number|Number[]} [options.height=0.0] A number or array of numbers representing the heights of each position. * @param {Number} [options.granularity = CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the positions lie. * @returns {Cartesian3[]} A new array of cartesian3 positions that have been subdivided and raised to the surface of the ellipsoid. * * @example * const positions = Cesium.Cartesian3.fromDegreesArray([ *   -105.0, 40.0, *   -100.0, 38.0, *   -105.0, 35.0, *   -100.0, 32.0 * ]); * const surfacePositions = Cesium.PolylinePipeline.generateCartesianRhumbArc({ *   positons: positions * }); */PolylinePipeline.generateCartesianRhumbArc = function (options) {  const numberArray = PolylinePipeline.generateRhumbArc(options);  const size = numberArray.length / 3;  const newPositions = new Array(size);  for (let i = 0; i < size; i++) {    newPositions[i] = Cartesian3.unpack(numberArray, i * 3);  }  return newPositions;};export default PolylinePipeline;
 |