智慧水务管理系统 - 精河县供水工程综合管理平台

TileBoundingS2Cell.js 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. import Cartesian3 from "../Core/Cartesian3.js";
  2. import defined from "../Core/defined.js";
  3. import Cartographic from "../Core/Cartographic.js";
  4. import Ellipsoid from "../Core/Ellipsoid.js";
  5. import Intersect from "../Core/Intersect.js";
  6. import Matrix3 from "../Core/Matrix3.js";
  7. import Plane from "../Core/Plane.js";
  8. import CoplanarPolygonOutlineGeometry from "../Core/CoplanarPolygonOutlineGeometry.js";
  9. import BoundingSphere from "../Core/BoundingSphere.js";
  10. import Check from "../Core/Check.js";
  11. import ColorGeometryInstanceAttribute from "../Core/ColorGeometryInstanceAttribute.js";
  12. import GeometryInstance from "../Core/GeometryInstance.js";
  13. import Matrix4 from "../Core/Matrix4.js";
  14. import PerInstanceColorAppearance from "./PerInstanceColorAppearance.js";
  15. import Primitive from "./Primitive.js";
  16. import S2Cell from "../Core/S2Cell.js";
  17. let centerCartographicScratch = new Cartographic();
  18. /**
  19. * A tile bounding volume specified as an S2 cell token with minimum and maximum heights.
  20. * The bounding volume is a k DOP. A k-DOP is the Boolean intersection of extents along k directions.
  21. *
  22. * @alias TileBoundingS2Cell
  23. * @constructor
  24. *
  25. * @param {object} options Object with the following properties:
  26. * @param {string} options.token The token of the S2 cell.
  27. * @param {number} [options.minimumHeight=0.0] The minimum height of the bounding volume.
  28. * @param {number} [options.maximumHeight=0.0] The maximum height of the bounding volume.
  29. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid.
  30. * @param {boolean} [options.computeBoundingVolumes=true] True to compute the {@link TileBoundingS2Cell#boundingVolume} and
  31. * {@link TileBoundingS2Cell#boundingSphere}. If false, these properties will be undefined.
  32. *
  33. * @private
  34. */
  35. function TileBoundingS2Cell(options) {
  36. //>>includeStart('debug', pragmas.debug);
  37. Check.typeOf.object("options", options);
  38. Check.typeOf.string("options.token", options.token);
  39. //>>includeEnd('debug');
  40. const s2Cell = S2Cell.fromToken(options.token);
  41. const minimumHeight = options.minimumHeight ?? 0.0;
  42. const maximumHeight = options.maximumHeight ?? 0.0;
  43. const ellipsoid = options.ellipsoid ?? Ellipsoid.WGS84;
  44. this.s2Cell = s2Cell;
  45. this.minimumHeight = minimumHeight;
  46. this.maximumHeight = maximumHeight;
  47. this.ellipsoid = ellipsoid;
  48. const boundingPlanes = computeBoundingPlanes(
  49. s2Cell,
  50. minimumHeight,
  51. maximumHeight,
  52. ellipsoid,
  53. );
  54. this._boundingPlanes = boundingPlanes;
  55. // Pre-compute vertices to speed up the plane intersection test.
  56. const vertices = computeVertices(boundingPlanes);
  57. this._vertices = vertices;
  58. // Pre-compute edge normals to speed up the point-polygon distance check in distanceToCamera.
  59. this._edgeNormals = new Array(6);
  60. this._edgeNormals[0] = computeEdgeNormals(
  61. boundingPlanes[0],
  62. vertices.slice(0, 4),
  63. );
  64. let i;
  65. // Based on the way the edge normals are computed, the edge normals all point away from the "face"
  66. // of the polyhedron they surround, except the plane for the top plane. Therefore, we negate the normals
  67. // for the top plane.
  68. for (i = 0; i < 4; i++) {
  69. this._edgeNormals[0][i] = Cartesian3.negate(
  70. this._edgeNormals[0][i],
  71. this._edgeNormals[0][i],
  72. );
  73. }
  74. this._edgeNormals[1] = computeEdgeNormals(
  75. boundingPlanes[1],
  76. vertices.slice(4, 8),
  77. );
  78. for (i = 0; i < 4; i++) {
  79. // For each plane, iterate through the vertices in CCW order.
  80. this._edgeNormals[2 + i] = computeEdgeNormals(boundingPlanes[2 + i], [
  81. vertices[i % 4],
  82. vertices[(i + 1) % 4],
  83. vertices[4 + ((i + 1) % 4)],
  84. vertices[4 + i],
  85. ]);
  86. }
  87. this._planeVertices = [
  88. this._vertices.slice(0, 4),
  89. this._vertices.slice(4, 8),
  90. ];
  91. for (i = 0; i < 4; i++) {
  92. this._planeVertices.push([
  93. this._vertices[i % 4],
  94. this._vertices[(i + 1) % 4],
  95. this._vertices[4 + ((i + 1) % 4)],
  96. this._vertices[4 + i],
  97. ]);
  98. }
  99. const center = s2Cell.getCenter();
  100. centerCartographicScratch = ellipsoid.cartesianToCartographic(
  101. center,
  102. centerCartographicScratch,
  103. );
  104. centerCartographicScratch.height = (maximumHeight + minimumHeight) / 2;
  105. this.center = ellipsoid.cartographicToCartesian(
  106. centerCartographicScratch,
  107. center,
  108. );
  109. this._boundingSphere = BoundingSphere.fromPoints(vertices);
  110. }
  111. const centerGeodeticNormalScratch = new Cartesian3();
  112. const topCartographicScratch = new Cartographic();
  113. const topScratch = new Cartesian3();
  114. const vertexCartographicScratch = new Cartographic();
  115. const vertexScratch = new Cartesian3();
  116. const vertexGeodeticNormalScratch = new Cartesian3();
  117. const sideNormalScratch = new Cartesian3();
  118. const sideScratch = new Cartesian3();
  119. /**
  120. * Computes bounding planes of the kDOP.
  121. * @private
  122. */
  123. function computeBoundingPlanes(
  124. s2Cell,
  125. minimumHeight,
  126. maximumHeight,
  127. ellipsoid,
  128. ) {
  129. const planes = new Array(6);
  130. const centerPoint = s2Cell.getCenter();
  131. // Compute top plane.
  132. // - Get geodetic surface normal at the center of the S2 cell.
  133. // - Get center point at maximum height of bounding volume.
  134. // - Create top plane from surface normal and top point.
  135. const centerSurfaceNormal = ellipsoid.geodeticSurfaceNormal(
  136. centerPoint,
  137. centerGeodeticNormalScratch,
  138. );
  139. const topCartographic = ellipsoid.cartesianToCartographic(
  140. centerPoint,
  141. topCartographicScratch,
  142. );
  143. topCartographic.height = maximumHeight;
  144. const top = ellipsoid.cartographicToCartesian(topCartographic, topScratch);
  145. const topPlane = Plane.fromPointNormal(top, centerSurfaceNormal);
  146. planes[0] = topPlane;
  147. // Compute bottom plane.
  148. // - Iterate through bottom vertices
  149. // - Get distance from vertex to top plane
  150. // - Find longest distance from vertex to top plane
  151. // - Translate top plane by the distance
  152. let maxDistance = 0;
  153. let i;
  154. const vertices = [];
  155. let vertex, vertexCartographic;
  156. for (i = 0; i < 4; i++) {
  157. vertex = s2Cell.getVertex(i);
  158. vertices[i] = vertex;
  159. vertexCartographic = ellipsoid.cartesianToCartographic(
  160. vertex,
  161. vertexCartographicScratch,
  162. );
  163. vertexCartographic.height = minimumHeight;
  164. const distance = Plane.getPointDistance(
  165. topPlane,
  166. ellipsoid.cartographicToCartesian(vertexCartographic, vertexScratch),
  167. );
  168. if (distance < maxDistance) {
  169. maxDistance = distance;
  170. }
  171. }
  172. const bottomPlane = Plane.clone(topPlane);
  173. // Negate the normal of the bottom plane since we want all normals to point "outwards".
  174. bottomPlane.normal = Cartesian3.negate(
  175. bottomPlane.normal,
  176. bottomPlane.normal,
  177. );
  178. bottomPlane.distance = bottomPlane.distance * -1 + maxDistance;
  179. planes[1] = bottomPlane;
  180. // Compute side planes.
  181. // - Iterate through vertices (in CCW order, by default)
  182. // - Get a vertex and another vertex adjacent to it.
  183. // - Compute geodetic surface normal at one vertex.
  184. // - Compute vector between vertices.
  185. // - Compute normal of side plane. (cross product of top dir and side dir)
  186. for (i = 0; i < 4; i++) {
  187. vertex = vertices[i];
  188. const adjacentVertex = vertices[(i + 1) % 4];
  189. const geodeticNormal = ellipsoid.geodeticSurfaceNormal(
  190. vertex,
  191. vertexGeodeticNormalScratch,
  192. );
  193. const side = Cartesian3.subtract(adjacentVertex, vertex, sideScratch);
  194. let sideNormal = Cartesian3.cross(side, geodeticNormal, sideNormalScratch);
  195. sideNormal = Cartesian3.normalize(sideNormal, sideNormal);
  196. planes[2 + i] = Plane.fromPointNormal(vertex, sideNormal);
  197. }
  198. return planes;
  199. }
  200. let n0Scratch = new Cartesian3();
  201. let n1Scratch = new Cartesian3();
  202. let n2Scratch = new Cartesian3();
  203. let x0Scratch = new Cartesian3();
  204. let x1Scratch = new Cartesian3();
  205. let x2Scratch = new Cartesian3();
  206. const t0Scratch = new Cartesian3();
  207. const t1Scratch = new Cartesian3();
  208. const t2Scratch = new Cartesian3();
  209. let f0Scratch = new Cartesian3();
  210. let f1Scratch = new Cartesian3();
  211. let f2Scratch = new Cartesian3();
  212. let sScratch = new Cartesian3();
  213. const matrixScratch = new Matrix3();
  214. /**
  215. * Computes intersection of 3 planes.
  216. * @private
  217. */
  218. function computeIntersection(p0, p1, p2) {
  219. n0Scratch = p0.normal;
  220. n1Scratch = p1.normal;
  221. n2Scratch = p2.normal;
  222. x0Scratch = Cartesian3.multiplyByScalar(p0.normal, -p0.distance, x0Scratch);
  223. x1Scratch = Cartesian3.multiplyByScalar(p1.normal, -p1.distance, x1Scratch);
  224. x2Scratch = Cartesian3.multiplyByScalar(p2.normal, -p2.distance, x2Scratch);
  225. f0Scratch = Cartesian3.multiplyByScalar(
  226. Cartesian3.cross(n1Scratch, n2Scratch, t0Scratch),
  227. Cartesian3.dot(x0Scratch, n0Scratch),
  228. f0Scratch,
  229. );
  230. f1Scratch = Cartesian3.multiplyByScalar(
  231. Cartesian3.cross(n2Scratch, n0Scratch, t1Scratch),
  232. Cartesian3.dot(x1Scratch, n1Scratch),
  233. f1Scratch,
  234. );
  235. f2Scratch = Cartesian3.multiplyByScalar(
  236. Cartesian3.cross(n0Scratch, n1Scratch, t2Scratch),
  237. Cartesian3.dot(x2Scratch, n2Scratch),
  238. f2Scratch,
  239. );
  240. matrixScratch[0] = n0Scratch.x;
  241. matrixScratch[1] = n1Scratch.x;
  242. matrixScratch[2] = n2Scratch.x;
  243. matrixScratch[3] = n0Scratch.y;
  244. matrixScratch[4] = n1Scratch.y;
  245. matrixScratch[5] = n2Scratch.y;
  246. matrixScratch[6] = n0Scratch.z;
  247. matrixScratch[7] = n1Scratch.z;
  248. matrixScratch[8] = n2Scratch.z;
  249. const determinant = Matrix3.determinant(matrixScratch);
  250. sScratch = Cartesian3.add(f0Scratch, f1Scratch, sScratch);
  251. sScratch = Cartesian3.add(sScratch, f2Scratch, sScratch);
  252. return new Cartesian3(
  253. sScratch.x / determinant,
  254. sScratch.y / determinant,
  255. sScratch.z / determinant,
  256. );
  257. }
  258. /**
  259. * Compute the vertices of the kDOP.
  260. * @private
  261. */
  262. function computeVertices(boundingPlanes) {
  263. const vertices = new Array(8);
  264. for (let i = 0; i < 4; i++) {
  265. // Vertices on the top plane.
  266. vertices[i] = computeIntersection(
  267. boundingPlanes[0],
  268. boundingPlanes[2 + ((i + 3) % 4)],
  269. boundingPlanes[2 + (i % 4)],
  270. );
  271. // Vertices on the bottom plane.
  272. vertices[i + 4] = computeIntersection(
  273. boundingPlanes[1],
  274. boundingPlanes[2 + ((i + 3) % 4)],
  275. boundingPlanes[2 + (i % 4)],
  276. );
  277. }
  278. return vertices;
  279. }
  280. let edgeScratch = new Cartesian3();
  281. let edgeNormalScratch = new Cartesian3();
  282. /**
  283. * Compute edge normals on a plane.
  284. * @private
  285. */
  286. function computeEdgeNormals(plane, vertices) {
  287. const edgeNormals = [];
  288. for (let i = 0; i < 4; i++) {
  289. edgeScratch = Cartesian3.subtract(
  290. vertices[(i + 1) % 4],
  291. vertices[i],
  292. edgeScratch,
  293. );
  294. edgeNormalScratch = Cartesian3.cross(
  295. plane.normal,
  296. edgeScratch,
  297. edgeNormalScratch,
  298. );
  299. edgeNormalScratch = Cartesian3.normalize(
  300. edgeNormalScratch,
  301. edgeNormalScratch,
  302. );
  303. edgeNormals[i] = Cartesian3.clone(edgeNormalScratch);
  304. }
  305. return edgeNormals;
  306. }
  307. Object.defineProperties(TileBoundingS2Cell.prototype, {
  308. /**
  309. * The underlying bounding volume.
  310. *
  311. * @memberof TileBoundingS2Cell.prototype
  312. *
  313. * @type {object}
  314. * @readonly
  315. */
  316. boundingVolume: {
  317. get: function () {
  318. return this;
  319. },
  320. },
  321. /**
  322. * The underlying bounding sphere.
  323. *
  324. * @memberof TileBoundingS2Cell.prototype
  325. *
  326. * @type {BoundingSphere}
  327. * @readonly
  328. */
  329. boundingSphere: {
  330. get: function () {
  331. return this._boundingSphere;
  332. },
  333. },
  334. });
  335. const facePointScratch = new Cartesian3();
  336. /**
  337. * The distance to point check for this kDOP involves checking the signed distance of the point to each bounding
  338. * plane. A plane qualifies for a distance check if the point being tested against is in the half-space in the direction
  339. * of the normal i.e. if the signed distance of the point from the plane is greater than 0.
  340. *
  341. * There are 4 possible cases for a point if it is outside the polyhedron:
  342. *
  343. * \ X / X \ / \ / \ /
  344. * ---\---------/--- ---\---------/--- ---X---------/--- ---\---------/---
  345. * \ / \ / \ / \ /
  346. * ---\-----/--- ---\-----/--- ---\-----/--- ---\-----/---
  347. * \ / \ / \ / \ /
  348. * \ /
  349. * \
  350. * / \
  351. * / X \
  352. *
  353. * I II III IV
  354. *
  355. * Case I: There is only one plane selected.
  356. * In this case, we project the point onto the plane and do a point polygon distance check to find the closest point on the polygon.
  357. * The point may lie inside the "face" of the polygon or outside. If it is outside, we need to determine which edges to test against.
  358. *
  359. * Case II: There are two planes selected.
  360. * In this case, the point will lie somewhere on the line created at the intersection of the selected planes or one of the planes.
  361. *
  362. * Case III: There are three planes selected.
  363. * In this case, the point will lie on the vertex, at the intersection of the selected planes.
  364. *
  365. * Case IV: There are more than three planes selected.
  366. * Since we are on an ellipsoid, this will only happen in the bottom plane, which is what we will use for the distance test.
  367. */
  368. TileBoundingS2Cell.prototype.distanceToCamera = function (frameState) {
  369. //>>includeStart('debug', pragmas.debug);
  370. Check.defined("frameState", frameState);
  371. //>>includeEnd('debug');
  372. const point = frameState.camera.positionWC;
  373. const selectedPlaneIndices = [];
  374. const vertices = [];
  375. let edgeNormals;
  376. if (Plane.getPointDistance(this._boundingPlanes[0], point) > 0) {
  377. selectedPlaneIndices.push(0);
  378. vertices.push(this._planeVertices[0]);
  379. edgeNormals = this._edgeNormals[0];
  380. } else if (Plane.getPointDistance(this._boundingPlanes[1], point) > 0) {
  381. selectedPlaneIndices.push(1);
  382. vertices.push(this._planeVertices[1]);
  383. edgeNormals = this._edgeNormals[1];
  384. }
  385. let i;
  386. let sidePlaneIndex;
  387. for (i = 0; i < 4; i++) {
  388. sidePlaneIndex = 2 + i;
  389. if (
  390. Plane.getPointDistance(this._boundingPlanes[sidePlaneIndex], point) > 0
  391. ) {
  392. selectedPlaneIndices.push(sidePlaneIndex);
  393. // Store vertices in CCW order.
  394. vertices.push(this._planeVertices[sidePlaneIndex]);
  395. edgeNormals = this._edgeNormals[sidePlaneIndex];
  396. }
  397. }
  398. // Check if inside all planes.
  399. if (selectedPlaneIndices.length === 0) {
  400. return 0.0;
  401. }
  402. // We use the skip variable when the side plane indices are non-consecutive.
  403. let facePoint;
  404. let selectedPlane;
  405. if (selectedPlaneIndices.length === 1) {
  406. // Handles Case I
  407. selectedPlane = this._boundingPlanes[selectedPlaneIndices[0]];
  408. facePoint = closestPointPolygon(
  409. Plane.projectPointOntoPlane(selectedPlane, point, facePointScratch),
  410. vertices[0],
  411. selectedPlane,
  412. edgeNormals,
  413. );
  414. return Cartesian3.distance(facePoint, point);
  415. } else if (selectedPlaneIndices.length === 2) {
  416. // Handles Case II
  417. // Since we are on the ellipsoid, the dihedral angle between a top plane and a side plane
  418. // will always be acute, so we can do a faster check there.
  419. if (selectedPlaneIndices[0] === 0) {
  420. const edge = [
  421. this._vertices[
  422. 4 * selectedPlaneIndices[0] + (selectedPlaneIndices[1] - 2)
  423. ],
  424. this._vertices[
  425. 4 * selectedPlaneIndices[0] + ((selectedPlaneIndices[1] - 2 + 1) % 4)
  426. ],
  427. ];
  428. facePoint = closestPointLineSegment(point, edge[0], edge[1]);
  429. return Cartesian3.distance(facePoint, point);
  430. }
  431. let minimumDistance = Number.MAX_VALUE;
  432. let distance;
  433. for (i = 0; i < 2; i++) {
  434. selectedPlane = this._boundingPlanes[selectedPlaneIndices[i]];
  435. facePoint = closestPointPolygon(
  436. Plane.projectPointOntoPlane(selectedPlane, point, facePointScratch),
  437. vertices[i],
  438. selectedPlane,
  439. this._edgeNormals[selectedPlaneIndices[i]],
  440. );
  441. distance = Cartesian3.distanceSquared(facePoint, point);
  442. if (distance < minimumDistance) {
  443. minimumDistance = distance;
  444. }
  445. }
  446. return Math.sqrt(minimumDistance);
  447. } else if (selectedPlaneIndices.length > 3) {
  448. // Handles Case IV
  449. facePoint = closestPointPolygon(
  450. Plane.projectPointOntoPlane(
  451. this._boundingPlanes[1],
  452. point,
  453. facePointScratch,
  454. ),
  455. this._planeVertices[1],
  456. this._boundingPlanes[1],
  457. this._edgeNormals[1],
  458. );
  459. return Cartesian3.distance(facePoint, point);
  460. }
  461. // Handles Case III
  462. const skip =
  463. selectedPlaneIndices[1] === 2 && selectedPlaneIndices[2] === 5 ? 0 : 1;
  464. // Vertex is on top plane.
  465. if (selectedPlaneIndices[0] === 0) {
  466. return Cartesian3.distance(
  467. point,
  468. this._vertices[(selectedPlaneIndices[1] - 2 + skip) % 4],
  469. );
  470. }
  471. // Vertex is on bottom plane.
  472. return Cartesian3.distance(
  473. point,
  474. this._vertices[4 + ((selectedPlaneIndices[1] - 2 + skip) % 4)],
  475. );
  476. };
  477. const dScratch = new Cartesian3();
  478. const pL0Scratch = new Cartesian3();
  479. /**
  480. * Finds point on a line segment closest to a given point.
  481. * @private
  482. */
  483. function closestPointLineSegment(p, l0, l1) {
  484. const d = Cartesian3.subtract(l1, l0, dScratch);
  485. const pL0 = Cartesian3.subtract(p, l0, pL0Scratch);
  486. let t = Cartesian3.dot(d, pL0);
  487. if (t <= 0) {
  488. return l0;
  489. }
  490. const dMag = Cartesian3.dot(d, d);
  491. if (t >= dMag) {
  492. return l1;
  493. }
  494. t = t / dMag;
  495. return new Cartesian3(
  496. (1 - t) * l0.x + t * l1.x,
  497. (1 - t) * l0.y + t * l1.y,
  498. (1 - t) * l0.z + t * l1.z,
  499. );
  500. }
  501. const edgePlaneScratch = new Plane(Cartesian3.UNIT_X, 0.0);
  502. /**
  503. * Finds closes point on the polygon, created by the given vertices, from
  504. * a point. The test point and the polygon are all on the same plane.
  505. * @private
  506. */
  507. function closestPointPolygon(p, vertices, plane, edgeNormals) {
  508. let minDistance = Number.MAX_VALUE;
  509. let distance;
  510. let closestPoint;
  511. let closestPointOnEdge;
  512. for (let i = 0; i < vertices.length; i++) {
  513. const edgePlane = Plane.fromPointNormal(
  514. vertices[i],
  515. edgeNormals[i],
  516. edgePlaneScratch,
  517. );
  518. const edgePlaneDistance = Plane.getPointDistance(edgePlane, p);
  519. // Skip checking against the edge if the point is not in the half-space that the
  520. // edgePlane's normal points towards i.e. if the edgePlane is facing away from the point.
  521. if (edgePlaneDistance < 0) {
  522. continue;
  523. }
  524. closestPointOnEdge = closestPointLineSegment(
  525. p,
  526. vertices[i],
  527. vertices[(i + 1) % 4],
  528. );
  529. distance = Cartesian3.distance(p, closestPointOnEdge);
  530. if (distance < minDistance) {
  531. minDistance = distance;
  532. closestPoint = closestPointOnEdge;
  533. }
  534. }
  535. if (!defined(closestPoint)) {
  536. return p;
  537. }
  538. return closestPoint;
  539. }
  540. /**
  541. * Determines which side of a plane this volume is located.
  542. *
  543. * @param {Plane} plane The plane to test against.
  544. * @returns {Intersect} {@link Intersect.INSIDE} if the entire volume is on the side of the plane
  545. * the normal is pointing, {@link Intersect.OUTSIDE} if the entire volume is
  546. * on the opposite side, and {@link Intersect.INTERSECTING} if the volume
  547. * intersects the plane.
  548. */
  549. TileBoundingS2Cell.prototype.intersectPlane = function (plane) {
  550. //>>includeStart('debug', pragmas.debug);
  551. Check.defined("plane", plane);
  552. //>>includeEnd('debug');
  553. let plusCount = 0;
  554. let negCount = 0;
  555. for (let i = 0; i < this._vertices.length; i++) {
  556. const distanceToPlane =
  557. Cartesian3.dot(plane.normal, this._vertices[i]) + plane.distance;
  558. if (distanceToPlane < 0) {
  559. negCount++;
  560. } else {
  561. plusCount++;
  562. }
  563. }
  564. if (plusCount === this._vertices.length) {
  565. return Intersect.INSIDE;
  566. } else if (negCount === this._vertices.length) {
  567. return Intersect.OUTSIDE;
  568. }
  569. return Intersect.INTERSECTING;
  570. };
  571. /**
  572. * Creates a debug primitive that shows the outline of the tile bounding
  573. * volume.
  574. *
  575. * @param {Color} color The desired color of the primitive's mesh
  576. * @return {Primitive}
  577. */
  578. TileBoundingS2Cell.prototype.createDebugVolume = function (color) {
  579. //>>includeStart('debug', pragmas.debug);
  580. Check.defined("color", color);
  581. //>>includeEnd('debug');
  582. const modelMatrix = Matrix4.clone(Matrix4.IDENTITY);
  583. const topPlanePolygon = new CoplanarPolygonOutlineGeometry({
  584. polygonHierarchy: {
  585. positions: this._planeVertices[0],
  586. },
  587. });
  588. const topPlaneGeometry =
  589. CoplanarPolygonOutlineGeometry.createGeometry(topPlanePolygon);
  590. const topPlaneInstance = new GeometryInstance({
  591. geometry: topPlaneGeometry,
  592. id: "outline",
  593. modelMatrix: modelMatrix,
  594. attributes: {
  595. color: ColorGeometryInstanceAttribute.fromColor(color),
  596. },
  597. });
  598. const bottomPlanePolygon = new CoplanarPolygonOutlineGeometry({
  599. polygonHierarchy: {
  600. positions: this._planeVertices[1],
  601. },
  602. });
  603. const bottomPlaneGeometry =
  604. CoplanarPolygonOutlineGeometry.createGeometry(bottomPlanePolygon);
  605. const bottomPlaneInstance = new GeometryInstance({
  606. geometry: bottomPlaneGeometry,
  607. id: "outline",
  608. modelMatrix: modelMatrix,
  609. attributes: {
  610. color: ColorGeometryInstanceAttribute.fromColor(color),
  611. },
  612. });
  613. const sideInstances = [];
  614. for (let i = 0; i < 4; i++) {
  615. const sidePlanePolygon = new CoplanarPolygonOutlineGeometry({
  616. polygonHierarchy: {
  617. positions: this._planeVertices[2 + i],
  618. },
  619. });
  620. const sidePlaneGeometry =
  621. CoplanarPolygonOutlineGeometry.createGeometry(sidePlanePolygon);
  622. sideInstances[i] = new GeometryInstance({
  623. geometry: sidePlaneGeometry,
  624. id: "outline",
  625. modelMatrix: modelMatrix,
  626. attributes: {
  627. color: ColorGeometryInstanceAttribute.fromColor(color),
  628. },
  629. });
  630. }
  631. return new Primitive({
  632. geometryInstances: [
  633. sideInstances[0],
  634. sideInstances[1],
  635. sideInstances[2],
  636. sideInstances[3],
  637. bottomPlaneInstance,
  638. topPlaneInstance,
  639. ],
  640. appearance: new PerInstanceColorAppearance({
  641. translucent: false,
  642. flat: true,
  643. }),
  644. asynchronous: false,
  645. });
  646. };
  647. export default TileBoundingS2Cell;