| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730 | import BoundingSphere from "./BoundingSphere.js";import Cartesian3 from "./Cartesian3.js";import defaultValue from "./defaultValue.js";import defined from "./defined.js";import DeveloperError from "./DeveloperError.js";import Ellipsoid from "./Ellipsoid.js";import CesiumMath from "./Math.js";import Rectangle from "./Rectangle.js";import Visibility from "./Visibility.js";/** * Creates an Occluder derived from an object's position and radius, as well as the camera position. * The occluder can be used to determine whether or not other objects are visible or hidden behind the * visible horizon defined by the occluder and camera position. * * @alias Occluder * * @param {BoundingSphere} occluderBoundingSphere The bounding sphere surrounding the occluder. * @param {Cartesian3} cameraPosition The coordinate of the viewer/camera. * * @constructor * * @example * // Construct an occluder one unit away from the origin with a radius of one. * const cameraPosition = Cesium.Cartesian3.ZERO; * const occluderBoundingSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -1), 1); * const occluder = new Cesium.Occluder(occluderBoundingSphere, cameraPosition); */function Occluder(occluderBoundingSphere, cameraPosition) {  //>>includeStart('debug', pragmas.debug);  if (!defined(occluderBoundingSphere)) {    throw new DeveloperError("occluderBoundingSphere is required.");  }  if (!defined(cameraPosition)) {    throw new DeveloperError("camera position is required.");  }  //>>includeEnd('debug');  this._occluderPosition = Cartesian3.clone(occluderBoundingSphere.center);  this._occluderRadius = occluderBoundingSphere.radius;  this._horizonDistance = 0.0;  this._horizonPlaneNormal = undefined;  this._horizonPlanePosition = undefined;  this._cameraPosition = undefined;  // cameraPosition fills in the above values  this.cameraPosition = cameraPosition;}const scratchCartesian3 = new Cartesian3();Object.defineProperties(Occluder.prototype, {  /**   * The position of the occluder.   * @memberof Occluder.prototype   * @type {Cartesian3}   */  position: {    get: function () {      return this._occluderPosition;    },  },  /**   * The radius of the occluder.   * @memberof Occluder.prototype   * @type {Number}   */  radius: {    get: function () {      return this._occluderRadius;    },  },  /**   * The position of the camera.   * @memberof Occluder.prototype   * @type {Cartesian3}   */  cameraPosition: {    set: function (cameraPosition) {      //>>includeStart('debug', pragmas.debug);      if (!defined(cameraPosition)) {        throw new DeveloperError("cameraPosition is required.");      }      //>>includeEnd('debug');      cameraPosition = Cartesian3.clone(cameraPosition, this._cameraPosition);      const cameraToOccluderVec = Cartesian3.subtract(        this._occluderPosition,        cameraPosition,        scratchCartesian3      );      let invCameraToOccluderDistance = Cartesian3.magnitudeSquared(        cameraToOccluderVec      );      const occluderRadiusSqrd = this._occluderRadius * this._occluderRadius;      let horizonDistance;      let horizonPlaneNormal;      let horizonPlanePosition;      if (invCameraToOccluderDistance > occluderRadiusSqrd) {        horizonDistance = Math.sqrt(          invCameraToOccluderDistance - occluderRadiusSqrd        );        invCameraToOccluderDistance =          1.0 / Math.sqrt(invCameraToOccluderDistance);        horizonPlaneNormal = Cartesian3.multiplyByScalar(          cameraToOccluderVec,          invCameraToOccluderDistance,          scratchCartesian3        );        const nearPlaneDistance =          horizonDistance * horizonDistance * invCameraToOccluderDistance;        horizonPlanePosition = Cartesian3.add(          cameraPosition,          Cartesian3.multiplyByScalar(            horizonPlaneNormal,            nearPlaneDistance,            scratchCartesian3          ),          scratchCartesian3        );      } else {        horizonDistance = Number.MAX_VALUE;      }      this._horizonDistance = horizonDistance;      this._horizonPlaneNormal = horizonPlaneNormal;      this._horizonPlanePosition = horizonPlanePosition;      this._cameraPosition = cameraPosition;    },  },});/** * Creates an occluder from a bounding sphere and the camera position. * * @param {BoundingSphere} occluderBoundingSphere The bounding sphere surrounding the occluder. * @param {Cartesian3} cameraPosition The coordinate of the viewer/camera. * @param {Occluder} [result] The object onto which to store the result. * @returns {Occluder} The occluder derived from an object's position and radius, as well as the camera position. */Occluder.fromBoundingSphere = function (  occluderBoundingSphere,  cameraPosition,  result) {  //>>includeStart('debug', pragmas.debug);  if (!defined(occluderBoundingSphere)) {    throw new DeveloperError("occluderBoundingSphere is required.");  }  if (!defined(cameraPosition)) {    throw new DeveloperError("camera position is required.");  }  //>>includeEnd('debug');  if (!defined(result)) {    return new Occluder(occluderBoundingSphere, cameraPosition);  }  Cartesian3.clone(occluderBoundingSphere.center, result._occluderPosition);  result._occluderRadius = occluderBoundingSphere.radius;  result.cameraPosition = cameraPosition;  return result;};const tempVecScratch = new Cartesian3();/** * Determines whether or not a point, the <code>occludee</code>, is hidden from view by the occluder. * * @param {Cartesian3} occludee The point surrounding the occludee object. * @returns {Boolean} <code>true</code> if the occludee is visible; otherwise <code>false</code>. * * * @example * const cameraPosition = new Cesium.Cartesian3(0, 0, 0); * const littleSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -1), 0.25); * const occluder = new Cesium.Occluder(littleSphere, cameraPosition); * const point = new Cesium.Cartesian3(0, 0, -3); * occluder.isPointVisible(point); //returns true * * @see Occluder#computeVisibility */Occluder.prototype.isPointVisible = function (occludee) {  if (this._horizonDistance !== Number.MAX_VALUE) {    let tempVec = Cartesian3.subtract(      occludee,      this._occluderPosition,      tempVecScratch    );    let temp = this._occluderRadius;    temp = Cartesian3.magnitudeSquared(tempVec) - temp * temp;    if (temp > 0.0) {      temp = Math.sqrt(temp) + this._horizonDistance;      tempVec = Cartesian3.subtract(occludee, this._cameraPosition, tempVec);      return temp * temp > Cartesian3.magnitudeSquared(tempVec);    }  }  return false;};const occludeePositionScratch = new Cartesian3();/** * Determines whether or not a sphere, the <code>occludee</code>, is hidden from view by the occluder. * * @param {BoundingSphere} occludee The bounding sphere surrounding the occludee object. * @returns {Boolean} <code>true</code> if the occludee is visible; otherwise <code>false</code>. * * * @example * const cameraPosition = new Cesium.Cartesian3(0, 0, 0); * const littleSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -1), 0.25); * const occluder = new Cesium.Occluder(littleSphere, cameraPosition); * const bigSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -3), 1); * occluder.isBoundingSphereVisible(bigSphere); //returns true * * @see Occluder#computeVisibility */Occluder.prototype.isBoundingSphereVisible = function (occludee) {  const occludeePosition = Cartesian3.clone(    occludee.center,    occludeePositionScratch  );  const occludeeRadius = occludee.radius;  if (this._horizonDistance !== Number.MAX_VALUE) {    let tempVec = Cartesian3.subtract(      occludeePosition,      this._occluderPosition,      tempVecScratch    );    let temp = this._occluderRadius - occludeeRadius;    temp = Cartesian3.magnitudeSquared(tempVec) - temp * temp;    if (occludeeRadius < this._occluderRadius) {      if (temp > 0.0) {        temp = Math.sqrt(temp) + this._horizonDistance;        tempVec = Cartesian3.subtract(          occludeePosition,          this._cameraPosition,          tempVec        );        return (          temp * temp + occludeeRadius * occludeeRadius >          Cartesian3.magnitudeSquared(tempVec)        );      }      return false;    }    // Prevent against the case where the occludee radius is larger than the occluder's; since this is    // an uncommon case, the following code should rarely execute.    if (temp > 0.0) {      tempVec = Cartesian3.subtract(        occludeePosition,        this._cameraPosition,        tempVec      );      const tempVecMagnitudeSquared = Cartesian3.magnitudeSquared(tempVec);      const occluderRadiusSquared = this._occluderRadius * this._occluderRadius;      const occludeeRadiusSquared = occludeeRadius * occludeeRadius;      if (        (this._horizonDistance * this._horizonDistance +          occluderRadiusSquared) *          occludeeRadiusSquared >        tempVecMagnitudeSquared * occluderRadiusSquared      ) {        // The occludee is close enough that the occluder cannot possible occlude the occludee        return true;      }      temp = Math.sqrt(temp) + this._horizonDistance;      return temp * temp + occludeeRadiusSquared > tempVecMagnitudeSquared;    }    // The occludee completely encompasses the occluder    return true;  }  return false;};const tempScratch = new Cartesian3();/** * Determine to what extent an occludee is visible (not visible, partially visible,  or fully visible). * * @param {BoundingSphere} occludeeBS The bounding sphere of the occludee. * @returns {Visibility} Visibility.NONE if the occludee is not visible, *                       Visibility.PARTIAL if the occludee is partially visible, or *                       Visibility.FULL if the occludee is fully visible. * * * @example * const sphere1 = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -1.5), 0.5); * const sphere2 = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -2.5), 0.5); * const cameraPosition = new Cesium.Cartesian3(0, 0, 0); * const occluder = new Cesium.Occluder(sphere1, cameraPosition); * occluder.computeVisibility(sphere2); //returns Visibility.NONE * * @see Occluder#isVisible */Occluder.prototype.computeVisibility = function (occludeeBS) {  //>>includeStart('debug', pragmas.debug);  if (!defined(occludeeBS)) {    throw new DeveloperError("occludeeBS is required.");  }  //>>includeEnd('debug');  // If the occludee radius is larger than the occluders, this will return that  // the entire ocludee is visible, even though that may not be the case, though this should  // not occur too often.  const occludeePosition = Cartesian3.clone(occludeeBS.center);  const occludeeRadius = occludeeBS.radius;  if (occludeeRadius > this._occluderRadius) {    return Visibility.FULL;  }  if (this._horizonDistance !== Number.MAX_VALUE) {    // The camera is outside the occluder    let tempVec = Cartesian3.subtract(      occludeePosition,      this._occluderPosition,      tempScratch    );    let temp = this._occluderRadius - occludeeRadius;    const occluderToOccludeeDistSqrd = Cartesian3.magnitudeSquared(tempVec);    temp = occluderToOccludeeDistSqrd - temp * temp;    if (temp > 0.0) {      // The occludee is not completely inside the occluder      // Check to see if the occluder completely hides the occludee      temp = Math.sqrt(temp) + this._horizonDistance;      tempVec = Cartesian3.subtract(        occludeePosition,        this._cameraPosition,        tempVec      );      const cameraToOccludeeDistSqrd = Cartesian3.magnitudeSquared(tempVec);      if (        temp * temp + occludeeRadius * occludeeRadius <        cameraToOccludeeDistSqrd      ) {        return Visibility.NONE;      }      // Check to see whether the occluder is fully or partially visible      // when the occludee does not intersect the occluder      temp = this._occluderRadius + occludeeRadius;      temp = occluderToOccludeeDistSqrd - temp * temp;      if (temp > 0.0) {        // The occludee does not intersect the occluder.        temp = Math.sqrt(temp) + this._horizonDistance;        return cameraToOccludeeDistSqrd <          temp * temp + occludeeRadius * occludeeRadius          ? Visibility.FULL          : Visibility.PARTIAL;      }      //Check to see if the occluder is fully or partially visible when the occludee DOES      //intersect the occluder      tempVec = Cartesian3.subtract(        occludeePosition,        this._horizonPlanePosition,        tempVec      );      return Cartesian3.dot(tempVec, this._horizonPlaneNormal) > -occludeeRadius        ? Visibility.PARTIAL        : Visibility.FULL;    }  }  return Visibility.NONE;};const occludeePointScratch = new Cartesian3();/** * Computes a point that can be used as the occludee position to the visibility functions. * Use a radius of zero for the occludee radius.  Typically, a user computes a bounding sphere around * an object that is used for visibility; however it is also possible to compute a point that if * seen/not seen would also indicate if an object is visible/not visible.  This function is better * called for objects that do not move relative to the occluder and is large, such as a chunk of * terrain.  You are better off not calling this and using the object's bounding sphere for objects * such as a satellite or ground vehicle. * * @param {BoundingSphere} occluderBoundingSphere The bounding sphere surrounding the occluder. * @param {Cartesian3} occludeePosition The point where the occludee (bounding sphere of radius 0) is located. * @param {Cartesian3[]} positions List of altitude points on the horizon near the surface of the occluder. * @returns {Object} An object containing two attributes: <code>occludeePoint</code> and <code>valid</code> * which is a boolean value. * * @exception {DeveloperError} <code>positions</code> must contain at least one element. * @exception {DeveloperError} <code>occludeePosition</code> must have a value other than <code>occluderBoundingSphere.center</code>. * * @example * const cameraPosition = new Cesium.Cartesian3(0, 0, 0); * const occluderBoundingSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -8), 2); * const occluder = new Cesium.Occluder(occluderBoundingSphere, cameraPosition); * const positions = [new Cesium.Cartesian3(-0.25, 0, -5.3), new Cesium.Cartesian3(0.25, 0, -5.3)]; * const tileOccluderSphere = Cesium.BoundingSphere.fromPoints(positions); * const occludeePosition = tileOccluderSphere.center; * const occludeePt = Cesium.Occluder.computeOccludeePoint(occluderBoundingSphere, occludeePosition, positions); */Occluder.computeOccludeePoint = function (  occluderBoundingSphere,  occludeePosition,  positions) {  //>>includeStart('debug', pragmas.debug);  if (!defined(occluderBoundingSphere)) {    throw new DeveloperError("occluderBoundingSphere is required.");  }  if (!defined(positions)) {    throw new DeveloperError("positions is required.");  }  if (positions.length === 0) {    throw new DeveloperError("positions must contain at least one element");  }  //>>includeEnd('debug');  const occludeePos = Cartesian3.clone(occludeePosition);  const occluderPosition = Cartesian3.clone(occluderBoundingSphere.center);  const occluderRadius = occluderBoundingSphere.radius;  const numPositions = positions.length;  //>>includeStart('debug', pragmas.debug);  if (Cartesian3.equals(occluderPosition, occludeePosition)) {    throw new DeveloperError(      "occludeePosition must be different than occluderBoundingSphere.center"    );  }  //>>includeEnd('debug');  // Compute a plane with a normal from the occluder to the occludee position.  const occluderPlaneNormal = Cartesian3.normalize(    Cartesian3.subtract(occludeePos, occluderPosition, occludeePointScratch),    occludeePointScratch  );  const occluderPlaneD = -Cartesian3.dot(occluderPlaneNormal, occluderPosition);  //For each position, determine the horizon intersection. Choose the position and intersection  //that results in the greatest angle with the occcluder plane.  const aRotationVector = Occluder._anyRotationVector(    occluderPosition,    occluderPlaneNormal,    occluderPlaneD  );  let dot = Occluder._horizonToPlaneNormalDotProduct(    occluderBoundingSphere,    occluderPlaneNormal,    occluderPlaneD,    aRotationVector,    positions[0]  );  if (!dot) {    //The position is inside the mimimum radius, which is invalid    return undefined;  }  let tempDot;  for (let i = 1; i < numPositions; ++i) {    tempDot = Occluder._horizonToPlaneNormalDotProduct(      occluderBoundingSphere,      occluderPlaneNormal,      occluderPlaneD,      aRotationVector,      positions[i]    );    if (!tempDot) {      //The position is inside the minimum radius, which is invalid      return undefined;    }    if (tempDot < dot) {      dot = tempDot;    }  }  //Verify that the dot is not near 90 degress  if (dot < 0.00174532836589830883577820272085) {    return undefined;  }  const distance = occluderRadius / dot;  return Cartesian3.add(    occluderPosition,    Cartesian3.multiplyByScalar(      occluderPlaneNormal,      distance,      occludeePointScratch    ),    occludeePointScratch  );};const computeOccludeePointFromRectangleScratch = [];/** * Computes a point that can be used as the occludee position to the visibility functions from a rectangle. * * @param {Rectangle} rectangle The rectangle used to create a bounding sphere. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid used to determine positions of the rectangle. * @returns {Object} An object containing two attributes: <code>occludeePoint</code> and <code>valid</code> * which is a boolean value. */Occluder.computeOccludeePointFromRectangle = function (rectangle, ellipsoid) {  //>>includeStart('debug', pragmas.debug);  if (!defined(rectangle)) {    throw new DeveloperError("rectangle is required.");  }  //>>includeEnd('debug');  ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);  const positions = Rectangle.subsample(    rectangle,    ellipsoid,    0.0,    computeOccludeePointFromRectangleScratch  );  const bs = BoundingSphere.fromPoints(positions);  // TODO: get correct ellipsoid center  const ellipsoidCenter = Cartesian3.ZERO;  if (!Cartesian3.equals(ellipsoidCenter, bs.center)) {    return Occluder.computeOccludeePoint(      new BoundingSphere(ellipsoidCenter, ellipsoid.minimumRadius),      bs.center,      positions    );  }  return undefined;};const tempVec0Scratch = new Cartesian3();Occluder._anyRotationVector = function (  occluderPosition,  occluderPlaneNormal,  occluderPlaneD) {  const tempVec0 = Cartesian3.abs(occluderPlaneNormal, tempVec0Scratch);  let majorAxis = tempVec0.x > tempVec0.y ? 0 : 1;  if (    (majorAxis === 0 && tempVec0.z > tempVec0.x) ||    (majorAxis === 1 && tempVec0.z > tempVec0.y)  ) {    majorAxis = 2;  }  const tempVec = new Cartesian3();  let tempVec1;  if (majorAxis === 0) {    tempVec0.x = occluderPosition.x;    tempVec0.y = occluderPosition.y + 1.0;    tempVec0.z = occluderPosition.z + 1.0;    tempVec1 = Cartesian3.UNIT_X;  } else if (majorAxis === 1) {    tempVec0.x = occluderPosition.x + 1.0;    tempVec0.y = occluderPosition.y;    tempVec0.z = occluderPosition.z + 1.0;    tempVec1 = Cartesian3.UNIT_Y;  } else {    tempVec0.x = occluderPosition.x + 1.0;    tempVec0.y = occluderPosition.y + 1.0;    tempVec0.z = occluderPosition.z;    tempVec1 = Cartesian3.UNIT_Z;  }  const u =    (Cartesian3.dot(occluderPlaneNormal, tempVec0) + occluderPlaneD) /    -Cartesian3.dot(occluderPlaneNormal, tempVec1);  return Cartesian3.normalize(    Cartesian3.subtract(      Cartesian3.add(        tempVec0,        Cartesian3.multiplyByScalar(tempVec1, u, tempVec),        tempVec0      ),      occluderPosition,      tempVec0    ),    tempVec0  );};const posDirectionScratch = new Cartesian3();Occluder._rotationVector = function (  occluderPosition,  occluderPlaneNormal,  occluderPlaneD,  position,  anyRotationVector) {  //Determine the angle between the occluder plane normal and the position direction  let positionDirection = Cartesian3.subtract(    position,    occluderPosition,    posDirectionScratch  );  positionDirection = Cartesian3.normalize(    positionDirection,    positionDirection  );  if (    Cartesian3.dot(occluderPlaneNormal, positionDirection) <    0.99999998476912904932780850903444  ) {    const crossProduct = Cartesian3.cross(      occluderPlaneNormal,      positionDirection,      positionDirection    );    const length = Cartesian3.magnitude(crossProduct);    if (length > CesiumMath.EPSILON13) {      return Cartesian3.normalize(crossProduct, new Cartesian3());    }  }  //The occluder plane normal and the position direction are colinear. Use any  //vector in the occluder plane as the rotation vector  return anyRotationVector;};const posScratch1 = new Cartesian3();const occluerPosScratch = new Cartesian3();const posScratch2 = new Cartesian3();const horizonPlanePosScratch = new Cartesian3();Occluder._horizonToPlaneNormalDotProduct = function (  occluderBS,  occluderPlaneNormal,  occluderPlaneD,  anyRotationVector,  position) {  const pos = Cartesian3.clone(position, posScratch1);  const occluderPosition = Cartesian3.clone(    occluderBS.center,    occluerPosScratch  );  const occluderRadius = occluderBS.radius;  //Verify that the position is outside the occluder  let positionToOccluder = Cartesian3.subtract(    occluderPosition,    pos,    posScratch2  );  const occluderToPositionDistanceSquared = Cartesian3.magnitudeSquared(    positionToOccluder  );  const occluderRadiusSquared = occluderRadius * occluderRadius;  if (occluderToPositionDistanceSquared < occluderRadiusSquared) {    return false;  }  //Horizon parameters  const horizonDistanceSquared =    occluderToPositionDistanceSquared - occluderRadiusSquared;  const horizonDistance = Math.sqrt(horizonDistanceSquared);  const occluderToPositionDistance = Math.sqrt(    occluderToPositionDistanceSquared  );  const invOccluderToPositionDistance = 1.0 / occluderToPositionDistance;  const cosTheta = horizonDistance * invOccluderToPositionDistance;  const horizonPlaneDistance = cosTheta * horizonDistance;  positionToOccluder = Cartesian3.normalize(    positionToOccluder,    positionToOccluder  );  const horizonPlanePosition = Cartesian3.add(    pos,    Cartesian3.multiplyByScalar(      positionToOccluder,      horizonPlaneDistance,      horizonPlanePosScratch    ),    horizonPlanePosScratch  );  const horizonCrossDistance = Math.sqrt(    horizonDistanceSquared - horizonPlaneDistance * horizonPlaneDistance  );  //Rotate the position to occluder vector 90 degrees  let tempVec = this._rotationVector(    occluderPosition,    occluderPlaneNormal,    occluderPlaneD,    pos,    anyRotationVector  );  let horizonCrossDirection = Cartesian3.fromElements(    tempVec.x * tempVec.x * positionToOccluder.x +      (tempVec.x * tempVec.y - tempVec.z) * positionToOccluder.y +      (tempVec.x * tempVec.z + tempVec.y) * positionToOccluder.z,    (tempVec.x * tempVec.y + tempVec.z) * positionToOccluder.x +      tempVec.y * tempVec.y * positionToOccluder.y +      (tempVec.y * tempVec.z - tempVec.x) * positionToOccluder.z,    (tempVec.x * tempVec.z - tempVec.y) * positionToOccluder.x +      (tempVec.y * tempVec.z + tempVec.x) * positionToOccluder.y +      tempVec.z * tempVec.z * positionToOccluder.z,    posScratch1  );  horizonCrossDirection = Cartesian3.normalize(    horizonCrossDirection,    horizonCrossDirection  );  //Horizon positions  const offset = Cartesian3.multiplyByScalar(    horizonCrossDirection,    horizonCrossDistance,    posScratch1  );  tempVec = Cartesian3.normalize(    Cartesian3.subtract(      Cartesian3.add(horizonPlanePosition, offset, posScratch2),      occluderPosition,      posScratch2    ),    posScratch2  );  const dot0 = Cartesian3.dot(occluderPlaneNormal, tempVec);  tempVec = Cartesian3.normalize(    Cartesian3.subtract(      Cartesian3.subtract(horizonPlanePosition, offset, tempVec),      occluderPosition,      tempVec    ),    tempVec  );  const dot1 = Cartesian3.dot(occluderPlaneNormal, tempVec);  return dot0 < dot1 ? dot0 : dot1;};export default Occluder;
 |