PolylineVolumeGeometryLibrary.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. import Cartesian2 from "./Cartesian2.js";
  2. import Cartesian3 from "./Cartesian3.js";
  3. import Cartesian4 from "./Cartesian4.js";
  4. import Cartographic from "./Cartographic.js";
  5. import CornerType from "./CornerType.js";
  6. import EllipsoidTangentPlane from "./EllipsoidTangentPlane.js";
  7. import CesiumMath from "./Math.js";
  8. import Matrix3 from "./Matrix3.js";
  9. import Matrix4 from "./Matrix4.js";
  10. import PolylinePipeline from "./PolylinePipeline.js";
  11. import Quaternion from "./Quaternion.js";
  12. import Transforms from "./Transforms.js";
  13. import oneTimeWarning from "../Core/oneTimeWarning.js";
  14. const scratch2Array = [new Cartesian3(), new Cartesian3()];
  15. const scratchCartesian1 = new Cartesian3();
  16. const scratchCartesian2 = new Cartesian3();
  17. const scratchCartesian3 = new Cartesian3();
  18. const scratchCartesian4 = new Cartesian3();
  19. const scratchCartesian5 = new Cartesian3();
  20. const scratchCartesian6 = new Cartesian3();
  21. const scratchCartesian7 = new Cartesian3();
  22. const scratchCartesian8 = new Cartesian3();
  23. const scratchCartesian9 = new Cartesian3();
  24. const scratch1 = new Cartesian3();
  25. const scratch2 = new Cartesian3();
  26. /**
  27. * @private
  28. */
  29. const PolylineVolumeGeometryLibrary = {};
  30. let cartographic = new Cartographic();
  31. function scaleToSurface(positions, ellipsoid) {
  32. const heights = new Array(positions.length);
  33. for (let i = 0; i < positions.length; i++) {
  34. const pos = positions[i];
  35. cartographic = ellipsoid.cartesianToCartographic(pos, cartographic);
  36. heights[i] = cartographic.height;
  37. positions[i] = ellipsoid.scaleToGeodeticSurface(pos, pos);
  38. }
  39. return heights;
  40. }
  41. function subdivideHeights(points, h0, h1, granularity) {
  42. const p0 = points[0];
  43. const p1 = points[1];
  44. const angleBetween = Cartesian3.angleBetween(p0, p1);
  45. const numPoints = Math.ceil(angleBetween / granularity);
  46. const heights = new Array(numPoints);
  47. let i;
  48. if (h0 === h1) {
  49. for (i = 0; i < numPoints; i++) {
  50. heights[i] = h0;
  51. }
  52. heights.push(h1);
  53. return heights;
  54. }
  55. const dHeight = h1 - h0;
  56. const heightPerVertex = dHeight / numPoints;
  57. for (i = 1; i < numPoints; i++) {
  58. const h = h0 + i * heightPerVertex;
  59. heights[i] = h;
  60. }
  61. heights[0] = h0;
  62. heights.push(h1);
  63. return heights;
  64. }
  65. const nextScratch = new Cartesian3();
  66. const prevScratch = new Cartesian3();
  67. function computeRotationAngle(start, end, position, ellipsoid) {
  68. const tangentPlane = new EllipsoidTangentPlane(position, ellipsoid);
  69. const next = tangentPlane.projectPointOntoPlane(
  70. Cartesian3.add(position, start, nextScratch),
  71. nextScratch
  72. );
  73. const prev = tangentPlane.projectPointOntoPlane(
  74. Cartesian3.add(position, end, prevScratch),
  75. prevScratch
  76. );
  77. const angle = Cartesian2.angleBetween(next, prev);
  78. return prev.x * next.y - prev.y * next.x >= 0.0 ? -angle : angle;
  79. }
  80. const negativeX = new Cartesian3(-1, 0, 0);
  81. let transform = new Matrix4();
  82. const translation = new Matrix4();
  83. let rotationZ = new Matrix3();
  84. const scaleMatrix = Matrix3.IDENTITY.clone();
  85. const westScratch = new Cartesian3();
  86. const finalPosScratch = new Cartesian4();
  87. const heightCartesian = new Cartesian3();
  88. function addPosition(
  89. center,
  90. left,
  91. shape,
  92. finalPositions,
  93. ellipsoid,
  94. height,
  95. xScalar,
  96. repeat
  97. ) {
  98. let west = westScratch;
  99. let finalPosition = finalPosScratch;
  100. transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, transform);
  101. west = Matrix4.multiplyByPointAsVector(transform, negativeX, west);
  102. west = Cartesian3.normalize(west, west);
  103. const angle = computeRotationAngle(west, left, center, ellipsoid);
  104. rotationZ = Matrix3.fromRotationZ(angle, rotationZ);
  105. heightCartesian.z = height;
  106. transform = Matrix4.multiplyTransformation(
  107. transform,
  108. Matrix4.fromRotationTranslation(rotationZ, heightCartesian, translation),
  109. transform
  110. );
  111. const scale = scaleMatrix;
  112. scale[0] = xScalar;
  113. for (let j = 0; j < repeat; j++) {
  114. for (let i = 0; i < shape.length; i += 3) {
  115. finalPosition = Cartesian3.fromArray(shape, i, finalPosition);
  116. finalPosition = Matrix3.multiplyByVector(
  117. scale,
  118. finalPosition,
  119. finalPosition
  120. );
  121. finalPosition = Matrix4.multiplyByPoint(
  122. transform,
  123. finalPosition,
  124. finalPosition
  125. );
  126. finalPositions.push(finalPosition.x, finalPosition.y, finalPosition.z);
  127. }
  128. }
  129. return finalPositions;
  130. }
  131. const centerScratch = new Cartesian3();
  132. function addPositions(
  133. centers,
  134. left,
  135. shape,
  136. finalPositions,
  137. ellipsoid,
  138. heights,
  139. xScalar
  140. ) {
  141. for (let i = 0; i < centers.length; i += 3) {
  142. const center = Cartesian3.fromArray(centers, i, centerScratch);
  143. finalPositions = addPosition(
  144. center,
  145. left,
  146. shape,
  147. finalPositions,
  148. ellipsoid,
  149. heights[i / 3],
  150. xScalar,
  151. 1
  152. );
  153. }
  154. return finalPositions;
  155. }
  156. function convertShapeTo3DDuplicate(shape2D, boundingRectangle) {
  157. //orientate 2D shape to XZ plane center at (0, 0, 0), duplicate points
  158. const length = shape2D.length;
  159. const shape = new Array(length * 6);
  160. let index = 0;
  161. const xOffset = boundingRectangle.x + boundingRectangle.width / 2;
  162. const yOffset = boundingRectangle.y + boundingRectangle.height / 2;
  163. let point = shape2D[0];
  164. shape[index++] = point.x - xOffset;
  165. shape[index++] = 0.0;
  166. shape[index++] = point.y - yOffset;
  167. for (let i = 1; i < length; i++) {
  168. point = shape2D[i];
  169. const x = point.x - xOffset;
  170. const z = point.y - yOffset;
  171. shape[index++] = x;
  172. shape[index++] = 0.0;
  173. shape[index++] = z;
  174. shape[index++] = x;
  175. shape[index++] = 0.0;
  176. shape[index++] = z;
  177. }
  178. point = shape2D[0];
  179. shape[index++] = point.x - xOffset;
  180. shape[index++] = 0.0;
  181. shape[index++] = point.y - yOffset;
  182. return shape;
  183. }
  184. function convertShapeTo3D(shape2D, boundingRectangle) {
  185. //orientate 2D shape to XZ plane center at (0, 0, 0)
  186. const length = shape2D.length;
  187. const shape = new Array(length * 3);
  188. let index = 0;
  189. const xOffset = boundingRectangle.x + boundingRectangle.width / 2;
  190. const yOffset = boundingRectangle.y + boundingRectangle.height / 2;
  191. for (let i = 0; i < length; i++) {
  192. shape[index++] = shape2D[i].x - xOffset;
  193. shape[index++] = 0;
  194. shape[index++] = shape2D[i].y - yOffset;
  195. }
  196. return shape;
  197. }
  198. const quaterion = new Quaternion();
  199. const startPointScratch = new Cartesian3();
  200. const rotMatrix = new Matrix3();
  201. function computeRoundCorner(
  202. pivot,
  203. startPoint,
  204. endPoint,
  205. cornerType,
  206. leftIsOutside,
  207. ellipsoid,
  208. finalPositions,
  209. shape,
  210. height,
  211. duplicatePoints
  212. ) {
  213. const angle = Cartesian3.angleBetween(
  214. Cartesian3.subtract(startPoint, pivot, scratch1),
  215. Cartesian3.subtract(endPoint, pivot, scratch2)
  216. );
  217. const granularity =
  218. cornerType === CornerType.BEVELED
  219. ? 0
  220. : Math.ceil(angle / CesiumMath.toRadians(5));
  221. let m;
  222. if (leftIsOutside) {
  223. m = Matrix3.fromQuaternion(
  224. Quaternion.fromAxisAngle(
  225. Cartesian3.negate(pivot, scratch1),
  226. angle / (granularity + 1),
  227. quaterion
  228. ),
  229. rotMatrix
  230. );
  231. } else {
  232. m = Matrix3.fromQuaternion(
  233. Quaternion.fromAxisAngle(pivot, angle / (granularity + 1), quaterion),
  234. rotMatrix
  235. );
  236. }
  237. let left;
  238. let surfacePoint;
  239. startPoint = Cartesian3.clone(startPoint, startPointScratch);
  240. if (granularity > 0) {
  241. const repeat = duplicatePoints ? 2 : 1;
  242. for (let i = 0; i < granularity; i++) {
  243. startPoint = Matrix3.multiplyByVector(m, startPoint, startPoint);
  244. left = Cartesian3.subtract(startPoint, pivot, scratch1);
  245. left = Cartesian3.normalize(left, left);
  246. if (!leftIsOutside) {
  247. left = Cartesian3.negate(left, left);
  248. }
  249. surfacePoint = ellipsoid.scaleToGeodeticSurface(startPoint, scratch2);
  250. finalPositions = addPosition(
  251. surfacePoint,
  252. left,
  253. shape,
  254. finalPositions,
  255. ellipsoid,
  256. height,
  257. 1,
  258. repeat
  259. );
  260. }
  261. } else {
  262. left = Cartesian3.subtract(startPoint, pivot, scratch1);
  263. left = Cartesian3.normalize(left, left);
  264. if (!leftIsOutside) {
  265. left = Cartesian3.negate(left, left);
  266. }
  267. surfacePoint = ellipsoid.scaleToGeodeticSurface(startPoint, scratch2);
  268. finalPositions = addPosition(
  269. surfacePoint,
  270. left,
  271. shape,
  272. finalPositions,
  273. ellipsoid,
  274. height,
  275. 1,
  276. 1
  277. );
  278. endPoint = Cartesian3.clone(endPoint, startPointScratch);
  279. left = Cartesian3.subtract(endPoint, pivot, scratch1);
  280. left = Cartesian3.normalize(left, left);
  281. if (!leftIsOutside) {
  282. left = Cartesian3.negate(left, left);
  283. }
  284. surfacePoint = ellipsoid.scaleToGeodeticSurface(endPoint, scratch2);
  285. finalPositions = addPosition(
  286. surfacePoint,
  287. left,
  288. shape,
  289. finalPositions,
  290. ellipsoid,
  291. height,
  292. 1,
  293. 1
  294. );
  295. }
  296. return finalPositions;
  297. }
  298. PolylineVolumeGeometryLibrary.removeDuplicatesFromShape = function (
  299. shapePositions
  300. ) {
  301. const length = shapePositions.length;
  302. const cleanedPositions = [];
  303. for (let i0 = length - 1, i1 = 0; i1 < length; i0 = i1++) {
  304. const v0 = shapePositions[i0];
  305. const v1 = shapePositions[i1];
  306. if (!Cartesian2.equals(v0, v1)) {
  307. cleanedPositions.push(v1); // Shallow copy!
  308. }
  309. }
  310. return cleanedPositions;
  311. };
  312. PolylineVolumeGeometryLibrary.angleIsGreaterThanPi = function (
  313. forward,
  314. backward,
  315. position,
  316. ellipsoid
  317. ) {
  318. const tangentPlane = new EllipsoidTangentPlane(position, ellipsoid);
  319. const next = tangentPlane.projectPointOntoPlane(
  320. Cartesian3.add(position, forward, nextScratch),
  321. nextScratch
  322. );
  323. const prev = tangentPlane.projectPointOntoPlane(
  324. Cartesian3.add(position, backward, prevScratch),
  325. prevScratch
  326. );
  327. return prev.x * next.y - prev.y * next.x >= 0.0;
  328. };
  329. const scratchForwardProjection = new Cartesian3();
  330. const scratchBackwardProjection = new Cartesian3();
  331. PolylineVolumeGeometryLibrary.computePositions = function (
  332. positions,
  333. shape2D,
  334. boundingRectangle,
  335. geometry,
  336. duplicatePoints
  337. ) {
  338. const ellipsoid = geometry._ellipsoid;
  339. const heights = scaleToSurface(positions, ellipsoid);
  340. const granularity = geometry._granularity;
  341. const cornerType = geometry._cornerType;
  342. const shapeForSides = duplicatePoints
  343. ? convertShapeTo3DDuplicate(shape2D, boundingRectangle)
  344. : convertShapeTo3D(shape2D, boundingRectangle);
  345. const shapeForEnds = duplicatePoints
  346. ? convertShapeTo3D(shape2D, boundingRectangle)
  347. : undefined;
  348. const heightOffset = boundingRectangle.height / 2;
  349. const width = boundingRectangle.width / 2;
  350. let length = positions.length;
  351. let finalPositions = [];
  352. let ends = duplicatePoints ? [] : undefined;
  353. let forward = scratchCartesian1;
  354. let backward = scratchCartesian2;
  355. let cornerDirection = scratchCartesian3;
  356. let surfaceNormal = scratchCartesian4;
  357. let pivot = scratchCartesian5;
  358. let start = scratchCartesian6;
  359. let end = scratchCartesian7;
  360. let left = scratchCartesian8;
  361. let previousPosition = scratchCartesian9;
  362. let position = positions[0];
  363. let nextPosition = positions[1];
  364. surfaceNormal = ellipsoid.geodeticSurfaceNormal(position, surfaceNormal);
  365. forward = Cartesian3.subtract(nextPosition, position, forward);
  366. forward = Cartesian3.normalize(forward, forward);
  367. left = Cartesian3.cross(surfaceNormal, forward, left);
  368. left = Cartesian3.normalize(left, left);
  369. let h0 = heights[0];
  370. let h1 = heights[1];
  371. if (duplicatePoints) {
  372. ends = addPosition(
  373. position,
  374. left,
  375. shapeForEnds,
  376. ends,
  377. ellipsoid,
  378. h0 + heightOffset,
  379. 1,
  380. 1
  381. );
  382. }
  383. previousPosition = Cartesian3.clone(position, previousPosition);
  384. position = nextPosition;
  385. backward = Cartesian3.negate(forward, backward);
  386. let subdividedHeights;
  387. let subdividedPositions;
  388. for (let i = 1; i < length - 1; i++) {
  389. const repeat = duplicatePoints ? 2 : 1;
  390. nextPosition = positions[i + 1];
  391. if (position.equals(nextPosition)) {
  392. oneTimeWarning(
  393. "Positions are too close and are considered equivalent with rounding error."
  394. );
  395. continue;
  396. }
  397. forward = Cartesian3.subtract(nextPosition, position, forward);
  398. forward = Cartesian3.normalize(forward, forward);
  399. cornerDirection = Cartesian3.add(forward, backward, cornerDirection);
  400. cornerDirection = Cartesian3.normalize(cornerDirection, cornerDirection);
  401. surfaceNormal = ellipsoid.geodeticSurfaceNormal(position, surfaceNormal);
  402. const forwardProjection = Cartesian3.multiplyByScalar(
  403. surfaceNormal,
  404. Cartesian3.dot(forward, surfaceNormal),
  405. scratchForwardProjection
  406. );
  407. Cartesian3.subtract(forward, forwardProjection, forwardProjection);
  408. Cartesian3.normalize(forwardProjection, forwardProjection);
  409. const backwardProjection = Cartesian3.multiplyByScalar(
  410. surfaceNormal,
  411. Cartesian3.dot(backward, surfaceNormal),
  412. scratchBackwardProjection
  413. );
  414. Cartesian3.subtract(backward, backwardProjection, backwardProjection);
  415. Cartesian3.normalize(backwardProjection, backwardProjection);
  416. const doCorner = !CesiumMath.equalsEpsilon(
  417. Math.abs(Cartesian3.dot(forwardProjection, backwardProjection)),
  418. 1.0,
  419. CesiumMath.EPSILON7
  420. );
  421. if (doCorner) {
  422. cornerDirection = Cartesian3.cross(
  423. cornerDirection,
  424. surfaceNormal,
  425. cornerDirection
  426. );
  427. cornerDirection = Cartesian3.cross(
  428. surfaceNormal,
  429. cornerDirection,
  430. cornerDirection
  431. );
  432. cornerDirection = Cartesian3.normalize(cornerDirection, cornerDirection);
  433. const scalar =
  434. 1 /
  435. Math.max(
  436. 0.25,
  437. Cartesian3.magnitude(
  438. Cartesian3.cross(cornerDirection, backward, scratch1)
  439. )
  440. );
  441. const leftIsOutside = PolylineVolumeGeometryLibrary.angleIsGreaterThanPi(
  442. forward,
  443. backward,
  444. position,
  445. ellipsoid
  446. );
  447. if (leftIsOutside) {
  448. pivot = Cartesian3.add(
  449. position,
  450. Cartesian3.multiplyByScalar(
  451. cornerDirection,
  452. scalar * width,
  453. cornerDirection
  454. ),
  455. pivot
  456. );
  457. start = Cartesian3.add(
  458. pivot,
  459. Cartesian3.multiplyByScalar(left, width, start),
  460. start
  461. );
  462. scratch2Array[0] = Cartesian3.clone(previousPosition, scratch2Array[0]);
  463. scratch2Array[1] = Cartesian3.clone(start, scratch2Array[1]);
  464. subdividedHeights = subdivideHeights(
  465. scratch2Array,
  466. h0 + heightOffset,
  467. h1 + heightOffset,
  468. granularity
  469. );
  470. subdividedPositions = PolylinePipeline.generateArc({
  471. positions: scratch2Array,
  472. granularity: granularity,
  473. ellipsoid: ellipsoid,
  474. });
  475. finalPositions = addPositions(
  476. subdividedPositions,
  477. left,
  478. shapeForSides,
  479. finalPositions,
  480. ellipsoid,
  481. subdividedHeights,
  482. 1
  483. );
  484. left = Cartesian3.cross(surfaceNormal, forward, left);
  485. left = Cartesian3.normalize(left, left);
  486. end = Cartesian3.add(
  487. pivot,
  488. Cartesian3.multiplyByScalar(left, width, end),
  489. end
  490. );
  491. if (
  492. cornerType === CornerType.ROUNDED ||
  493. cornerType === CornerType.BEVELED
  494. ) {
  495. computeRoundCorner(
  496. pivot,
  497. start,
  498. end,
  499. cornerType,
  500. leftIsOutside,
  501. ellipsoid,
  502. finalPositions,
  503. shapeForSides,
  504. h1 + heightOffset,
  505. duplicatePoints
  506. );
  507. } else {
  508. cornerDirection = Cartesian3.negate(cornerDirection, cornerDirection);
  509. finalPositions = addPosition(
  510. position,
  511. cornerDirection,
  512. shapeForSides,
  513. finalPositions,
  514. ellipsoid,
  515. h1 + heightOffset,
  516. scalar,
  517. repeat
  518. );
  519. }
  520. previousPosition = Cartesian3.clone(end, previousPosition);
  521. } else {
  522. pivot = Cartesian3.add(
  523. position,
  524. Cartesian3.multiplyByScalar(
  525. cornerDirection,
  526. scalar * width,
  527. cornerDirection
  528. ),
  529. pivot
  530. );
  531. start = Cartesian3.add(
  532. pivot,
  533. Cartesian3.multiplyByScalar(left, -width, start),
  534. start
  535. );
  536. scratch2Array[0] = Cartesian3.clone(previousPosition, scratch2Array[0]);
  537. scratch2Array[1] = Cartesian3.clone(start, scratch2Array[1]);
  538. subdividedHeights = subdivideHeights(
  539. scratch2Array,
  540. h0 + heightOffset,
  541. h1 + heightOffset,
  542. granularity
  543. );
  544. subdividedPositions = PolylinePipeline.generateArc({
  545. positions: scratch2Array,
  546. granularity: granularity,
  547. ellipsoid: ellipsoid,
  548. });
  549. finalPositions = addPositions(
  550. subdividedPositions,
  551. left,
  552. shapeForSides,
  553. finalPositions,
  554. ellipsoid,
  555. subdividedHeights,
  556. 1
  557. );
  558. left = Cartesian3.cross(surfaceNormal, forward, left);
  559. left = Cartesian3.normalize(left, left);
  560. end = Cartesian3.add(
  561. pivot,
  562. Cartesian3.multiplyByScalar(left, -width, end),
  563. end
  564. );
  565. if (
  566. cornerType === CornerType.ROUNDED ||
  567. cornerType === CornerType.BEVELED
  568. ) {
  569. computeRoundCorner(
  570. pivot,
  571. start,
  572. end,
  573. cornerType,
  574. leftIsOutside,
  575. ellipsoid,
  576. finalPositions,
  577. shapeForSides,
  578. h1 + heightOffset,
  579. duplicatePoints
  580. );
  581. } else {
  582. finalPositions = addPosition(
  583. position,
  584. cornerDirection,
  585. shapeForSides,
  586. finalPositions,
  587. ellipsoid,
  588. h1 + heightOffset,
  589. scalar,
  590. repeat
  591. );
  592. }
  593. previousPosition = Cartesian3.clone(end, previousPosition);
  594. }
  595. backward = Cartesian3.negate(forward, backward);
  596. } else {
  597. finalPositions = addPosition(
  598. previousPosition,
  599. left,
  600. shapeForSides,
  601. finalPositions,
  602. ellipsoid,
  603. h0 + heightOffset,
  604. 1,
  605. 1
  606. );
  607. previousPosition = position;
  608. }
  609. h0 = h1;
  610. h1 = heights[i + 1];
  611. position = nextPosition;
  612. }
  613. scratch2Array[0] = Cartesian3.clone(previousPosition, scratch2Array[0]);
  614. scratch2Array[1] = Cartesian3.clone(position, scratch2Array[1]);
  615. subdividedHeights = subdivideHeights(
  616. scratch2Array,
  617. h0 + heightOffset,
  618. h1 + heightOffset,
  619. granularity
  620. );
  621. subdividedPositions = PolylinePipeline.generateArc({
  622. positions: scratch2Array,
  623. granularity: granularity,
  624. ellipsoid: ellipsoid,
  625. });
  626. finalPositions = addPositions(
  627. subdividedPositions,
  628. left,
  629. shapeForSides,
  630. finalPositions,
  631. ellipsoid,
  632. subdividedHeights,
  633. 1
  634. );
  635. if (duplicatePoints) {
  636. ends = addPosition(
  637. position,
  638. left,
  639. shapeForEnds,
  640. ends,
  641. ellipsoid,
  642. h1 + heightOffset,
  643. 1,
  644. 1
  645. );
  646. }
  647. length = finalPositions.length;
  648. const posLength = duplicatePoints ? length + ends.length : length;
  649. const combinedPositions = new Float64Array(posLength);
  650. combinedPositions.set(finalPositions);
  651. if (duplicatePoints) {
  652. combinedPositions.set(ends, length);
  653. }
  654. return combinedPositions;
  655. };
  656. export default PolylineVolumeGeometryLibrary;