WallOutlineGeometry.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. import BoundingSphere from "./BoundingSphere.js";
  2. import Cartesian3 from "./Cartesian3.js";
  3. import ComponentDatatype from "./ComponentDatatype.js";
  4. import defaultValue from "./defaultValue.js";
  5. import defined from "./defined.js";
  6. import DeveloperError from "./DeveloperError.js";
  7. import Ellipsoid from "./Ellipsoid.js";
  8. import Geometry from "./Geometry.js";
  9. import GeometryAttribute from "./GeometryAttribute.js";
  10. import GeometryAttributes from "./GeometryAttributes.js";
  11. import IndexDatatype from "./IndexDatatype.js";
  12. import CesiumMath from "./Math.js";
  13. import PrimitiveType from "./PrimitiveType.js";
  14. import WallGeometryLibrary from "./WallGeometryLibrary.js";
  15. const scratchCartesian3Position1 = new Cartesian3();
  16. const scratchCartesian3Position2 = new Cartesian3();
  17. /**
  18. * A description of a wall outline. A wall is defined by a series of points,
  19. * which extrude down to the ground. Optionally, they can extrude downwards to a specified height.
  20. *
  21. * @alias WallOutlineGeometry
  22. * @constructor
  23. *
  24. * @param {Object} options Object with the following properties:
  25. * @param {Cartesian3[]} options.positions An array of Cartesian objects, which are the points of the wall.
  26. * @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.
  27. * @param {Number[]} [options.maximumHeights] An array parallel to <code>positions</code> that give the maximum height of the
  28. * wall at <code>positions</code>. If undefined, the height of each position in used.
  29. * @param {Number[]} [options.minimumHeights] An array parallel to <code>positions</code> that give the minimum height of the
  30. * wall at <code>positions</code>. If undefined, the height at each position is 0.0.
  31. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid for coordinate manipulation
  32. *
  33. * @exception {DeveloperError} positions length must be greater than or equal to 2.
  34. * @exception {DeveloperError} positions and maximumHeights must have the same length.
  35. * @exception {DeveloperError} positions and minimumHeights must have the same length.
  36. *
  37. * @see WallGeometry#createGeometry
  38. * @see WallGeometry#fromConstantHeight
  39. *
  40. * @example
  41. * // create a wall outline that spans from ground level to 10000 meters
  42. * const wall = new Cesium.WallOutlineGeometry({
  43. * positions : Cesium.Cartesian3.fromDegreesArrayHeights([
  44. * 19.0, 47.0, 10000.0,
  45. * 19.0, 48.0, 10000.0,
  46. * 20.0, 48.0, 10000.0,
  47. * 20.0, 47.0, 10000.0,
  48. * 19.0, 47.0, 10000.0
  49. * ])
  50. * });
  51. * const geometry = Cesium.WallOutlineGeometry.createGeometry(wall);
  52. */
  53. function WallOutlineGeometry(options) {
  54. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  55. const wallPositions = options.positions;
  56. const maximumHeights = options.maximumHeights;
  57. const minimumHeights = options.minimumHeights;
  58. //>>includeStart('debug', pragmas.debug);
  59. if (!defined(wallPositions)) {
  60. throw new DeveloperError("options.positions is required.");
  61. }
  62. if (
  63. defined(maximumHeights) &&
  64. maximumHeights.length !== wallPositions.length
  65. ) {
  66. throw new DeveloperError(
  67. "options.positions and options.maximumHeights must have the same length."
  68. );
  69. }
  70. if (
  71. defined(minimumHeights) &&
  72. minimumHeights.length !== wallPositions.length
  73. ) {
  74. throw new DeveloperError(
  75. "options.positions and options.minimumHeights must have the same length."
  76. );
  77. }
  78. //>>includeEnd('debug');
  79. const granularity = defaultValue(
  80. options.granularity,
  81. CesiumMath.RADIANS_PER_DEGREE
  82. );
  83. const ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
  84. this._positions = wallPositions;
  85. this._minimumHeights = minimumHeights;
  86. this._maximumHeights = maximumHeights;
  87. this._granularity = granularity;
  88. this._ellipsoid = Ellipsoid.clone(ellipsoid);
  89. this._workerName = "createWallOutlineGeometry";
  90. let numComponents = 1 + wallPositions.length * Cartesian3.packedLength + 2;
  91. if (defined(minimumHeights)) {
  92. numComponents += minimumHeights.length;
  93. }
  94. if (defined(maximumHeights)) {
  95. numComponents += maximumHeights.length;
  96. }
  97. /**
  98. * The number of elements used to pack the object into an array.
  99. * @type {Number}
  100. */
  101. this.packedLength = numComponents + Ellipsoid.packedLength + 1;
  102. }
  103. /**
  104. * Stores the provided instance into the provided array.
  105. *
  106. * @param {WallOutlineGeometry} value The value to pack.
  107. * @param {Number[]} array The array to pack into.
  108. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements.
  109. *
  110. * @returns {Number[]} The array that was packed into
  111. */
  112. WallOutlineGeometry.pack = function (value, array, startingIndex) {
  113. //>>includeStart('debug', pragmas.debug);
  114. if (!defined(value)) {
  115. throw new DeveloperError("value is required");
  116. }
  117. if (!defined(array)) {
  118. throw new DeveloperError("array is required");
  119. }
  120. //>>includeEnd('debug');
  121. startingIndex = defaultValue(startingIndex, 0);
  122. let i;
  123. const positions = value._positions;
  124. let length = positions.length;
  125. array[startingIndex++] = length;
  126. for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) {
  127. Cartesian3.pack(positions[i], array, startingIndex);
  128. }
  129. const minimumHeights = value._minimumHeights;
  130. length = defined(minimumHeights) ? minimumHeights.length : 0;
  131. array[startingIndex++] = length;
  132. if (defined(minimumHeights)) {
  133. for (i = 0; i < length; ++i) {
  134. array[startingIndex++] = minimumHeights[i];
  135. }
  136. }
  137. const maximumHeights = value._maximumHeights;
  138. length = defined(maximumHeights) ? maximumHeights.length : 0;
  139. array[startingIndex++] = length;
  140. if (defined(maximumHeights)) {
  141. for (i = 0; i < length; ++i) {
  142. array[startingIndex++] = maximumHeights[i];
  143. }
  144. }
  145. Ellipsoid.pack(value._ellipsoid, array, startingIndex);
  146. startingIndex += Ellipsoid.packedLength;
  147. array[startingIndex] = value._granularity;
  148. return array;
  149. };
  150. const scratchEllipsoid = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE);
  151. const scratchOptions = {
  152. positions: undefined,
  153. minimumHeights: undefined,
  154. maximumHeights: undefined,
  155. ellipsoid: scratchEllipsoid,
  156. granularity: undefined,
  157. };
  158. /**
  159. * Retrieves an instance from a packed array.
  160. *
  161. * @param {Number[]} array The packed array.
  162. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked.
  163. * @param {WallOutlineGeometry} [result] The object into which to store the result.
  164. * @returns {WallOutlineGeometry} The modified result parameter or a new WallOutlineGeometry instance if one was not provided.
  165. */
  166. WallOutlineGeometry.unpack = function (array, startingIndex, result) {
  167. //>>includeStart('debug', pragmas.debug);
  168. if (!defined(array)) {
  169. throw new DeveloperError("array is required");
  170. }
  171. //>>includeEnd('debug');
  172. startingIndex = defaultValue(startingIndex, 0);
  173. let i;
  174. let length = array[startingIndex++];
  175. const positions = new Array(length);
  176. for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) {
  177. positions[i] = Cartesian3.unpack(array, startingIndex);
  178. }
  179. length = array[startingIndex++];
  180. let minimumHeights;
  181. if (length > 0) {
  182. minimumHeights = new Array(length);
  183. for (i = 0; i < length; ++i) {
  184. minimumHeights[i] = array[startingIndex++];
  185. }
  186. }
  187. length = array[startingIndex++];
  188. let maximumHeights;
  189. if (length > 0) {
  190. maximumHeights = new Array(length);
  191. for (i = 0; i < length; ++i) {
  192. maximumHeights[i] = array[startingIndex++];
  193. }
  194. }
  195. const ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid);
  196. startingIndex += Ellipsoid.packedLength;
  197. const granularity = array[startingIndex];
  198. if (!defined(result)) {
  199. scratchOptions.positions = positions;
  200. scratchOptions.minimumHeights = minimumHeights;
  201. scratchOptions.maximumHeights = maximumHeights;
  202. scratchOptions.granularity = granularity;
  203. return new WallOutlineGeometry(scratchOptions);
  204. }
  205. result._positions = positions;
  206. result._minimumHeights = minimumHeights;
  207. result._maximumHeights = maximumHeights;
  208. result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid);
  209. result._granularity = granularity;
  210. return result;
  211. };
  212. /**
  213. * A description of a walloutline. A wall is defined by a series of points,
  214. * which extrude down to the ground. Optionally, they can extrude downwards to a specified height.
  215. *
  216. * @param {Object} options Object with the following properties:
  217. * @param {Cartesian3[]} options.positions An array of Cartesian objects, which are the points of the wall.
  218. * @param {Number} [options.maximumHeight] A constant that defines the maximum height of the
  219. * wall at <code>positions</code>. If undefined, the height of each position in used.
  220. * @param {Number} [options.minimumHeight] A constant that defines the minimum height of the
  221. * wall at <code>positions</code>. If undefined, the height at each position is 0.0.
  222. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid for coordinate manipulation
  223. * @returns {WallOutlineGeometry}
  224. *
  225. *
  226. * @example
  227. * // create a wall that spans from 10000 meters to 20000 meters
  228. * const wall = Cesium.WallOutlineGeometry.fromConstantHeights({
  229. * positions : Cesium.Cartesian3.fromDegreesArray([
  230. * 19.0, 47.0,
  231. * 19.0, 48.0,
  232. * 20.0, 48.0,
  233. * 20.0, 47.0,
  234. * 19.0, 47.0,
  235. * ]),
  236. * minimumHeight : 20000.0,
  237. * maximumHeight : 10000.0
  238. * });
  239. * const geometry = Cesium.WallOutlineGeometry.createGeometry(wall);
  240. *
  241. * @see WallOutlineGeometry#createGeometry
  242. */
  243. WallOutlineGeometry.fromConstantHeights = function (options) {
  244. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  245. const positions = options.positions;
  246. //>>includeStart('debug', pragmas.debug);
  247. if (!defined(positions)) {
  248. throw new DeveloperError("options.positions is required.");
  249. }
  250. //>>includeEnd('debug');
  251. let minHeights;
  252. let maxHeights;
  253. const min = options.minimumHeight;
  254. const max = options.maximumHeight;
  255. const doMin = defined(min);
  256. const doMax = defined(max);
  257. if (doMin || doMax) {
  258. const length = positions.length;
  259. minHeights = doMin ? new Array(length) : undefined;
  260. maxHeights = doMax ? new Array(length) : undefined;
  261. for (let i = 0; i < length; ++i) {
  262. if (doMin) {
  263. minHeights[i] = min;
  264. }
  265. if (doMax) {
  266. maxHeights[i] = max;
  267. }
  268. }
  269. }
  270. const newOptions = {
  271. positions: positions,
  272. maximumHeights: maxHeights,
  273. minimumHeights: minHeights,
  274. ellipsoid: options.ellipsoid,
  275. };
  276. return new WallOutlineGeometry(newOptions);
  277. };
  278. /**
  279. * Computes the geometric representation of a wall outline, including its vertices, indices, and a bounding sphere.
  280. *
  281. * @param {WallOutlineGeometry} wallGeometry A description of the wall outline.
  282. * @returns {Geometry|undefined} The computed vertices and indices.
  283. */
  284. WallOutlineGeometry.createGeometry = function (wallGeometry) {
  285. const wallPositions = wallGeometry._positions;
  286. const minimumHeights = wallGeometry._minimumHeights;
  287. const maximumHeights = wallGeometry._maximumHeights;
  288. const granularity = wallGeometry._granularity;
  289. const ellipsoid = wallGeometry._ellipsoid;
  290. const pos = WallGeometryLibrary.computePositions(
  291. ellipsoid,
  292. wallPositions,
  293. maximumHeights,
  294. minimumHeights,
  295. granularity,
  296. false
  297. );
  298. if (!defined(pos)) {
  299. return;
  300. }
  301. const bottomPositions = pos.bottomPositions;
  302. const topPositions = pos.topPositions;
  303. let length = topPositions.length;
  304. let size = length * 2;
  305. const positions = new Float64Array(size);
  306. let positionIndex = 0;
  307. // add lower and upper points one after the other, lower
  308. // points being even and upper points being odd
  309. length /= 3;
  310. let i;
  311. for (i = 0; i < length; ++i) {
  312. const i3 = i * 3;
  313. const topPosition = Cartesian3.fromArray(
  314. topPositions,
  315. i3,
  316. scratchCartesian3Position1
  317. );
  318. const bottomPosition = Cartesian3.fromArray(
  319. bottomPositions,
  320. i3,
  321. scratchCartesian3Position2
  322. );
  323. // insert the lower point
  324. positions[positionIndex++] = bottomPosition.x;
  325. positions[positionIndex++] = bottomPosition.y;
  326. positions[positionIndex++] = bottomPosition.z;
  327. // insert the upper point
  328. positions[positionIndex++] = topPosition.x;
  329. positions[positionIndex++] = topPosition.y;
  330. positions[positionIndex++] = topPosition.z;
  331. }
  332. const attributes = new GeometryAttributes({
  333. position: new GeometryAttribute({
  334. componentDatatype: ComponentDatatype.DOUBLE,
  335. componentsPerAttribute: 3,
  336. values: positions,
  337. }),
  338. });
  339. const numVertices = size / 3;
  340. size = 2 * numVertices - 4 + numVertices;
  341. const indices = IndexDatatype.createTypedArray(numVertices, size);
  342. let edgeIndex = 0;
  343. for (i = 0; i < numVertices - 2; i += 2) {
  344. const LL = i;
  345. const LR = i + 2;
  346. const pl = Cartesian3.fromArray(
  347. positions,
  348. LL * 3,
  349. scratchCartesian3Position1
  350. );
  351. const pr = Cartesian3.fromArray(
  352. positions,
  353. LR * 3,
  354. scratchCartesian3Position2
  355. );
  356. if (Cartesian3.equalsEpsilon(pl, pr, CesiumMath.EPSILON10)) {
  357. continue;
  358. }
  359. const UL = i + 1;
  360. const UR = i + 3;
  361. indices[edgeIndex++] = UL;
  362. indices[edgeIndex++] = LL;
  363. indices[edgeIndex++] = UL;
  364. indices[edgeIndex++] = UR;
  365. indices[edgeIndex++] = LL;
  366. indices[edgeIndex++] = LR;
  367. }
  368. indices[edgeIndex++] = numVertices - 2;
  369. indices[edgeIndex++] = numVertices - 1;
  370. return new Geometry({
  371. attributes: attributes,
  372. indices: indices,
  373. primitiveType: PrimitiveType.LINES,
  374. boundingSphere: new BoundingSphere.fromVertices(positions),
  375. });
  376. };
  377. export default WallOutlineGeometry;