PolylineVolumeOutlineGeometry.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import arrayRemoveDuplicates from "./arrayRemoveDuplicates.js";
  2. import BoundingRectangle from "./BoundingRectangle.js";
  3. import BoundingSphere from "./BoundingSphere.js";
  4. import Cartesian2 from "./Cartesian2.js";
  5. import Cartesian3 from "./Cartesian3.js";
  6. import ComponentDatatype from "./ComponentDatatype.js";
  7. import CornerType from "./CornerType.js";
  8. import defaultValue from "./defaultValue.js";
  9. import defined from "./defined.js";
  10. import DeveloperError from "./DeveloperError.js";
  11. import Ellipsoid from "./Ellipsoid.js";
  12. import Geometry from "./Geometry.js";
  13. import GeometryAttribute from "./GeometryAttribute.js";
  14. import GeometryAttributes from "./GeometryAttributes.js";
  15. import IndexDatatype from "./IndexDatatype.js";
  16. import CesiumMath from "./Math.js";
  17. import PolygonPipeline from "./PolygonPipeline.js";
  18. import PolylineVolumeGeometryLibrary from "./PolylineVolumeGeometryLibrary.js";
  19. import PrimitiveType from "./PrimitiveType.js";
  20. import WindingOrder from "./WindingOrder.js";
  21. function computeAttributes(positions, shape) {
  22. const attributes = new GeometryAttributes();
  23. attributes.position = new GeometryAttribute({
  24. componentDatatype: ComponentDatatype.DOUBLE,
  25. componentsPerAttribute: 3,
  26. values: positions,
  27. });
  28. const shapeLength = shape.length;
  29. const vertexCount = attributes.position.values.length / 3;
  30. const positionLength = positions.length / 3;
  31. const shapeCount = positionLength / shapeLength;
  32. const indices = IndexDatatype.createTypedArray(
  33. vertexCount,
  34. 2 * shapeLength * (shapeCount + 1)
  35. );
  36. let i, j;
  37. let index = 0;
  38. i = 0;
  39. let offset = i * shapeLength;
  40. for (j = 0; j < shapeLength - 1; j++) {
  41. indices[index++] = j + offset;
  42. indices[index++] = j + offset + 1;
  43. }
  44. indices[index++] = shapeLength - 1 + offset;
  45. indices[index++] = offset;
  46. i = shapeCount - 1;
  47. offset = i * shapeLength;
  48. for (j = 0; j < shapeLength - 1; j++) {
  49. indices[index++] = j + offset;
  50. indices[index++] = j + offset + 1;
  51. }
  52. indices[index++] = shapeLength - 1 + offset;
  53. indices[index++] = offset;
  54. for (i = 0; i < shapeCount - 1; i++) {
  55. const firstOffset = shapeLength * i;
  56. const secondOffset = firstOffset + shapeLength;
  57. for (j = 0; j < shapeLength; j++) {
  58. indices[index++] = j + firstOffset;
  59. indices[index++] = j + secondOffset;
  60. }
  61. }
  62. const geometry = new Geometry({
  63. attributes: attributes,
  64. indices: IndexDatatype.createTypedArray(vertexCount, indices),
  65. boundingSphere: BoundingSphere.fromVertices(positions),
  66. primitiveType: PrimitiveType.LINES,
  67. });
  68. return geometry;
  69. }
  70. /**
  71. * A description of a polyline with a volume (a 2D shape extruded along a polyline).
  72. *
  73. * @alias PolylineVolumeOutlineGeometry
  74. * @constructor
  75. *
  76. * @param {Object} options Object with the following properties:
  77. * @param {Cartesian3[]} options.polylinePositions An array of positions that define the center of the polyline volume.
  78. * @param {Cartesian2[]} options.shapePositions An array of positions that define the shape to be extruded along the polyline
  79. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference.
  80. * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
  81. * @param {CornerType} [options.cornerType=CornerType.ROUNDED] Determines the style of the corners.
  82. *
  83. * @see PolylineVolumeOutlineGeometry#createGeometry
  84. *
  85. * @example
  86. * function computeCircle(radius) {
  87. * const positions = [];
  88. * for (let i = 0; i < 360; i++) {
  89. * const radians = Cesium.Math.toRadians(i);
  90. * positions.push(new Cesium.Cartesian2(radius * Math.cos(radians), radius * Math.sin(radians)));
  91. * }
  92. * return positions;
  93. * }
  94. *
  95. * const volumeOutline = new Cesium.PolylineVolumeOutlineGeometry({
  96. * polylinePositions : Cesium.Cartesian3.fromDegreesArray([
  97. * -72.0, 40.0,
  98. * -70.0, 35.0
  99. * ]),
  100. * shapePositions : computeCircle(100000.0)
  101. * });
  102. */
  103. function PolylineVolumeOutlineGeometry(options) {
  104. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  105. const positions = options.polylinePositions;
  106. const shape = options.shapePositions;
  107. //>>includeStart('debug', pragmas.debug);
  108. if (!defined(positions)) {
  109. throw new DeveloperError("options.polylinePositions is required.");
  110. }
  111. if (!defined(shape)) {
  112. throw new DeveloperError("options.shapePositions is required.");
  113. }
  114. //>>includeEnd('debug');
  115. this._positions = positions;
  116. this._shape = shape;
  117. this._ellipsoid = Ellipsoid.clone(
  118. defaultValue(options.ellipsoid, Ellipsoid.WGS84)
  119. );
  120. this._cornerType = defaultValue(options.cornerType, CornerType.ROUNDED);
  121. this._granularity = defaultValue(
  122. options.granularity,
  123. CesiumMath.RADIANS_PER_DEGREE
  124. );
  125. this._workerName = "createPolylineVolumeOutlineGeometry";
  126. let numComponents = 1 + positions.length * Cartesian3.packedLength;
  127. numComponents += 1 + shape.length * Cartesian2.packedLength;
  128. /**
  129. * The number of elements used to pack the object into an array.
  130. * @type {Number}
  131. */
  132. this.packedLength = numComponents + Ellipsoid.packedLength + 2;
  133. }
  134. /**
  135. * Stores the provided instance into the provided array.
  136. *
  137. * @param {PolylineVolumeOutlineGeometry} value The value to pack.
  138. * @param {Number[]} array The array to pack into.
  139. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements.
  140. *
  141. * @returns {Number[]} The array that was packed into
  142. */
  143. PolylineVolumeOutlineGeometry.pack = function (value, array, startingIndex) {
  144. //>>includeStart('debug', pragmas.debug);
  145. if (!defined(value)) {
  146. throw new DeveloperError("value is required");
  147. }
  148. if (!defined(array)) {
  149. throw new DeveloperError("array is required");
  150. }
  151. //>>includeEnd('debug');
  152. startingIndex = defaultValue(startingIndex, 0);
  153. let i;
  154. const positions = value._positions;
  155. let length = positions.length;
  156. array[startingIndex++] = length;
  157. for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) {
  158. Cartesian3.pack(positions[i], array, startingIndex);
  159. }
  160. const shape = value._shape;
  161. length = shape.length;
  162. array[startingIndex++] = length;
  163. for (i = 0; i < length; ++i, startingIndex += Cartesian2.packedLength) {
  164. Cartesian2.pack(shape[i], array, startingIndex);
  165. }
  166. Ellipsoid.pack(value._ellipsoid, array, startingIndex);
  167. startingIndex += Ellipsoid.packedLength;
  168. array[startingIndex++] = value._cornerType;
  169. array[startingIndex] = value._granularity;
  170. return array;
  171. };
  172. const scratchEllipsoid = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE);
  173. const scratchOptions = {
  174. polylinePositions: undefined,
  175. shapePositions: undefined,
  176. ellipsoid: scratchEllipsoid,
  177. height: undefined,
  178. cornerType: undefined,
  179. granularity: undefined,
  180. };
  181. /**
  182. * Retrieves an instance from a packed array.
  183. *
  184. * @param {Number[]} array The packed array.
  185. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked.
  186. * @param {PolylineVolumeOutlineGeometry} [result] The object into which to store the result.
  187. * @returns {PolylineVolumeOutlineGeometry} The modified result parameter or a new PolylineVolumeOutlineGeometry instance if one was not provided.
  188. */
  189. PolylineVolumeOutlineGeometry.unpack = function (array, startingIndex, result) {
  190. //>>includeStart('debug', pragmas.debug);
  191. if (!defined(array)) {
  192. throw new DeveloperError("array is required");
  193. }
  194. //>>includeEnd('debug');
  195. startingIndex = defaultValue(startingIndex, 0);
  196. let i;
  197. let length = array[startingIndex++];
  198. const positions = new Array(length);
  199. for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) {
  200. positions[i] = Cartesian3.unpack(array, startingIndex);
  201. }
  202. length = array[startingIndex++];
  203. const shape = new Array(length);
  204. for (i = 0; i < length; ++i, startingIndex += Cartesian2.packedLength) {
  205. shape[i] = Cartesian2.unpack(array, startingIndex);
  206. }
  207. const ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid);
  208. startingIndex += Ellipsoid.packedLength;
  209. const cornerType = array[startingIndex++];
  210. const granularity = array[startingIndex];
  211. if (!defined(result)) {
  212. scratchOptions.polylinePositions = positions;
  213. scratchOptions.shapePositions = shape;
  214. scratchOptions.cornerType = cornerType;
  215. scratchOptions.granularity = granularity;
  216. return new PolylineVolumeOutlineGeometry(scratchOptions);
  217. }
  218. result._positions = positions;
  219. result._shape = shape;
  220. result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid);
  221. result._cornerType = cornerType;
  222. result._granularity = granularity;
  223. return result;
  224. };
  225. const brScratch = new BoundingRectangle();
  226. /**
  227. * Computes the geometric representation of the outline of a polyline with a volume, including its vertices, indices, and a bounding sphere.
  228. *
  229. * @param {PolylineVolumeOutlineGeometry} polylineVolumeOutlineGeometry A description of the polyline volume outline.
  230. * @returns {Geometry|undefined} The computed vertices and indices.
  231. */
  232. PolylineVolumeOutlineGeometry.createGeometry = function (
  233. polylineVolumeOutlineGeometry
  234. ) {
  235. const positions = polylineVolumeOutlineGeometry._positions;
  236. const cleanPositions = arrayRemoveDuplicates(
  237. positions,
  238. Cartesian3.equalsEpsilon
  239. );
  240. let shape2D = polylineVolumeOutlineGeometry._shape;
  241. shape2D = PolylineVolumeGeometryLibrary.removeDuplicatesFromShape(shape2D);
  242. if (cleanPositions.length < 2 || shape2D.length < 3) {
  243. return undefined;
  244. }
  245. if (
  246. PolygonPipeline.computeWindingOrder2D(shape2D) === WindingOrder.CLOCKWISE
  247. ) {
  248. shape2D.reverse();
  249. }
  250. const boundingRectangle = BoundingRectangle.fromPoints(shape2D, brScratch);
  251. const computedPositions = PolylineVolumeGeometryLibrary.computePositions(
  252. cleanPositions,
  253. shape2D,
  254. boundingRectangle,
  255. polylineVolumeOutlineGeometry,
  256. false
  257. );
  258. return computeAttributes(computedPositions, shape2D);
  259. };
  260. export default PolylineVolumeOutlineGeometry;