PolygonPipeline.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. import earcut from "earcut";
  2. import Cartesian2 from "./Cartesian2.js";
  3. import Cartesian3 from "./Cartesian3.js";
  4. import Cartographic from "./Cartographic.js";
  5. import Check from "./Check.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 CesiumMath from "./Math.js";
  14. import PrimitiveType from "./PrimitiveType.js";
  15. import WindingOrder from "./WindingOrder.js";
  16. const scaleToGeodeticHeightN = new Cartesian3();
  17. const scaleToGeodeticHeightP = new Cartesian3();
  18. /**
  19. * @private
  20. */
  21. const PolygonPipeline = {};
  22. /**
  23. * @exception {DeveloperError} At least three positions are required.
  24. */
  25. PolygonPipeline.computeArea2D = function (positions) {
  26. //>>includeStart('debug', pragmas.debug);
  27. Check.defined("positions", positions);
  28. Check.typeOf.number.greaterThanOrEquals(
  29. "positions.length",
  30. positions.length,
  31. 3
  32. );
  33. //>>includeEnd('debug');
  34. const length = positions.length;
  35. let area = 0.0;
  36. for (let i0 = length - 1, i1 = 0; i1 < length; i0 = i1++) {
  37. const v0 = positions[i0];
  38. const v1 = positions[i1];
  39. area += v0.x * v1.y - v1.x * v0.y;
  40. }
  41. return area * 0.5;
  42. };
  43. /**
  44. * @returns {WindingOrder} The winding order.
  45. *
  46. * @exception {DeveloperError} At least three positions are required.
  47. */
  48. PolygonPipeline.computeWindingOrder2D = function (positions) {
  49. const area = PolygonPipeline.computeArea2D(positions);
  50. return area > 0.0 ? WindingOrder.COUNTER_CLOCKWISE : WindingOrder.CLOCKWISE;
  51. };
  52. /**
  53. * Triangulate a polygon.
  54. *
  55. * @param {Cartesian2[]} positions Cartesian2 array containing the vertices of the polygon
  56. * @param {number[]} [holes] An array of the staring indices of the holes.
  57. * @returns {number[]} Index array representing triangles that fill the polygon
  58. */
  59. PolygonPipeline.triangulate = function (positions, holes) {
  60. //>>includeStart('debug', pragmas.debug);
  61. Check.defined("positions", positions);
  62. //>>includeEnd('debug');
  63. const flattenedPositions = Cartesian2.packArray(positions);
  64. return earcut(flattenedPositions, holes, 2);
  65. };
  66. const subdivisionV0Scratch = new Cartesian3();
  67. const subdivisionV1Scratch = new Cartesian3();
  68. const subdivisionV2Scratch = new Cartesian3();
  69. const subdivisionS0Scratch = new Cartesian3();
  70. const subdivisionS1Scratch = new Cartesian3();
  71. const subdivisionS2Scratch = new Cartesian3();
  72. const subdivisionMidScratch = new Cartesian3();
  73. const subdivisionT0Scratch = new Cartesian2();
  74. const subdivisionT1Scratch = new Cartesian2();
  75. const subdivisionT2Scratch = new Cartesian2();
  76. const subdivisionTexcoordMidScratch = new Cartesian2();
  77. /**
  78. * Subdivides positions and raises points to the surface of the ellipsoid.
  79. *
  80. * @param {Ellipsoid} ellipsoid The ellipsoid the polygon in on.
  81. * @param {Cartesian3[]} positions An array of {@link Cartesian3} positions of the polygon.
  82. * @param {number[]} indices An array of indices that determines the triangles in the polygon.
  83. * @param {Cartesian2[]} texcoords An optional array of {@link Cartesian2} texture coordinates of the polygon.
  84. * @param {number} [granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
  85. *
  86. * @exception {DeveloperError} At least three indices are required.
  87. * @exception {DeveloperError} The number of indices must be divisable by three.
  88. * @exception {DeveloperError} Granularity must be greater than zero.
  89. */
  90. PolygonPipeline.computeSubdivision = function (
  91. ellipsoid,
  92. positions,
  93. indices,
  94. texcoords,
  95. granularity
  96. ) {
  97. granularity = defaultValue(granularity, CesiumMath.RADIANS_PER_DEGREE);
  98. const hasTexcoords = defined(texcoords);
  99. //>>includeStart('debug', pragmas.debug);
  100. Check.typeOf.object("ellipsoid", ellipsoid);
  101. Check.defined("positions", positions);
  102. Check.defined("indices", indices);
  103. Check.typeOf.number.greaterThanOrEquals("indices.length", indices.length, 3);
  104. Check.typeOf.number.equals("indices.length % 3", "0", indices.length % 3, 0);
  105. Check.typeOf.number.greaterThan("granularity", granularity, 0.0);
  106. //>>includeEnd('debug');
  107. // triangles that need (or might need) to be subdivided.
  108. const triangles = indices.slice(0);
  109. // New positions due to edge splits are appended to the positions list.
  110. let i;
  111. const length = positions.length;
  112. const subdividedPositions = new Array(length * 3);
  113. const subdividedTexcoords = new Array(length * 2);
  114. let q = 0;
  115. let p = 0;
  116. for (i = 0; i < length; i++) {
  117. const item = positions[i];
  118. subdividedPositions[q++] = item.x;
  119. subdividedPositions[q++] = item.y;
  120. subdividedPositions[q++] = item.z;
  121. if (hasTexcoords) {
  122. const texcoordItem = texcoords[i];
  123. subdividedTexcoords[p++] = texcoordItem.x;
  124. subdividedTexcoords[p++] = texcoordItem.y;
  125. }
  126. }
  127. const subdividedIndices = [];
  128. // Used to make sure shared edges are not split more than once.
  129. const edges = {};
  130. const radius = ellipsoid.maximumRadius;
  131. const minDistance = CesiumMath.chordLength(granularity, radius);
  132. const minDistanceSqrd = minDistance * minDistance;
  133. while (triangles.length > 0) {
  134. const i2 = triangles.pop();
  135. const i1 = triangles.pop();
  136. const i0 = triangles.pop();
  137. const v0 = Cartesian3.fromArray(
  138. subdividedPositions,
  139. i0 * 3,
  140. subdivisionV0Scratch
  141. );
  142. const v1 = Cartesian3.fromArray(
  143. subdividedPositions,
  144. i1 * 3,
  145. subdivisionV1Scratch
  146. );
  147. const v2 = Cartesian3.fromArray(
  148. subdividedPositions,
  149. i2 * 3,
  150. subdivisionV2Scratch
  151. );
  152. let t0, t1, t2;
  153. if (hasTexcoords) {
  154. t0 = Cartesian2.fromArray(
  155. subdividedTexcoords,
  156. i0 * 2,
  157. subdivisionT0Scratch
  158. );
  159. t1 = Cartesian2.fromArray(
  160. subdividedTexcoords,
  161. i1 * 2,
  162. subdivisionT1Scratch
  163. );
  164. t2 = Cartesian2.fromArray(
  165. subdividedTexcoords,
  166. i2 * 2,
  167. subdivisionT2Scratch
  168. );
  169. }
  170. const s0 = Cartesian3.multiplyByScalar(
  171. Cartesian3.normalize(v0, subdivisionS0Scratch),
  172. radius,
  173. subdivisionS0Scratch
  174. );
  175. const s1 = Cartesian3.multiplyByScalar(
  176. Cartesian3.normalize(v1, subdivisionS1Scratch),
  177. radius,
  178. subdivisionS1Scratch
  179. );
  180. const s2 = Cartesian3.multiplyByScalar(
  181. Cartesian3.normalize(v2, subdivisionS2Scratch),
  182. radius,
  183. subdivisionS2Scratch
  184. );
  185. const g0 = Cartesian3.magnitudeSquared(
  186. Cartesian3.subtract(s0, s1, subdivisionMidScratch)
  187. );
  188. const g1 = Cartesian3.magnitudeSquared(
  189. Cartesian3.subtract(s1, s2, subdivisionMidScratch)
  190. );
  191. const g2 = Cartesian3.magnitudeSquared(
  192. Cartesian3.subtract(s2, s0, subdivisionMidScratch)
  193. );
  194. const max = Math.max(g0, g1, g2);
  195. let edge;
  196. let mid;
  197. let midTexcoord;
  198. // if the max length squared of a triangle edge is greater than the chord length of squared
  199. // of the granularity, subdivide the triangle
  200. if (max > minDistanceSqrd) {
  201. if (g0 === max) {
  202. edge = `${Math.min(i0, i1)} ${Math.max(i0, i1)}`;
  203. i = edges[edge];
  204. if (!defined(i)) {
  205. mid = Cartesian3.add(v0, v1, subdivisionMidScratch);
  206. Cartesian3.multiplyByScalar(mid, 0.5, mid);
  207. subdividedPositions.push(mid.x, mid.y, mid.z);
  208. i = subdividedPositions.length / 3 - 1;
  209. edges[edge] = i;
  210. if (hasTexcoords) {
  211. midTexcoord = Cartesian2.add(t0, t1, subdivisionTexcoordMidScratch);
  212. Cartesian2.multiplyByScalar(midTexcoord, 0.5, midTexcoord);
  213. subdividedTexcoords.push(midTexcoord.x, midTexcoord.y);
  214. }
  215. }
  216. triangles.push(i0, i, i2);
  217. triangles.push(i, i1, i2);
  218. } else if (g1 === max) {
  219. edge = `${Math.min(i1, i2)} ${Math.max(i1, i2)}`;
  220. i = edges[edge];
  221. if (!defined(i)) {
  222. mid = Cartesian3.add(v1, v2, subdivisionMidScratch);
  223. Cartesian3.multiplyByScalar(mid, 0.5, mid);
  224. subdividedPositions.push(mid.x, mid.y, mid.z);
  225. i = subdividedPositions.length / 3 - 1;
  226. edges[edge] = i;
  227. if (hasTexcoords) {
  228. midTexcoord = Cartesian2.add(t1, t2, subdivisionTexcoordMidScratch);
  229. Cartesian2.multiplyByScalar(midTexcoord, 0.5, midTexcoord);
  230. subdividedTexcoords.push(midTexcoord.x, midTexcoord.y);
  231. }
  232. }
  233. triangles.push(i1, i, i0);
  234. triangles.push(i, i2, i0);
  235. } else if (g2 === max) {
  236. edge = `${Math.min(i2, i0)} ${Math.max(i2, i0)}`;
  237. i = edges[edge];
  238. if (!defined(i)) {
  239. mid = Cartesian3.add(v2, v0, subdivisionMidScratch);
  240. Cartesian3.multiplyByScalar(mid, 0.5, mid);
  241. subdividedPositions.push(mid.x, mid.y, mid.z);
  242. i = subdividedPositions.length / 3 - 1;
  243. edges[edge] = i;
  244. if (hasTexcoords) {
  245. midTexcoord = Cartesian2.add(t2, t0, subdivisionTexcoordMidScratch);
  246. Cartesian2.multiplyByScalar(midTexcoord, 0.5, midTexcoord);
  247. subdividedTexcoords.push(midTexcoord.x, midTexcoord.y);
  248. }
  249. }
  250. triangles.push(i2, i, i1);
  251. triangles.push(i, i0, i1);
  252. }
  253. } else {
  254. subdividedIndices.push(i0);
  255. subdividedIndices.push(i1);
  256. subdividedIndices.push(i2);
  257. }
  258. }
  259. const geometryOptions = {
  260. attributes: {
  261. position: new GeometryAttribute({
  262. componentDatatype: ComponentDatatype.DOUBLE,
  263. componentsPerAttribute: 3,
  264. values: subdividedPositions,
  265. }),
  266. },
  267. indices: subdividedIndices,
  268. primitiveType: PrimitiveType.TRIANGLES,
  269. };
  270. if (hasTexcoords) {
  271. geometryOptions.attributes.st = new GeometryAttribute({
  272. componentDatatype: ComponentDatatype.FLOAT,
  273. componentsPerAttribute: 2,
  274. values: subdividedTexcoords,
  275. });
  276. }
  277. return new Geometry(geometryOptions);
  278. };
  279. const subdivisionC0Scratch = new Cartographic();
  280. const subdivisionC1Scratch = new Cartographic();
  281. const subdivisionC2Scratch = new Cartographic();
  282. const subdivisionCartographicScratch = new Cartographic();
  283. /**
  284. * Subdivides positions on rhumb lines and raises points to the surface of the ellipsoid.
  285. *
  286. * @param {Ellipsoid} ellipsoid The ellipsoid the polygon in on.
  287. * @param {Cartesian3[]} positions An array of {@link Cartesian3} positions of the polygon.
  288. * @param {number[]} indices An array of indices that determines the triangles in the polygon.
  289. * @param {Cartesian2[]} texcoords An optional array of {@link Cartesian2} texture coordinates of the polygon.
  290. * @param {number} [granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
  291. *
  292. * @exception {DeveloperError} At least three indices are required.
  293. * @exception {DeveloperError} The number of indices must be divisable by three.
  294. * @exception {DeveloperError} Granularity must be greater than zero.
  295. */
  296. PolygonPipeline.computeRhumbLineSubdivision = function (
  297. ellipsoid,
  298. positions,
  299. indices,
  300. texcoords,
  301. granularity
  302. ) {
  303. granularity = defaultValue(granularity, CesiumMath.RADIANS_PER_DEGREE);
  304. const hasTexcoords = defined(texcoords);
  305. //>>includeStart('debug', pragmas.debug);
  306. Check.typeOf.object("ellipsoid", ellipsoid);
  307. Check.defined("positions", positions);
  308. Check.defined("indices", indices);
  309. Check.typeOf.number.greaterThanOrEquals("indices.length", indices.length, 3);
  310. Check.typeOf.number.equals("indices.length % 3", "0", indices.length % 3, 0);
  311. Check.typeOf.number.greaterThan("granularity", granularity, 0.0);
  312. //>>includeEnd('debug');
  313. // triangles that need (or might need) to be subdivided.
  314. const triangles = indices.slice(0);
  315. // New positions due to edge splits are appended to the positions list.
  316. let i;
  317. const length = positions.length;
  318. const subdividedPositions = new Array(length * 3);
  319. const subdividedTexcoords = new Array(length * 2);
  320. let q = 0;
  321. let p = 0;
  322. for (i = 0; i < length; i++) {
  323. const item = positions[i];
  324. subdividedPositions[q++] = item.x;
  325. subdividedPositions[q++] = item.y;
  326. subdividedPositions[q++] = item.z;
  327. if (hasTexcoords) {
  328. const texcoordItem = texcoords[i];
  329. subdividedTexcoords[p++] = texcoordItem.x;
  330. subdividedTexcoords[p++] = texcoordItem.y;
  331. }
  332. }
  333. const subdividedIndices = [];
  334. // Used to make sure shared edges are not split more than once.
  335. const edges = {};
  336. const radius = ellipsoid.maximumRadius;
  337. const minDistance = CesiumMath.chordLength(granularity, radius);
  338. const rhumb0 = new EllipsoidRhumbLine(undefined, undefined, ellipsoid);
  339. const rhumb1 = new EllipsoidRhumbLine(undefined, undefined, ellipsoid);
  340. const rhumb2 = new EllipsoidRhumbLine(undefined, undefined, ellipsoid);
  341. while (triangles.length > 0) {
  342. const i2 = triangles.pop();
  343. const i1 = triangles.pop();
  344. const i0 = triangles.pop();
  345. const v0 = Cartesian3.fromArray(
  346. subdividedPositions,
  347. i0 * 3,
  348. subdivisionV0Scratch
  349. );
  350. const v1 = Cartesian3.fromArray(
  351. subdividedPositions,
  352. i1 * 3,
  353. subdivisionV1Scratch
  354. );
  355. const v2 = Cartesian3.fromArray(
  356. subdividedPositions,
  357. i2 * 3,
  358. subdivisionV2Scratch
  359. );
  360. let t0, t1, t2;
  361. if (hasTexcoords) {
  362. t0 = Cartesian2.fromArray(
  363. subdividedTexcoords,
  364. i0 * 2,
  365. subdivisionT0Scratch
  366. );
  367. t1 = Cartesian2.fromArray(
  368. subdividedTexcoords,
  369. i1 * 2,
  370. subdivisionT1Scratch
  371. );
  372. t2 = Cartesian2.fromArray(
  373. subdividedTexcoords,
  374. i2 * 2,
  375. subdivisionT2Scratch
  376. );
  377. }
  378. const c0 = ellipsoid.cartesianToCartographic(v0, subdivisionC0Scratch);
  379. const c1 = ellipsoid.cartesianToCartographic(v1, subdivisionC1Scratch);
  380. const c2 = ellipsoid.cartesianToCartographic(v2, subdivisionC2Scratch);
  381. rhumb0.setEndPoints(c0, c1);
  382. const g0 = rhumb0.surfaceDistance;
  383. rhumb1.setEndPoints(c1, c2);
  384. const g1 = rhumb1.surfaceDistance;
  385. rhumb2.setEndPoints(c2, c0);
  386. const g2 = rhumb2.surfaceDistance;
  387. const max = Math.max(g0, g1, g2);
  388. let edge;
  389. let mid;
  390. let midHeight;
  391. let midCartesian3;
  392. let midTexcoord;
  393. // if the max length squared of a triangle edge is greater than granularity, subdivide the triangle
  394. if (max > minDistance) {
  395. if (g0 === max) {
  396. edge = `${Math.min(i0, i1)} ${Math.max(i0, i1)}`;
  397. i = edges[edge];
  398. if (!defined(i)) {
  399. mid = rhumb0.interpolateUsingFraction(
  400. 0.5,
  401. subdivisionCartographicScratch
  402. );
  403. midHeight = (c0.height + c1.height) * 0.5;
  404. midCartesian3 = Cartesian3.fromRadians(
  405. mid.longitude,
  406. mid.latitude,
  407. midHeight,
  408. ellipsoid,
  409. subdivisionMidScratch
  410. );
  411. subdividedPositions.push(
  412. midCartesian3.x,
  413. midCartesian3.y,
  414. midCartesian3.z
  415. );
  416. i = subdividedPositions.length / 3 - 1;
  417. edges[edge] = i;
  418. if (hasTexcoords) {
  419. midTexcoord = Cartesian2.add(t0, t1, subdivisionTexcoordMidScratch);
  420. Cartesian2.multiplyByScalar(midTexcoord, 0.5, midTexcoord);
  421. subdividedTexcoords.push(midTexcoord.x, midTexcoord.y);
  422. }
  423. }
  424. triangles.push(i0, i, i2);
  425. triangles.push(i, i1, i2);
  426. } else if (g1 === max) {
  427. edge = `${Math.min(i1, i2)} ${Math.max(i1, i2)}`;
  428. i = edges[edge];
  429. if (!defined(i)) {
  430. mid = rhumb1.interpolateUsingFraction(
  431. 0.5,
  432. subdivisionCartographicScratch
  433. );
  434. midHeight = (c1.height + c2.height) * 0.5;
  435. midCartesian3 = Cartesian3.fromRadians(
  436. mid.longitude,
  437. mid.latitude,
  438. midHeight,
  439. ellipsoid,
  440. subdivisionMidScratch
  441. );
  442. subdividedPositions.push(
  443. midCartesian3.x,
  444. midCartesian3.y,
  445. midCartesian3.z
  446. );
  447. i = subdividedPositions.length / 3 - 1;
  448. edges[edge] = i;
  449. if (hasTexcoords) {
  450. midTexcoord = Cartesian2.add(t1, t2, subdivisionTexcoordMidScratch);
  451. Cartesian2.multiplyByScalar(midTexcoord, 0.5, midTexcoord);
  452. subdividedTexcoords.push(midTexcoord.x, midTexcoord.y);
  453. }
  454. }
  455. triangles.push(i1, i, i0);
  456. triangles.push(i, i2, i0);
  457. } else if (g2 === max) {
  458. edge = `${Math.min(i2, i0)} ${Math.max(i2, i0)}`;
  459. i = edges[edge];
  460. if (!defined(i)) {
  461. mid = rhumb2.interpolateUsingFraction(
  462. 0.5,
  463. subdivisionCartographicScratch
  464. );
  465. midHeight = (c2.height + c0.height) * 0.5;
  466. midCartesian3 = Cartesian3.fromRadians(
  467. mid.longitude,
  468. mid.latitude,
  469. midHeight,
  470. ellipsoid,
  471. subdivisionMidScratch
  472. );
  473. subdividedPositions.push(
  474. midCartesian3.x,
  475. midCartesian3.y,
  476. midCartesian3.z
  477. );
  478. i = subdividedPositions.length / 3 - 1;
  479. edges[edge] = i;
  480. if (hasTexcoords) {
  481. midTexcoord = Cartesian2.add(t2, t0, subdivisionTexcoordMidScratch);
  482. Cartesian2.multiplyByScalar(midTexcoord, 0.5, midTexcoord);
  483. subdividedTexcoords.push(midTexcoord.x, midTexcoord.y);
  484. }
  485. }
  486. triangles.push(i2, i, i1);
  487. triangles.push(i, i0, i1);
  488. }
  489. } else {
  490. subdividedIndices.push(i0);
  491. subdividedIndices.push(i1);
  492. subdividedIndices.push(i2);
  493. }
  494. }
  495. const geometryOptions = {
  496. attributes: {
  497. position: new GeometryAttribute({
  498. componentDatatype: ComponentDatatype.DOUBLE,
  499. componentsPerAttribute: 3,
  500. values: subdividedPositions,
  501. }),
  502. },
  503. indices: subdividedIndices,
  504. primitiveType: PrimitiveType.TRIANGLES,
  505. };
  506. if (hasTexcoords) {
  507. geometryOptions.attributes.st = new GeometryAttribute({
  508. componentDatatype: ComponentDatatype.FLOAT,
  509. componentsPerAttribute: 2,
  510. values: subdividedTexcoords,
  511. });
  512. }
  513. return new Geometry(geometryOptions);
  514. };
  515. /**
  516. * Scales each position of a geometry's position attribute to a height, in place.
  517. *
  518. * @param {number[]} positions The array of numbers representing the positions to be scaled
  519. * @param {number} [height=0.0] The desired height to add to the positions
  520. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the positions lie.
  521. * @param {boolean} [scaleToSurface=true] <code>true</code> if the positions need to be scaled to the surface before the height is added.
  522. * @returns {number[]} The input array of positions, scaled to height
  523. */
  524. PolygonPipeline.scaleToGeodeticHeight = function (
  525. positions,
  526. height,
  527. ellipsoid,
  528. scaleToSurface
  529. ) {
  530. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  531. let n = scaleToGeodeticHeightN;
  532. let p = scaleToGeodeticHeightP;
  533. height = defaultValue(height, 0.0);
  534. scaleToSurface = defaultValue(scaleToSurface, true);
  535. if (defined(positions)) {
  536. const length = positions.length;
  537. for (let i = 0; i < length; i += 3) {
  538. Cartesian3.fromArray(positions, i, p);
  539. if (scaleToSurface) {
  540. p = ellipsoid.scaleToGeodeticSurface(p, p);
  541. }
  542. if (height !== 0) {
  543. n = ellipsoid.geodeticSurfaceNormal(p, n);
  544. Cartesian3.multiplyByScalar(n, height, n);
  545. Cartesian3.add(p, n, p);
  546. }
  547. positions[i] = p.x;
  548. positions[i + 1] = p.y;
  549. positions[i + 2] = p.z;
  550. }
  551. }
  552. return positions;
  553. };
  554. export default PolygonPipeline;