EllipsoidTangentPlane.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. import AxisAlignedBoundingBox from "./AxisAlignedBoundingBox.js";
  2. import Cartesian2 from "./Cartesian2.js";
  3. import Cartesian3 from "./Cartesian3.js";
  4. import Cartesian4 from "./Cartesian4.js";
  5. import Check from "./Check.js";
  6. import defaultValue from "./defaultValue.js";
  7. import defined from "./defined.js";
  8. import DeveloperError from "./DeveloperError.js";
  9. import Ellipsoid from "./Ellipsoid.js";
  10. import IntersectionTests from "./IntersectionTests.js";
  11. import Matrix4 from "./Matrix4.js";
  12. import Plane from "./Plane.js";
  13. import Ray from "./Ray.js";
  14. import Transforms from "./Transforms.js";
  15. const scratchCart4 = new Cartesian4();
  16. /**
  17. * A plane tangent to the provided ellipsoid at the provided origin.
  18. * If origin is not on the surface of the ellipsoid, it's surface projection will be used.
  19. * If origin is at the center of the ellipsoid, an exception will be thrown.
  20. * @alias EllipsoidTangentPlane
  21. * @constructor
  22. *
  23. * @param {Cartesian3} origin The point on the surface of the ellipsoid where the tangent plane touches.
  24. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use.
  25. *
  26. * @exception {DeveloperError} origin must not be at the center of the ellipsoid.
  27. */
  28. function EllipsoidTangentPlane(origin, ellipsoid) {
  29. //>>includeStart('debug', pragmas.debug);
  30. Check.defined("origin", origin);
  31. //>>includeEnd('debug');
  32. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  33. origin = ellipsoid.scaleToGeodeticSurface(origin);
  34. //>>includeStart('debug', pragmas.debug);
  35. if (!defined(origin)) {
  36. throw new DeveloperError(
  37. "origin must not be at the center of the ellipsoid."
  38. );
  39. }
  40. //>>includeEnd('debug');
  41. const eastNorthUp = Transforms.eastNorthUpToFixedFrame(origin, ellipsoid);
  42. this._ellipsoid = ellipsoid;
  43. this._origin = origin;
  44. this._xAxis = Cartesian3.fromCartesian4(
  45. Matrix4.getColumn(eastNorthUp, 0, scratchCart4)
  46. );
  47. this._yAxis = Cartesian3.fromCartesian4(
  48. Matrix4.getColumn(eastNorthUp, 1, scratchCart4)
  49. );
  50. const normal = Cartesian3.fromCartesian4(
  51. Matrix4.getColumn(eastNorthUp, 2, scratchCart4)
  52. );
  53. this._plane = Plane.fromPointNormal(origin, normal);
  54. }
  55. Object.defineProperties(EllipsoidTangentPlane.prototype, {
  56. /**
  57. * Gets the ellipsoid.
  58. * @memberof EllipsoidTangentPlane.prototype
  59. * @type {Ellipsoid}
  60. */
  61. ellipsoid: {
  62. get: function () {
  63. return this._ellipsoid;
  64. },
  65. },
  66. /**
  67. * Gets the origin.
  68. * @memberof EllipsoidTangentPlane.prototype
  69. * @type {Cartesian3}
  70. */
  71. origin: {
  72. get: function () {
  73. return this._origin;
  74. },
  75. },
  76. /**
  77. * Gets the plane which is tangent to the ellipsoid.
  78. * @memberof EllipsoidTangentPlane.prototype
  79. * @readonly
  80. * @type {Plane}
  81. */
  82. plane: {
  83. get: function () {
  84. return this._plane;
  85. },
  86. },
  87. /**
  88. * Gets the local X-axis (east) of the tangent plane.
  89. * @memberof EllipsoidTangentPlane.prototype
  90. * @readonly
  91. * @type {Cartesian3}
  92. */
  93. xAxis: {
  94. get: function () {
  95. return this._xAxis;
  96. },
  97. },
  98. /**
  99. * Gets the local Y-axis (north) of the tangent plane.
  100. * @memberof EllipsoidTangentPlane.prototype
  101. * @readonly
  102. * @type {Cartesian3}
  103. */
  104. yAxis: {
  105. get: function () {
  106. return this._yAxis;
  107. },
  108. },
  109. /**
  110. * Gets the local Z-axis (up) of the tangent plane.
  111. * @memberof EllipsoidTangentPlane.prototype
  112. * @readonly
  113. * @type {Cartesian3}
  114. */
  115. zAxis: {
  116. get: function () {
  117. return this._plane.normal;
  118. },
  119. },
  120. });
  121. const tmp = new AxisAlignedBoundingBox();
  122. /**
  123. * Creates a new instance from the provided ellipsoid and the center
  124. * point of the provided Cartesians.
  125. *
  126. * @param {Cartesian3[]} cartesians The list of positions surrounding the center point.
  127. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use.
  128. * @returns {EllipsoidTangentPlane} The new instance of EllipsoidTangentPlane.
  129. */
  130. EllipsoidTangentPlane.fromPoints = function (cartesians, ellipsoid) {
  131. //>>includeStart('debug', pragmas.debug);
  132. Check.defined("cartesians", cartesians);
  133. //>>includeEnd('debug');
  134. const box = AxisAlignedBoundingBox.fromPoints(cartesians, tmp);
  135. return new EllipsoidTangentPlane(box.center, ellipsoid);
  136. };
  137. const scratchProjectPointOntoPlaneRay = new Ray();
  138. const scratchProjectPointOntoPlaneCartesian3 = new Cartesian3();
  139. /**
  140. * Computes the projection of the provided 3D position onto the 2D plane, radially outward from the {@link EllipsoidTangentPlane.ellipsoid} coordinate system origin.
  141. *
  142. * @param {Cartesian3} cartesian The point to project.
  143. * @param {Cartesian2} [result] The object onto which to store the result.
  144. * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if none was provided. Undefined if there is no intersection point
  145. */
  146. EllipsoidTangentPlane.prototype.projectPointOntoPlane = function (
  147. cartesian,
  148. result
  149. ) {
  150. //>>includeStart('debug', pragmas.debug);
  151. Check.defined("cartesian", cartesian);
  152. //>>includeEnd('debug');
  153. const ray = scratchProjectPointOntoPlaneRay;
  154. ray.origin = cartesian;
  155. Cartesian3.normalize(cartesian, ray.direction);
  156. let intersectionPoint = IntersectionTests.rayPlane(
  157. ray,
  158. this._plane,
  159. scratchProjectPointOntoPlaneCartesian3
  160. );
  161. if (!defined(intersectionPoint)) {
  162. Cartesian3.negate(ray.direction, ray.direction);
  163. intersectionPoint = IntersectionTests.rayPlane(
  164. ray,
  165. this._plane,
  166. scratchProjectPointOntoPlaneCartesian3
  167. );
  168. }
  169. if (defined(intersectionPoint)) {
  170. const v = Cartesian3.subtract(
  171. intersectionPoint,
  172. this._origin,
  173. intersectionPoint
  174. );
  175. const x = Cartesian3.dot(this._xAxis, v);
  176. const y = Cartesian3.dot(this._yAxis, v);
  177. if (!defined(result)) {
  178. return new Cartesian2(x, y);
  179. }
  180. result.x = x;
  181. result.y = y;
  182. return result;
  183. }
  184. return undefined;
  185. };
  186. /**
  187. * Computes the projection of the provided 3D positions onto the 2D plane (where possible), radially outward from the global origin.
  188. * The resulting array may be shorter than the input array - if a single projection is impossible it will not be included.
  189. *
  190. * @see EllipsoidTangentPlane.projectPointOntoPlane
  191. *
  192. * @param {Cartesian3[]} cartesians The array of points to project.
  193. * @param {Cartesian2[]} [result] The array of Cartesian2 instances onto which to store results.
  194. * @returns {Cartesian2[]} The modified result parameter or a new array of Cartesian2 instances if none was provided.
  195. */
  196. EllipsoidTangentPlane.prototype.projectPointsOntoPlane = function (
  197. cartesians,
  198. result
  199. ) {
  200. //>>includeStart('debug', pragmas.debug);
  201. Check.defined("cartesians", cartesians);
  202. //>>includeEnd('debug');
  203. if (!defined(result)) {
  204. result = [];
  205. }
  206. let count = 0;
  207. const length = cartesians.length;
  208. for (let i = 0; i < length; i++) {
  209. const p = this.projectPointOntoPlane(cartesians[i], result[count]);
  210. if (defined(p)) {
  211. result[count] = p;
  212. count++;
  213. }
  214. }
  215. result.length = count;
  216. return result;
  217. };
  218. /**
  219. * Computes the projection of the provided 3D position onto the 2D plane, along the plane normal.
  220. *
  221. * @param {Cartesian3} cartesian The point to project.
  222. * @param {Cartesian2} [result] The object onto which to store the result.
  223. * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if none was provided.
  224. */
  225. EllipsoidTangentPlane.prototype.projectPointToNearestOnPlane = function (
  226. cartesian,
  227. result
  228. ) {
  229. //>>includeStart('debug', pragmas.debug);
  230. Check.defined("cartesian", cartesian);
  231. //>>includeEnd('debug');
  232. if (!defined(result)) {
  233. result = new Cartesian2();
  234. }
  235. const ray = scratchProjectPointOntoPlaneRay;
  236. ray.origin = cartesian;
  237. Cartesian3.clone(this._plane.normal, ray.direction);
  238. let intersectionPoint = IntersectionTests.rayPlane(
  239. ray,
  240. this._plane,
  241. scratchProjectPointOntoPlaneCartesian3
  242. );
  243. if (!defined(intersectionPoint)) {
  244. Cartesian3.negate(ray.direction, ray.direction);
  245. intersectionPoint = IntersectionTests.rayPlane(
  246. ray,
  247. this._plane,
  248. scratchProjectPointOntoPlaneCartesian3
  249. );
  250. }
  251. const v = Cartesian3.subtract(
  252. intersectionPoint,
  253. this._origin,
  254. intersectionPoint
  255. );
  256. const x = Cartesian3.dot(this._xAxis, v);
  257. const y = Cartesian3.dot(this._yAxis, v);
  258. result.x = x;
  259. result.y = y;
  260. return result;
  261. };
  262. /**
  263. * Computes the projection of the provided 3D positions onto the 2D plane, along the plane normal.
  264. *
  265. * @see EllipsoidTangentPlane.projectPointToNearestOnPlane
  266. *
  267. * @param {Cartesian3[]} cartesians The array of points to project.
  268. * @param {Cartesian2[]} [result] The array of Cartesian2 instances onto which to store results.
  269. * @returns {Cartesian2[]} The modified result parameter or a new array of Cartesian2 instances if none was provided. This will have the same length as <code>cartesians</code>.
  270. */
  271. EllipsoidTangentPlane.prototype.projectPointsToNearestOnPlane = function (
  272. cartesians,
  273. result
  274. ) {
  275. //>>includeStart('debug', pragmas.debug);
  276. Check.defined("cartesians", cartesians);
  277. //>>includeEnd('debug');
  278. if (!defined(result)) {
  279. result = [];
  280. }
  281. const length = cartesians.length;
  282. result.length = length;
  283. for (let i = 0; i < length; i++) {
  284. result[i] = this.projectPointToNearestOnPlane(cartesians[i], result[i]);
  285. }
  286. return result;
  287. };
  288. const projectPointsOntoEllipsoidScratch = new Cartesian3();
  289. /**
  290. * Computes the projection of the provided 2D position onto the 3D ellipsoid.
  291. *
  292. * @param {Cartesian2} cartesian The points to project.
  293. * @param {Cartesian3} [result] The Cartesian3 instance to store result.
  294. * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided.
  295. */
  296. EllipsoidTangentPlane.prototype.projectPointOntoEllipsoid = function (
  297. cartesian,
  298. result
  299. ) {
  300. //>>includeStart('debug', pragmas.debug);
  301. Check.defined("cartesian", cartesian);
  302. //>>includeEnd('debug');
  303. if (!defined(result)) {
  304. result = new Cartesian3();
  305. }
  306. const ellipsoid = this._ellipsoid;
  307. const origin = this._origin;
  308. const xAxis = this._xAxis;
  309. const yAxis = this._yAxis;
  310. const tmp = projectPointsOntoEllipsoidScratch;
  311. Cartesian3.multiplyByScalar(xAxis, cartesian.x, tmp);
  312. result = Cartesian3.add(origin, tmp, result);
  313. Cartesian3.multiplyByScalar(yAxis, cartesian.y, tmp);
  314. Cartesian3.add(result, tmp, result);
  315. ellipsoid.scaleToGeocentricSurface(result, result);
  316. return result;
  317. };
  318. /**
  319. * Computes the projection of the provided 2D positions onto the 3D ellipsoid.
  320. *
  321. * @param {Cartesian2[]} cartesians The array of points to project.
  322. * @param {Cartesian3[]} [result] The array of Cartesian3 instances onto which to store results.
  323. * @returns {Cartesian3[]} The modified result parameter or a new array of Cartesian3 instances if none was provided.
  324. */
  325. EllipsoidTangentPlane.prototype.projectPointsOntoEllipsoid = function (
  326. cartesians,
  327. result
  328. ) {
  329. //>>includeStart('debug', pragmas.debug);
  330. Check.defined("cartesians", cartesians);
  331. //>>includeEnd('debug');
  332. const length = cartesians.length;
  333. if (!defined(result)) {
  334. result = new Array(length);
  335. } else {
  336. result.length = length;
  337. }
  338. for (let i = 0; i < length; ++i) {
  339. result[i] = this.projectPointOntoEllipsoid(cartesians[i], result[i]);
  340. }
  341. return result;
  342. };
  343. export default EllipsoidTangentPlane;