PolygonGeometryLibrary.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981
  1. import ArcType from "./ArcType.js";
  2. import arrayRemoveDuplicates from "./arrayRemoveDuplicates.js";
  3. import Cartesian2 from "./Cartesian2.js";
  4. import Cartesian3 from "./Cartesian3.js";
  5. import Cartographic from "./Cartographic.js";
  6. import ComponentDatatype from "./ComponentDatatype.js";
  7. import defaultValue from "./defaultValue.js";
  8. import defined from "./defined.js";
  9. import Ellipsoid from "./Ellipsoid.js";
  10. import EllipsoidRhumbLine from "./EllipsoidRhumbLine.js";
  11. import Geometry from "./Geometry.js";
  12. import GeometryAttribute from "./GeometryAttribute.js";
  13. import GeometryAttributes from "./GeometryAttributes.js";
  14. import GeometryPipeline from "./GeometryPipeline.js";
  15. import IndexDatatype from "./IndexDatatype.js";
  16. import CesiumMath from "./Math.js";
  17. import Matrix3 from "./Matrix3.js";
  18. import PolygonPipeline from "./PolygonPipeline.js";
  19. import PrimitiveType from "./PrimitiveType.js";
  20. import Quaternion from "./Quaternion.js";
  21. import Queue from "./Queue.js";
  22. import WindingOrder from "./WindingOrder.js";
  23. /**
  24. * @private
  25. */
  26. const PolygonGeometryLibrary = {};
  27. PolygonGeometryLibrary.computeHierarchyPackedLength = function (
  28. polygonHierarchy,
  29. CartesianX
  30. ) {
  31. let numComponents = 0;
  32. const stack = [polygonHierarchy];
  33. while (stack.length > 0) {
  34. const hierarchy = stack.pop();
  35. if (!defined(hierarchy)) {
  36. continue;
  37. }
  38. numComponents += 2;
  39. const positions = hierarchy.positions;
  40. const holes = hierarchy.holes;
  41. if (defined(positions) && positions.length > 0) {
  42. numComponents += positions.length * CartesianX.packedLength;
  43. }
  44. if (defined(holes)) {
  45. const length = holes.length;
  46. for (let i = 0; i < length; ++i) {
  47. stack.push(holes[i]);
  48. }
  49. }
  50. }
  51. return numComponents;
  52. };
  53. PolygonGeometryLibrary.packPolygonHierarchy = function (
  54. polygonHierarchy,
  55. array,
  56. startingIndex,
  57. CartesianX
  58. ) {
  59. const stack = [polygonHierarchy];
  60. while (stack.length > 0) {
  61. const hierarchy = stack.pop();
  62. if (!defined(hierarchy)) {
  63. continue;
  64. }
  65. const positions = hierarchy.positions;
  66. const holes = hierarchy.holes;
  67. array[startingIndex++] = defined(positions) ? positions.length : 0;
  68. array[startingIndex++] = defined(holes) ? holes.length : 0;
  69. if (defined(positions)) {
  70. const positionsLength = positions.length;
  71. for (
  72. let i = 0;
  73. i < positionsLength;
  74. ++i, startingIndex += CartesianX.packedLength
  75. ) {
  76. CartesianX.pack(positions[i], array, startingIndex);
  77. }
  78. }
  79. if (defined(holes)) {
  80. const holesLength = holes.length;
  81. for (let j = 0; j < holesLength; ++j) {
  82. stack.push(holes[j]);
  83. }
  84. }
  85. }
  86. return startingIndex;
  87. };
  88. PolygonGeometryLibrary.unpackPolygonHierarchy = function (
  89. array,
  90. startingIndex,
  91. CartesianX
  92. ) {
  93. const positionsLength = array[startingIndex++];
  94. const holesLength = array[startingIndex++];
  95. const positions = new Array(positionsLength);
  96. const holes = holesLength > 0 ? new Array(holesLength) : undefined;
  97. for (
  98. let i = 0;
  99. i < positionsLength;
  100. ++i, startingIndex += CartesianX.packedLength
  101. ) {
  102. positions[i] = CartesianX.unpack(array, startingIndex);
  103. }
  104. for (let j = 0; j < holesLength; ++j) {
  105. holes[j] = PolygonGeometryLibrary.unpackPolygonHierarchy(
  106. array,
  107. startingIndex,
  108. CartesianX
  109. );
  110. startingIndex = holes[j].startingIndex;
  111. delete holes[j].startingIndex;
  112. }
  113. return {
  114. positions: positions,
  115. holes: holes,
  116. startingIndex: startingIndex,
  117. };
  118. };
  119. const distance2DScratch = new Cartesian2();
  120. function getPointAtDistance2D(p0, p1, distance, length) {
  121. Cartesian2.subtract(p1, p0, distance2DScratch);
  122. Cartesian2.multiplyByScalar(
  123. distance2DScratch,
  124. distance / length,
  125. distance2DScratch
  126. );
  127. Cartesian2.add(p0, distance2DScratch, distance2DScratch);
  128. return [distance2DScratch.x, distance2DScratch.y];
  129. }
  130. const distanceScratch = new Cartesian3();
  131. function getPointAtDistance(p0, p1, distance, length) {
  132. Cartesian3.subtract(p1, p0, distanceScratch);
  133. Cartesian3.multiplyByScalar(
  134. distanceScratch,
  135. distance / length,
  136. distanceScratch
  137. );
  138. Cartesian3.add(p0, distanceScratch, distanceScratch);
  139. return [distanceScratch.x, distanceScratch.y, distanceScratch.z];
  140. }
  141. PolygonGeometryLibrary.subdivideLineCount = function (p0, p1, minDistance) {
  142. const distance = Cartesian3.distance(p0, p1);
  143. const n = distance / minDistance;
  144. const countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n)));
  145. return Math.pow(2, countDivide);
  146. };
  147. const scratchCartographic0 = new Cartographic();
  148. const scratchCartographic1 = new Cartographic();
  149. const scratchCartographic2 = new Cartographic();
  150. const scratchCartesian0 = new Cartesian3();
  151. const scratchRhumbLine = new EllipsoidRhumbLine();
  152. PolygonGeometryLibrary.subdivideRhumbLineCount = function (
  153. ellipsoid,
  154. p0,
  155. p1,
  156. minDistance
  157. ) {
  158. const c0 = ellipsoid.cartesianToCartographic(p0, scratchCartographic0);
  159. const c1 = ellipsoid.cartesianToCartographic(p1, scratchCartographic1);
  160. const rhumb = new EllipsoidRhumbLine(c0, c1, ellipsoid);
  161. const n = rhumb.surfaceDistance / minDistance;
  162. const countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n)));
  163. return Math.pow(2, countDivide);
  164. };
  165. /**
  166. * Subdivides texture coordinates based on the subdivision of the associated world positions.
  167. *
  168. * @param {Cartesian2} t0 First texture coordinate.
  169. * @param {Cartesian2} t1 Second texture coordinate.
  170. * @param {Cartesian3} p0 First world position.
  171. * @param {Cartesian3} p1 Second world position.
  172. * @param {number} minDistance Minimum distance for a segment.
  173. * @param {Cartesian2[]} result The subdivided texture coordinates.
  174. *
  175. * @private
  176. */
  177. PolygonGeometryLibrary.subdivideTexcoordLine = function (
  178. t0,
  179. t1,
  180. p0,
  181. p1,
  182. minDistance,
  183. result
  184. ) {
  185. // Compute the number of subdivisions.
  186. const subdivisions = PolygonGeometryLibrary.subdivideLineCount(
  187. p0,
  188. p1,
  189. minDistance
  190. );
  191. // Compute the distance between each subdivided point.
  192. const length2D = Cartesian2.distance(t0, t1);
  193. const distanceBetweenCoords = length2D / subdivisions;
  194. // Resize the result array.
  195. const texcoords = result;
  196. texcoords.length = subdivisions * 2;
  197. // Compute texture coordinates using linear interpolation.
  198. let index = 0;
  199. for (let i = 0; i < subdivisions; i++) {
  200. const t = getPointAtDistance2D(t0, t1, i * distanceBetweenCoords, length2D);
  201. texcoords[index++] = t[0];
  202. texcoords[index++] = t[1];
  203. }
  204. return texcoords;
  205. };
  206. PolygonGeometryLibrary.subdivideLine = function (p0, p1, minDistance, result) {
  207. const numVertices = PolygonGeometryLibrary.subdivideLineCount(
  208. p0,
  209. p1,
  210. minDistance
  211. );
  212. const length = Cartesian3.distance(p0, p1);
  213. const distanceBetweenVertices = length / numVertices;
  214. if (!defined(result)) {
  215. result = [];
  216. }
  217. const positions = result;
  218. positions.length = numVertices * 3;
  219. let index = 0;
  220. for (let i = 0; i < numVertices; i++) {
  221. const p = getPointAtDistance(p0, p1, i * distanceBetweenVertices, length);
  222. positions[index++] = p[0];
  223. positions[index++] = p[1];
  224. positions[index++] = p[2];
  225. }
  226. return positions;
  227. };
  228. /**
  229. * Subdivides texture coordinates based on the subdivision of the associated world positions using a rhumb line.
  230. *
  231. * @param {Cartesian2} t0 First texture coordinate.
  232. * @param {Cartesian2} t1 Second texture coordinate.
  233. * @param {Ellipsoid} ellipsoid The ellipsoid.
  234. * @param {Cartesian3} p0 First world position.
  235. * @param {Cartesian3} p1 Second world position.
  236. * @param {number} minDistance Minimum distance for a segment.
  237. * @param {Cartesian2[]} result The subdivided texture coordinates.
  238. *
  239. * @private
  240. */
  241. PolygonGeometryLibrary.subdivideTexcoordRhumbLine = function (
  242. t0,
  243. t1,
  244. ellipsoid,
  245. p0,
  246. p1,
  247. minDistance,
  248. result
  249. ) {
  250. // Compute the surface distance.
  251. const c0 = ellipsoid.cartesianToCartographic(p0, scratchCartographic0);
  252. const c1 = ellipsoid.cartesianToCartographic(p1, scratchCartographic1);
  253. scratchRhumbLine.setEndPoints(c0, c1);
  254. const n = scratchRhumbLine.surfaceDistance / minDistance;
  255. // Compute the number of subdivisions.
  256. const countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n)));
  257. const subdivisions = Math.pow(2, countDivide);
  258. // Compute the distance between each subdivided point.
  259. const length2D = Cartesian2.distance(t0, t1);
  260. const distanceBetweenCoords = length2D / subdivisions;
  261. // Resize the result array.
  262. const texcoords = result;
  263. texcoords.length = subdivisions * 2;
  264. // Compute texture coordinates using linear interpolation.
  265. let index = 0;
  266. for (let i = 0; i < subdivisions; i++) {
  267. const t = getPointAtDistance2D(t0, t1, i * distanceBetweenCoords, length2D);
  268. texcoords[index++] = t[0];
  269. texcoords[index++] = t[1];
  270. }
  271. return texcoords;
  272. };
  273. PolygonGeometryLibrary.subdivideRhumbLine = function (
  274. ellipsoid,
  275. p0,
  276. p1,
  277. minDistance,
  278. result
  279. ) {
  280. const c0 = ellipsoid.cartesianToCartographic(p0, scratchCartographic0);
  281. const c1 = ellipsoid.cartesianToCartographic(p1, scratchCartographic1);
  282. const rhumb = new EllipsoidRhumbLine(c0, c1, ellipsoid);
  283. const n = rhumb.surfaceDistance / minDistance;
  284. const countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n)));
  285. const numVertices = Math.pow(2, countDivide);
  286. const distanceBetweenVertices = rhumb.surfaceDistance / numVertices;
  287. if (!defined(result)) {
  288. result = [];
  289. }
  290. const positions = result;
  291. positions.length = numVertices * 3;
  292. let index = 0;
  293. for (let i = 0; i < numVertices; i++) {
  294. const c = rhumb.interpolateUsingSurfaceDistance(
  295. i * distanceBetweenVertices,
  296. scratchCartographic2
  297. );
  298. const p = ellipsoid.cartographicToCartesian(c, scratchCartesian0);
  299. positions[index++] = p.x;
  300. positions[index++] = p.y;
  301. positions[index++] = p.z;
  302. }
  303. return positions;
  304. };
  305. const scaleToGeodeticHeightN1 = new Cartesian3();
  306. const scaleToGeodeticHeightN2 = new Cartesian3();
  307. const scaleToGeodeticHeightP1 = new Cartesian3();
  308. const scaleToGeodeticHeightP2 = new Cartesian3();
  309. PolygonGeometryLibrary.scaleToGeodeticHeightExtruded = function (
  310. geometry,
  311. maxHeight,
  312. minHeight,
  313. ellipsoid,
  314. perPositionHeight
  315. ) {
  316. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  317. const n1 = scaleToGeodeticHeightN1;
  318. let n2 = scaleToGeodeticHeightN2;
  319. const p = scaleToGeodeticHeightP1;
  320. let p2 = scaleToGeodeticHeightP2;
  321. if (
  322. defined(geometry) &&
  323. defined(geometry.attributes) &&
  324. defined(geometry.attributes.position)
  325. ) {
  326. const positions = geometry.attributes.position.values;
  327. const length = positions.length / 2;
  328. for (let i = 0; i < length; i += 3) {
  329. Cartesian3.fromArray(positions, i, p);
  330. ellipsoid.geodeticSurfaceNormal(p, n1);
  331. p2 = ellipsoid.scaleToGeodeticSurface(p, p2);
  332. n2 = Cartesian3.multiplyByScalar(n1, minHeight, n2);
  333. n2 = Cartesian3.add(p2, n2, n2);
  334. positions[i + length] = n2.x;
  335. positions[i + 1 + length] = n2.y;
  336. positions[i + 2 + length] = n2.z;
  337. if (perPositionHeight) {
  338. p2 = Cartesian3.clone(p, p2);
  339. }
  340. n2 = Cartesian3.multiplyByScalar(n1, maxHeight, n2);
  341. n2 = Cartesian3.add(p2, n2, n2);
  342. positions[i] = n2.x;
  343. positions[i + 1] = n2.y;
  344. positions[i + 2] = n2.z;
  345. }
  346. }
  347. return geometry;
  348. };
  349. PolygonGeometryLibrary.polygonOutlinesFromHierarchy = function (
  350. polygonHierarchy,
  351. scaleToEllipsoidSurface,
  352. ellipsoid
  353. ) {
  354. // create from a polygon hierarchy
  355. // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
  356. const polygons = [];
  357. const queue = new Queue();
  358. queue.enqueue(polygonHierarchy);
  359. let i;
  360. let j;
  361. let length;
  362. while (queue.length !== 0) {
  363. const outerNode = queue.dequeue();
  364. let outerRing = outerNode.positions;
  365. if (scaleToEllipsoidSurface) {
  366. length = outerRing.length;
  367. for (i = 0; i < length; i++) {
  368. ellipsoid.scaleToGeodeticSurface(outerRing[i], outerRing[i]);
  369. }
  370. }
  371. outerRing = arrayRemoveDuplicates(
  372. outerRing,
  373. Cartesian3.equalsEpsilon,
  374. true
  375. );
  376. if (outerRing.length < 3) {
  377. continue;
  378. }
  379. const numChildren = outerNode.holes ? outerNode.holes.length : 0;
  380. // The outer polygon contains inner polygons
  381. for (i = 0; i < numChildren; i++) {
  382. const hole = outerNode.holes[i];
  383. let holePositions = hole.positions;
  384. if (scaleToEllipsoidSurface) {
  385. length = holePositions.length;
  386. for (j = 0; j < length; ++j) {
  387. ellipsoid.scaleToGeodeticSurface(holePositions[j], holePositions[j]);
  388. }
  389. }
  390. holePositions = arrayRemoveDuplicates(
  391. holePositions,
  392. Cartesian3.equalsEpsilon,
  393. true
  394. );
  395. if (holePositions.length < 3) {
  396. continue;
  397. }
  398. polygons.push(holePositions);
  399. let numGrandchildren = 0;
  400. if (defined(hole.holes)) {
  401. numGrandchildren = hole.holes.length;
  402. }
  403. for (j = 0; j < numGrandchildren; j++) {
  404. queue.enqueue(hole.holes[j]);
  405. }
  406. }
  407. polygons.push(outerRing);
  408. }
  409. return polygons;
  410. };
  411. PolygonGeometryLibrary.polygonsFromHierarchy = function (
  412. polygonHierarchy,
  413. keepDuplicates,
  414. projectPointsTo2D,
  415. scaleToEllipsoidSurface,
  416. ellipsoid
  417. ) {
  418. // create from a polygon hierarchy
  419. // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
  420. const hierarchy = [];
  421. const polygons = [];
  422. const queue = new Queue();
  423. queue.enqueue(polygonHierarchy);
  424. while (queue.length !== 0) {
  425. const outerNode = queue.dequeue();
  426. let outerRing = outerNode.positions;
  427. const holes = outerNode.holes;
  428. let i;
  429. let length;
  430. if (scaleToEllipsoidSurface) {
  431. length = outerRing.length;
  432. for (i = 0; i < length; i++) {
  433. ellipsoid.scaleToGeodeticSurface(outerRing[i], outerRing[i]);
  434. }
  435. }
  436. if (!keepDuplicates) {
  437. outerRing = arrayRemoveDuplicates(
  438. outerRing,
  439. Cartesian3.equalsEpsilon,
  440. true
  441. );
  442. }
  443. if (outerRing.length < 3) {
  444. continue;
  445. }
  446. let positions2D = projectPointsTo2D(outerRing);
  447. if (!defined(positions2D)) {
  448. continue;
  449. }
  450. const holeIndices = [];
  451. let originalWindingOrder = PolygonPipeline.computeWindingOrder2D(
  452. positions2D
  453. );
  454. if (originalWindingOrder === WindingOrder.CLOCKWISE) {
  455. positions2D.reverse();
  456. outerRing = outerRing.slice().reverse();
  457. }
  458. let positions = outerRing.slice();
  459. const numChildren = defined(holes) ? holes.length : 0;
  460. const polygonHoles = [];
  461. let j;
  462. for (i = 0; i < numChildren; i++) {
  463. const hole = holes[i];
  464. let holePositions = hole.positions;
  465. if (scaleToEllipsoidSurface) {
  466. length = holePositions.length;
  467. for (j = 0; j < length; ++j) {
  468. ellipsoid.scaleToGeodeticSurface(holePositions[j], holePositions[j]);
  469. }
  470. }
  471. if (!keepDuplicates) {
  472. holePositions = arrayRemoveDuplicates(
  473. holePositions,
  474. Cartesian3.equalsEpsilon,
  475. true
  476. );
  477. }
  478. if (holePositions.length < 3) {
  479. continue;
  480. }
  481. const holePositions2D = projectPointsTo2D(holePositions);
  482. if (!defined(holePositions2D)) {
  483. continue;
  484. }
  485. originalWindingOrder = PolygonPipeline.computeWindingOrder2D(
  486. holePositions2D
  487. );
  488. if (originalWindingOrder === WindingOrder.CLOCKWISE) {
  489. holePositions2D.reverse();
  490. holePositions = holePositions.slice().reverse();
  491. }
  492. polygonHoles.push(holePositions);
  493. holeIndices.push(positions.length);
  494. positions = positions.concat(holePositions);
  495. positions2D = positions2D.concat(holePositions2D);
  496. let numGrandchildren = 0;
  497. if (defined(hole.holes)) {
  498. numGrandchildren = hole.holes.length;
  499. }
  500. for (j = 0; j < numGrandchildren; j++) {
  501. queue.enqueue(hole.holes[j]);
  502. }
  503. }
  504. hierarchy.push({
  505. outerRing: outerRing,
  506. holes: polygonHoles,
  507. });
  508. polygons.push({
  509. positions: positions,
  510. positions2D: positions2D,
  511. holes: holeIndices,
  512. });
  513. }
  514. return {
  515. hierarchy: hierarchy,
  516. polygons: polygons,
  517. };
  518. };
  519. const computeBoundingRectangleCartesian2 = new Cartesian2();
  520. const computeBoundingRectangleCartesian3 = new Cartesian3();
  521. const computeBoundingRectangleQuaternion = new Quaternion();
  522. const computeBoundingRectangleMatrix3 = new Matrix3();
  523. PolygonGeometryLibrary.computeBoundingRectangle = function (
  524. planeNormal,
  525. projectPointTo2D,
  526. positions,
  527. angle,
  528. result
  529. ) {
  530. const rotation = Quaternion.fromAxisAngle(
  531. planeNormal,
  532. angle,
  533. computeBoundingRectangleQuaternion
  534. );
  535. const textureMatrix = Matrix3.fromQuaternion(
  536. rotation,
  537. computeBoundingRectangleMatrix3
  538. );
  539. let minX = Number.POSITIVE_INFINITY;
  540. let maxX = Number.NEGATIVE_INFINITY;
  541. let minY = Number.POSITIVE_INFINITY;
  542. let maxY = Number.NEGATIVE_INFINITY;
  543. const length = positions.length;
  544. for (let i = 0; i < length; ++i) {
  545. const p = Cartesian3.clone(
  546. positions[i],
  547. computeBoundingRectangleCartesian3
  548. );
  549. Matrix3.multiplyByVector(textureMatrix, p, p);
  550. const st = projectPointTo2D(p, computeBoundingRectangleCartesian2);
  551. if (defined(st)) {
  552. minX = Math.min(minX, st.x);
  553. maxX = Math.max(maxX, st.x);
  554. minY = Math.min(minY, st.y);
  555. maxY = Math.max(maxY, st.y);
  556. }
  557. }
  558. result.x = minX;
  559. result.y = minY;
  560. result.width = maxX - minX;
  561. result.height = maxY - minY;
  562. return result;
  563. };
  564. PolygonGeometryLibrary.createGeometryFromPositions = function (
  565. ellipsoid,
  566. polygon,
  567. textureCoordinates,
  568. granularity,
  569. perPositionHeight,
  570. vertexFormat,
  571. arcType
  572. ) {
  573. let indices = PolygonPipeline.triangulate(polygon.positions2D, polygon.holes);
  574. /* If polygon is completely unrenderable, just use the first three vertices */
  575. if (indices.length < 3) {
  576. indices = [0, 1, 2];
  577. }
  578. const positions = polygon.positions;
  579. const hasTexcoords = defined(textureCoordinates);
  580. const texcoords = hasTexcoords ? textureCoordinates.positions : undefined;
  581. if (perPositionHeight) {
  582. const length = positions.length;
  583. const flattenedPositions = new Array(length * 3);
  584. let index = 0;
  585. for (let i = 0; i < length; i++) {
  586. const p = positions[i];
  587. flattenedPositions[index++] = p.x;
  588. flattenedPositions[index++] = p.y;
  589. flattenedPositions[index++] = p.z;
  590. }
  591. const geometryOptions = {
  592. attributes: {
  593. position: new GeometryAttribute({
  594. componentDatatype: ComponentDatatype.DOUBLE,
  595. componentsPerAttribute: 3,
  596. values: flattenedPositions,
  597. }),
  598. },
  599. indices: indices,
  600. primitiveType: PrimitiveType.TRIANGLES,
  601. };
  602. if (hasTexcoords) {
  603. geometryOptions.attributes.st = new GeometryAttribute({
  604. componentDatatype: ComponentDatatype.FLOAT,
  605. componentsPerAttribute: 2,
  606. values: Cartesian2.packArray(texcoords),
  607. });
  608. }
  609. const geometry = new Geometry(geometryOptions);
  610. if (vertexFormat.normal) {
  611. return GeometryPipeline.computeNormal(geometry);
  612. }
  613. return geometry;
  614. }
  615. if (arcType === ArcType.GEODESIC) {
  616. return PolygonPipeline.computeSubdivision(
  617. ellipsoid,
  618. positions,
  619. indices,
  620. texcoords,
  621. granularity
  622. );
  623. } else if (arcType === ArcType.RHUMB) {
  624. return PolygonPipeline.computeRhumbLineSubdivision(
  625. ellipsoid,
  626. positions,
  627. indices,
  628. texcoords,
  629. granularity
  630. );
  631. }
  632. };
  633. const computeWallTexcoordsSubdivided = [];
  634. const computeWallIndicesSubdivided = [];
  635. const p1Scratch = new Cartesian3();
  636. const p2Scratch = new Cartesian3();
  637. PolygonGeometryLibrary.computeWallGeometry = function (
  638. positions,
  639. textureCoordinates,
  640. ellipsoid,
  641. granularity,
  642. perPositionHeight,
  643. arcType
  644. ) {
  645. let edgePositions;
  646. let topEdgeLength;
  647. let i;
  648. let p1;
  649. let p2;
  650. let t1;
  651. let t2;
  652. let edgeTexcoords;
  653. let topEdgeTexcoordLength;
  654. let length = positions.length;
  655. let index = 0;
  656. let textureIndex = 0;
  657. const hasTexcoords = defined(textureCoordinates);
  658. const texcoords = hasTexcoords ? textureCoordinates.positions : undefined;
  659. if (!perPositionHeight) {
  660. const minDistance = CesiumMath.chordLength(
  661. granularity,
  662. ellipsoid.maximumRadius
  663. );
  664. let numVertices = 0;
  665. if (arcType === ArcType.GEODESIC) {
  666. for (i = 0; i < length; i++) {
  667. numVertices += PolygonGeometryLibrary.subdivideLineCount(
  668. positions[i],
  669. positions[(i + 1) % length],
  670. minDistance
  671. );
  672. }
  673. } else if (arcType === ArcType.RHUMB) {
  674. for (i = 0; i < length; i++) {
  675. numVertices += PolygonGeometryLibrary.subdivideRhumbLineCount(
  676. ellipsoid,
  677. positions[i],
  678. positions[(i + 1) % length],
  679. minDistance
  680. );
  681. }
  682. }
  683. topEdgeLength = (numVertices + length) * 3;
  684. edgePositions = new Array(topEdgeLength * 2);
  685. if (hasTexcoords) {
  686. topEdgeTexcoordLength = (numVertices + length) * 2;
  687. edgeTexcoords = new Array(topEdgeTexcoordLength * 2);
  688. }
  689. for (i = 0; i < length; i++) {
  690. p1 = positions[i];
  691. p2 = positions[(i + 1) % length];
  692. let tempPositions;
  693. let tempTexcoords;
  694. if (hasTexcoords) {
  695. t1 = texcoords[i];
  696. t2 = texcoords[(i + 1) % length];
  697. }
  698. if (arcType === ArcType.GEODESIC) {
  699. tempPositions = PolygonGeometryLibrary.subdivideLine(
  700. p1,
  701. p2,
  702. minDistance,
  703. computeWallIndicesSubdivided
  704. );
  705. if (hasTexcoords) {
  706. tempTexcoords = PolygonGeometryLibrary.subdivideTexcoordLine(
  707. t1,
  708. t2,
  709. p1,
  710. p2,
  711. minDistance,
  712. computeWallTexcoordsSubdivided
  713. );
  714. }
  715. } else if (arcType === ArcType.RHUMB) {
  716. tempPositions = PolygonGeometryLibrary.subdivideRhumbLine(
  717. ellipsoid,
  718. p1,
  719. p2,
  720. minDistance,
  721. computeWallIndicesSubdivided
  722. );
  723. if (hasTexcoords) {
  724. tempTexcoords = PolygonGeometryLibrary.subdivideTexcoordRhumbLine(
  725. t1,
  726. t2,
  727. ellipsoid,
  728. p1,
  729. p2,
  730. minDistance,
  731. computeWallTexcoordsSubdivided
  732. );
  733. }
  734. }
  735. const tempPositionsLength = tempPositions.length;
  736. for (let j = 0; j < tempPositionsLength; ++j, ++index) {
  737. edgePositions[index] = tempPositions[j];
  738. edgePositions[index + topEdgeLength] = tempPositions[j];
  739. }
  740. edgePositions[index] = p2.x;
  741. edgePositions[index + topEdgeLength] = p2.x;
  742. ++index;
  743. edgePositions[index] = p2.y;
  744. edgePositions[index + topEdgeLength] = p2.y;
  745. ++index;
  746. edgePositions[index] = p2.z;
  747. edgePositions[index + topEdgeLength] = p2.z;
  748. ++index;
  749. if (hasTexcoords) {
  750. const tempTexcoordsLength = tempTexcoords.length;
  751. for (let k = 0; k < tempTexcoordsLength; ++k, ++textureIndex) {
  752. edgeTexcoords[textureIndex] = tempTexcoords[k];
  753. edgeTexcoords[textureIndex + topEdgeTexcoordLength] =
  754. tempTexcoords[k];
  755. }
  756. edgeTexcoords[textureIndex] = t2.x;
  757. edgeTexcoords[textureIndex + topEdgeTexcoordLength] = t2.x;
  758. ++textureIndex;
  759. edgeTexcoords[textureIndex] = t2.y;
  760. edgeTexcoords[textureIndex + topEdgeTexcoordLength] = t2.y;
  761. ++textureIndex;
  762. }
  763. }
  764. } else {
  765. topEdgeLength = length * 3 * 2;
  766. edgePositions = new Array(topEdgeLength * 2);
  767. if (hasTexcoords) {
  768. topEdgeTexcoordLength = length * 2 * 2;
  769. edgeTexcoords = new Array(topEdgeTexcoordLength * 2);
  770. }
  771. for (i = 0; i < length; i++) {
  772. p1 = positions[i];
  773. p2 = positions[(i + 1) % length];
  774. edgePositions[index] = edgePositions[index + topEdgeLength] = p1.x;
  775. ++index;
  776. edgePositions[index] = edgePositions[index + topEdgeLength] = p1.y;
  777. ++index;
  778. edgePositions[index] = edgePositions[index + topEdgeLength] = p1.z;
  779. ++index;
  780. edgePositions[index] = edgePositions[index + topEdgeLength] = p2.x;
  781. ++index;
  782. edgePositions[index] = edgePositions[index + topEdgeLength] = p2.y;
  783. ++index;
  784. edgePositions[index] = edgePositions[index + topEdgeLength] = p2.z;
  785. ++index;
  786. if (hasTexcoords) {
  787. t1 = texcoords[i];
  788. t2 = texcoords[(i + 1) % length];
  789. edgeTexcoords[textureIndex] = edgeTexcoords[
  790. textureIndex + topEdgeTexcoordLength
  791. ] = t1.x;
  792. ++textureIndex;
  793. edgeTexcoords[textureIndex] = edgeTexcoords[
  794. textureIndex + topEdgeTexcoordLength
  795. ] = t1.y;
  796. ++textureIndex;
  797. edgeTexcoords[textureIndex] = edgeTexcoords[
  798. textureIndex + topEdgeTexcoordLength
  799. ] = t2.x;
  800. ++textureIndex;
  801. edgeTexcoords[textureIndex] = edgeTexcoords[
  802. textureIndex + topEdgeTexcoordLength
  803. ] = t2.y;
  804. ++textureIndex;
  805. }
  806. }
  807. }
  808. length = edgePositions.length;
  809. const indices = IndexDatatype.createTypedArray(
  810. length / 3,
  811. length - positions.length * 6
  812. );
  813. let edgeIndex = 0;
  814. length /= 6;
  815. for (i = 0; i < length; i++) {
  816. const UL = i;
  817. const UR = UL + 1;
  818. const LL = UL + length;
  819. const LR = LL + 1;
  820. p1 = Cartesian3.fromArray(edgePositions, UL * 3, p1Scratch);
  821. p2 = Cartesian3.fromArray(edgePositions, UR * 3, p2Scratch);
  822. if (
  823. Cartesian3.equalsEpsilon(
  824. p1,
  825. p2,
  826. CesiumMath.EPSILON10,
  827. CesiumMath.EPSILON10
  828. )
  829. ) {
  830. //skip corner
  831. continue;
  832. }
  833. indices[edgeIndex++] = UL;
  834. indices[edgeIndex++] = LL;
  835. indices[edgeIndex++] = UR;
  836. indices[edgeIndex++] = UR;
  837. indices[edgeIndex++] = LL;
  838. indices[edgeIndex++] = LR;
  839. }
  840. const geometryOptions = {
  841. attributes: new GeometryAttributes({
  842. position: new GeometryAttribute({
  843. componentDatatype: ComponentDatatype.DOUBLE,
  844. componentsPerAttribute: 3,
  845. values: edgePositions,
  846. }),
  847. }),
  848. indices: indices,
  849. primitiveType: PrimitiveType.TRIANGLES,
  850. };
  851. if (hasTexcoords) {
  852. geometryOptions.attributes.st = new GeometryAttribute({
  853. componentDatatype: ComponentDatatype.FLOAT,
  854. componentsPerAttribute: 2,
  855. values: edgeTexcoords,
  856. });
  857. }
  858. const geometry = new Geometry(geometryOptions);
  859. return geometry;
  860. };
  861. export default PolygonGeometryLibrary;