scaleToGeodeticSurface.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import Cartesian3 from "./Cartesian3.js";
  2. import defined from "./defined.js";
  3. import DeveloperError from "./DeveloperError.js";
  4. import CesiumMath from "./Math.js";
  5. const scaleToGeodeticSurfaceIntersection = new Cartesian3();
  6. const scaleToGeodeticSurfaceGradient = new Cartesian3();
  7. /**
  8. * Scales the provided Cartesian position along the geodetic surface normal
  9. * so that it is on the surface of this ellipsoid. If the position is
  10. * at the center of the ellipsoid, this function returns undefined.
  11. *
  12. * @param {Cartesian3} cartesian The Cartesian position to scale.
  13. * @param {Cartesian3} oneOverRadii One over radii of the ellipsoid.
  14. * @param {Cartesian3} oneOverRadiiSquared One over radii squared of the ellipsoid.
  15. * @param {number} centerToleranceSquared Tolerance for closeness to the center.
  16. * @param {Cartesian3} [result] The object onto which to store the result.
  17. * @returns {Cartesian3} The modified result parameter, a new Cartesian3 instance if none was provided, or undefined if the position is at the center.
  18. *
  19. * @function scaleToGeodeticSurface
  20. *
  21. * @private
  22. */
  23. function scaleToGeodeticSurface(
  24. cartesian,
  25. oneOverRadii,
  26. oneOverRadiiSquared,
  27. centerToleranceSquared,
  28. result
  29. ) {
  30. //>>includeStart('debug', pragmas.debug);
  31. if (!defined(cartesian)) {
  32. throw new DeveloperError("cartesian is required.");
  33. }
  34. if (!defined(oneOverRadii)) {
  35. throw new DeveloperError("oneOverRadii is required.");
  36. }
  37. if (!defined(oneOverRadiiSquared)) {
  38. throw new DeveloperError("oneOverRadiiSquared is required.");
  39. }
  40. if (!defined(centerToleranceSquared)) {
  41. throw new DeveloperError("centerToleranceSquared is required.");
  42. }
  43. //>>includeEnd('debug');
  44. const positionX = cartesian.x;
  45. const positionY = cartesian.y;
  46. const positionZ = cartesian.z;
  47. const oneOverRadiiX = oneOverRadii.x;
  48. const oneOverRadiiY = oneOverRadii.y;
  49. const oneOverRadiiZ = oneOverRadii.z;
  50. const x2 = positionX * positionX * oneOverRadiiX * oneOverRadiiX;
  51. const y2 = positionY * positionY * oneOverRadiiY * oneOverRadiiY;
  52. const z2 = positionZ * positionZ * oneOverRadiiZ * oneOverRadiiZ;
  53. // Compute the squared ellipsoid norm.
  54. const squaredNorm = x2 + y2 + z2;
  55. const ratio = Math.sqrt(1.0 / squaredNorm);
  56. // As an initial approximation, assume that the radial intersection is the projection point.
  57. const intersection = Cartesian3.multiplyByScalar(
  58. cartesian,
  59. ratio,
  60. scaleToGeodeticSurfaceIntersection
  61. );
  62. // If the position is near the center, the iteration will not converge.
  63. if (squaredNorm < centerToleranceSquared) {
  64. return !isFinite(ratio)
  65. ? undefined
  66. : Cartesian3.clone(intersection, result);
  67. }
  68. const oneOverRadiiSquaredX = oneOverRadiiSquared.x;
  69. const oneOverRadiiSquaredY = oneOverRadiiSquared.y;
  70. const oneOverRadiiSquaredZ = oneOverRadiiSquared.z;
  71. // Use the gradient at the intersection point in place of the true unit normal.
  72. // The difference in magnitude will be absorbed in the multiplier.
  73. const gradient = scaleToGeodeticSurfaceGradient;
  74. gradient.x = intersection.x * oneOverRadiiSquaredX * 2.0;
  75. gradient.y = intersection.y * oneOverRadiiSquaredY * 2.0;
  76. gradient.z = intersection.z * oneOverRadiiSquaredZ * 2.0;
  77. // Compute the initial guess at the normal vector multiplier, lambda.
  78. let lambda =
  79. ((1.0 - ratio) * Cartesian3.magnitude(cartesian)) /
  80. (0.5 * Cartesian3.magnitude(gradient));
  81. let correction = 0.0;
  82. let func;
  83. let denominator;
  84. let xMultiplier;
  85. let yMultiplier;
  86. let zMultiplier;
  87. let xMultiplier2;
  88. let yMultiplier2;
  89. let zMultiplier2;
  90. let xMultiplier3;
  91. let yMultiplier3;
  92. let zMultiplier3;
  93. do {
  94. lambda -= correction;
  95. xMultiplier = 1.0 / (1.0 + lambda * oneOverRadiiSquaredX);
  96. yMultiplier = 1.0 / (1.0 + lambda * oneOverRadiiSquaredY);
  97. zMultiplier = 1.0 / (1.0 + lambda * oneOverRadiiSquaredZ);
  98. xMultiplier2 = xMultiplier * xMultiplier;
  99. yMultiplier2 = yMultiplier * yMultiplier;
  100. zMultiplier2 = zMultiplier * zMultiplier;
  101. xMultiplier3 = xMultiplier2 * xMultiplier;
  102. yMultiplier3 = yMultiplier2 * yMultiplier;
  103. zMultiplier3 = zMultiplier2 * zMultiplier;
  104. func = x2 * xMultiplier2 + y2 * yMultiplier2 + z2 * zMultiplier2 - 1.0;
  105. // "denominator" here refers to the use of this expression in the velocity and acceleration
  106. // computations in the sections to follow.
  107. denominator =
  108. x2 * xMultiplier3 * oneOverRadiiSquaredX +
  109. y2 * yMultiplier3 * oneOverRadiiSquaredY +
  110. z2 * zMultiplier3 * oneOverRadiiSquaredZ;
  111. const derivative = -2.0 * denominator;
  112. correction = func / derivative;
  113. } while (Math.abs(func) > CesiumMath.EPSILON12);
  114. if (!defined(result)) {
  115. return new Cartesian3(
  116. positionX * xMultiplier,
  117. positionY * yMultiplier,
  118. positionZ * zMultiplier
  119. );
  120. }
  121. result.x = positionX * xMultiplier;
  122. result.y = positionY * yMultiplier;
  123. result.z = positionZ * zMultiplier;
  124. return result;
  125. }
  126. export default scaleToGeodeticSurface;