CorridorOutlineGeometry.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. import arrayFill from './arrayFill.js';
  2. import arrayRemoveDuplicates from './arrayRemoveDuplicates.js';
  3. import BoundingSphere from './BoundingSphere.js';
  4. import Cartesian3 from './Cartesian3.js';
  5. import Check from './Check.js';
  6. import ComponentDatatype from './ComponentDatatype.js';
  7. import CornerType from './CornerType.js';
  8. import CorridorGeometryLibrary from './CorridorGeometryLibrary.js';
  9. import defaultValue from './defaultValue.js';
  10. import defined from './defined.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 GeometryOffsetAttribute from './GeometryOffsetAttribute.js';
  16. import IndexDatatype from './IndexDatatype.js';
  17. import CesiumMath from './Math.js';
  18. import PolygonPipeline from './PolygonPipeline.js';
  19. import PrimitiveType from './PrimitiveType.js';
  20. var cartesian1 = new Cartesian3();
  21. var cartesian2 = new Cartesian3();
  22. var cartesian3 = new Cartesian3();
  23. function scaleToSurface(positions, ellipsoid) {
  24. for (var i = 0; i < positions.length; i++) {
  25. positions[i] = ellipsoid.scaleToGeodeticSurface(positions[i], positions[i]);
  26. }
  27. return positions;
  28. }
  29. function combine(computedPositions, cornerType) {
  30. var wallIndices = [];
  31. var positions = computedPositions.positions;
  32. var corners = computedPositions.corners;
  33. var endPositions = computedPositions.endPositions;
  34. var attributes = new GeometryAttributes();
  35. var corner;
  36. var leftCount = 0;
  37. var rightCount = 0;
  38. var i;
  39. var indicesLength = 0;
  40. var length;
  41. for (i = 0; i < positions.length; i += 2) {
  42. length = positions[i].length - 3;
  43. leftCount += length; //subtracting 3 to account for duplicate points at corners
  44. indicesLength += length / 3 * 4;
  45. rightCount += positions[i + 1].length - 3;
  46. }
  47. leftCount += 3; //add back count for end positions
  48. rightCount += 3;
  49. for (i = 0; i < corners.length; i++) {
  50. corner = corners[i];
  51. var leftSide = corners[i].leftPositions;
  52. if (defined(leftSide)) {
  53. length = leftSide.length;
  54. leftCount += length;
  55. indicesLength += length / 3 * 2;
  56. } else {
  57. length = corners[i].rightPositions.length;
  58. rightCount += length;
  59. indicesLength += length / 3 * 2;
  60. }
  61. }
  62. var addEndPositions = defined(endPositions);
  63. var endPositionLength;
  64. if (addEndPositions) {
  65. endPositionLength = endPositions[0].length - 3;
  66. leftCount += endPositionLength;
  67. rightCount += endPositionLength;
  68. endPositionLength /= 3;
  69. indicesLength += endPositionLength * 4;
  70. }
  71. var size = leftCount + rightCount;
  72. var finalPositions = new Float64Array(size);
  73. var front = 0;
  74. var back = size - 1;
  75. var UL, LL, UR, LR;
  76. var rightPos, leftPos;
  77. var halfLength = endPositionLength / 2;
  78. var indices = IndexDatatype.createTypedArray(size / 3, indicesLength + 4);
  79. var index = 0;
  80. indices[index++] = front / 3;
  81. indices[index++] = (back - 2) / 3;
  82. if (addEndPositions) { // add rounded end
  83. wallIndices.push(front / 3);
  84. leftPos = cartesian1;
  85. rightPos = cartesian2;
  86. var firstEndPositions = endPositions[0];
  87. for (i = 0; i < halfLength; i++) {
  88. leftPos = Cartesian3.fromArray(firstEndPositions, (halfLength - 1 - i) * 3, leftPos);
  89. rightPos = Cartesian3.fromArray(firstEndPositions, (halfLength + i) * 3, rightPos);
  90. CorridorGeometryLibrary.addAttribute(finalPositions, rightPos, front);
  91. CorridorGeometryLibrary.addAttribute(finalPositions, leftPos, undefined, back);
  92. LL = front / 3;
  93. LR = LL + 1;
  94. UL = (back - 2) / 3;
  95. UR = UL - 1;
  96. indices[index++] = UL;
  97. indices[index++] = UR;
  98. indices[index++] = LL;
  99. indices[index++] = LR;
  100. front += 3;
  101. back -= 3;
  102. }
  103. }
  104. var posIndex = 0;
  105. var rightEdge = positions[posIndex++]; //add first two edges
  106. var leftEdge = positions[posIndex++];
  107. finalPositions.set(rightEdge, front);
  108. finalPositions.set(leftEdge, back - leftEdge.length + 1);
  109. length = leftEdge.length - 3;
  110. wallIndices.push(front / 3, (back - 2) / 3);
  111. for (i = 0; i < length; i += 3) {
  112. LL = front / 3;
  113. LR = LL + 1;
  114. UL = (back - 2) / 3;
  115. UR = UL - 1;
  116. indices[index++] = UL;
  117. indices[index++] = UR;
  118. indices[index++] = LL;
  119. indices[index++] = LR;
  120. front += 3;
  121. back -= 3;
  122. }
  123. for (i = 0; i < corners.length; i++) {
  124. var j;
  125. corner = corners[i];
  126. var l = corner.leftPositions;
  127. var r = corner.rightPositions;
  128. var start;
  129. var outsidePoint = cartesian3;
  130. if (defined(l)) {
  131. back -= 3;
  132. start = UR;
  133. wallIndices.push(LR);
  134. for (j = 0; j < l.length / 3; j++) {
  135. outsidePoint = Cartesian3.fromArray(l, j * 3, outsidePoint);
  136. indices[index++] = start - j - 1;
  137. indices[index++] = start - j;
  138. CorridorGeometryLibrary.addAttribute(finalPositions, outsidePoint, undefined, back);
  139. back -= 3;
  140. }
  141. wallIndices.push(start - Math.floor(l.length / 6));
  142. if (cornerType === CornerType.BEVELED) {
  143. wallIndices.push((back - 2) / 3 + 1);
  144. }
  145. front += 3;
  146. } else {
  147. front += 3;
  148. start = LR;
  149. wallIndices.push(UR);
  150. for (j = 0; j < r.length / 3; j++) {
  151. outsidePoint = Cartesian3.fromArray(r, j * 3, outsidePoint);
  152. indices[index++] = start + j;
  153. indices[index++] = start + j + 1;
  154. CorridorGeometryLibrary.addAttribute(finalPositions, outsidePoint, front);
  155. front += 3;
  156. }
  157. wallIndices.push(start + Math.floor(r.length / 6));
  158. if (cornerType === CornerType.BEVELED) {
  159. wallIndices.push(front / 3 - 1);
  160. }
  161. back -= 3;
  162. }
  163. rightEdge = positions[posIndex++];
  164. leftEdge = positions[posIndex++];
  165. rightEdge.splice(0, 3); //remove duplicate points added by corner
  166. leftEdge.splice(leftEdge.length - 3, 3);
  167. finalPositions.set(rightEdge, front);
  168. finalPositions.set(leftEdge, back - leftEdge.length + 1);
  169. length = leftEdge.length - 3;
  170. for (j = 0; j < leftEdge.length; j += 3) {
  171. LR = front / 3;
  172. LL = LR - 1;
  173. UR = (back - 2) / 3;
  174. UL = UR + 1;
  175. indices[index++] = UL;
  176. indices[index++] = UR;
  177. indices[index++] = LL;
  178. indices[index++] = LR;
  179. front += 3;
  180. back -= 3;
  181. }
  182. front -= 3;
  183. back += 3;
  184. wallIndices.push(front / 3, (back - 2) / 3);
  185. }
  186. if (addEndPositions) { // add rounded end
  187. front += 3;
  188. back -= 3;
  189. leftPos = cartesian1;
  190. rightPos = cartesian2;
  191. var lastEndPositions = endPositions[1];
  192. for (i = 0; i < halfLength; i++) {
  193. leftPos = Cartesian3.fromArray(lastEndPositions, (endPositionLength - i - 1) * 3, leftPos);
  194. rightPos = Cartesian3.fromArray(lastEndPositions, i * 3, rightPos);
  195. CorridorGeometryLibrary.addAttribute(finalPositions, leftPos, undefined, back);
  196. CorridorGeometryLibrary.addAttribute(finalPositions, rightPos, front);
  197. LR = front / 3;
  198. LL = LR - 1;
  199. UR = (back - 2) / 3;
  200. UL = UR + 1;
  201. indices[index++] = UL;
  202. indices[index++] = UR;
  203. indices[index++] = LL;
  204. indices[index++] = LR;
  205. front += 3;
  206. back -= 3;
  207. }
  208. wallIndices.push(front / 3);
  209. } else {
  210. wallIndices.push(front / 3, (back - 2) / 3);
  211. }
  212. indices[index++] = front / 3;
  213. indices[index++] = (back - 2) / 3;
  214. attributes.position = new GeometryAttribute({
  215. componentDatatype : ComponentDatatype.DOUBLE,
  216. componentsPerAttribute : 3,
  217. values : finalPositions
  218. });
  219. return {
  220. attributes : attributes,
  221. indices : indices,
  222. wallIndices : wallIndices
  223. };
  224. }
  225. function computePositionsExtruded(params) {
  226. var ellipsoid = params.ellipsoid;
  227. var computedPositions = CorridorGeometryLibrary.computePositions(params);
  228. var attr = combine(computedPositions, params.cornerType);
  229. var wallIndices = attr.wallIndices;
  230. var height = params.height;
  231. var extrudedHeight = params.extrudedHeight;
  232. var attributes = attr.attributes;
  233. var indices = attr.indices;
  234. var positions = attributes.position.values;
  235. var length = positions.length;
  236. var extrudedPositions = new Float64Array(length);
  237. extrudedPositions.set(positions);
  238. var newPositions = new Float64Array(length * 2);
  239. positions = PolygonPipeline.scaleToGeodeticHeight(positions, height, ellipsoid);
  240. extrudedPositions = PolygonPipeline.scaleToGeodeticHeight(extrudedPositions, extrudedHeight, ellipsoid);
  241. newPositions.set(positions);
  242. newPositions.set(extrudedPositions, length);
  243. attributes.position.values = newPositions;
  244. length /= 3;
  245. if (defined(params.offsetAttribute)) {
  246. var applyOffset = new Uint8Array(length * 2);
  247. if (params.offsetAttribute === GeometryOffsetAttribute.TOP) {
  248. applyOffset = arrayFill(applyOffset, 1, 0, length);
  249. } else {
  250. var applyOffsetValue = params.offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1;
  251. applyOffset = arrayFill(applyOffset, applyOffsetValue);
  252. }
  253. attributes.applyOffset = new GeometryAttribute({
  254. componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
  255. componentsPerAttribute : 1,
  256. values: applyOffset
  257. });
  258. }
  259. var i;
  260. var iLength = indices.length;
  261. var newIndices = IndexDatatype.createTypedArray(newPositions.length / 3, (iLength + wallIndices.length) * 2);
  262. newIndices.set(indices);
  263. var index = iLength;
  264. for (i = 0; i < iLength; i += 2) { // bottom indices
  265. var v0 = indices[i];
  266. var v1 = indices[i + 1];
  267. newIndices[index++] = v0 + length;
  268. newIndices[index++] = v1 + length;
  269. }
  270. var UL, LL;
  271. for (i = 0; i < wallIndices.length; i++) { //wall indices
  272. UL = wallIndices[i];
  273. LL = UL + length;
  274. newIndices[index++] = UL;
  275. newIndices[index++] = LL;
  276. }
  277. return {
  278. attributes : attributes,
  279. indices : newIndices
  280. };
  281. }
  282. /**
  283. * A description of a corridor outline.
  284. *
  285. * @alias CorridorOutlineGeometry
  286. * @constructor
  287. *
  288. * @param {Object} options Object with the following properties:
  289. * @param {Cartesian3[]} options.positions An array of positions that define the center of the corridor outline.
  290. * @param {Number} options.width The distance between the edges of the corridor outline.
  291. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference.
  292. * @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.
  293. * @param {Number} [options.height=0] The distance in meters between the positions and the ellipsoid surface.
  294. * @param {Number} [options.extrudedHeight] The distance in meters between the extruded face and the ellipsoid surface.
  295. * @param {CornerType} [options.cornerType=CornerType.ROUNDED] Determines the style of the corners.
  296. *
  297. * @see CorridorOutlineGeometry.createGeometry
  298. *
  299. * @example
  300. * var corridor = new Cesium.CorridorOutlineGeometry({
  301. * positions : Cesium.Cartesian3.fromDegreesArray([-72.0, 40.0, -70.0, 35.0]),
  302. * width : 100000
  303. * });
  304. */
  305. function CorridorOutlineGeometry(options) {
  306. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  307. var positions = options.positions;
  308. var width = options.width;
  309. //>>includeStart('debug', pragmas.debug);
  310. Check.typeOf.object('options.positions', positions);
  311. Check.typeOf.number('options.width', width);
  312. //>>includeEnd('debug');
  313. var height = defaultValue(options.height, 0.0);
  314. var extrudedHeight = defaultValue(options.extrudedHeight, height);
  315. this._positions = positions;
  316. this._ellipsoid = Ellipsoid.clone(defaultValue(options.ellipsoid, Ellipsoid.WGS84));
  317. this._width = width;
  318. this._height = Math.max(height, extrudedHeight);
  319. this._extrudedHeight = Math.min(height, extrudedHeight);
  320. this._cornerType = defaultValue(options.cornerType, CornerType.ROUNDED);
  321. this._granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE);
  322. this._offsetAttribute = options.offsetAttribute;
  323. this._workerName = 'createCorridorOutlineGeometry';
  324. /**
  325. * The number of elements used to pack the object into an array.
  326. * @type {Number}
  327. */
  328. this.packedLength = 1 + positions.length * Cartesian3.packedLength + Ellipsoid.packedLength + 6;
  329. }
  330. /**
  331. * Stores the provided instance into the provided array.
  332. *
  333. * @param {CorridorOutlineGeometry} value The value to pack.
  334. * @param {Number[]} array The array to pack into.
  335. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements.
  336. *
  337. * @returns {Number[]} The array that was packed into
  338. */
  339. CorridorOutlineGeometry.pack = function(value, array, startingIndex) {
  340. //>>includeStart('debug', pragmas.debug);
  341. Check.typeOf.object('value', value);
  342. Check.typeOf.object('array', array);
  343. //>>includeEnd('debug');
  344. startingIndex = defaultValue(startingIndex, 0);
  345. var positions = value._positions;
  346. var length = positions.length;
  347. array[startingIndex++] = length;
  348. for (var i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) {
  349. Cartesian3.pack(positions[i], array, startingIndex);
  350. }
  351. Ellipsoid.pack(value._ellipsoid, array, startingIndex);
  352. startingIndex += Ellipsoid.packedLength;
  353. array[startingIndex++] = value._width;
  354. array[startingIndex++] = value._height;
  355. array[startingIndex++] = value._extrudedHeight;
  356. array[startingIndex++] = value._cornerType;
  357. array[startingIndex++] = value._granularity;
  358. array[startingIndex] = defaultValue(value._offsetAttribute, -1);
  359. return array;
  360. };
  361. var scratchEllipsoid = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE);
  362. var scratchOptions = {
  363. positions : undefined,
  364. ellipsoid : scratchEllipsoid,
  365. width : undefined,
  366. height : undefined,
  367. extrudedHeight : undefined,
  368. cornerType : undefined,
  369. granularity : undefined,
  370. offsetAttribute: undefined
  371. };
  372. /**
  373. * Retrieves an instance from a packed array.
  374. *
  375. * @param {Number[]} array The packed array.
  376. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked.
  377. * @param {CorridorOutlineGeometry} [result] The object into which to store the result.
  378. * @returns {CorridorOutlineGeometry} The modified result parameter or a new CorridorOutlineGeometry instance if one was not provided.
  379. */
  380. CorridorOutlineGeometry.unpack = function(array, startingIndex, result) {
  381. //>>includeStart('debug', pragmas.debug);
  382. Check.typeOf.object('array', array);
  383. //>>includeEnd('debug');
  384. startingIndex = defaultValue(startingIndex, 0);
  385. var length = array[startingIndex++];
  386. var positions = new Array(length);
  387. for (var i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) {
  388. positions[i] = Cartesian3.unpack(array, startingIndex);
  389. }
  390. var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid);
  391. startingIndex += Ellipsoid.packedLength;
  392. var width = array[startingIndex++];
  393. var height = array[startingIndex++];
  394. var extrudedHeight = array[startingIndex++];
  395. var cornerType = array[startingIndex++];
  396. var granularity = array[startingIndex++];
  397. var offsetAttribute = array[startingIndex];
  398. if (!defined(result)) {
  399. scratchOptions.positions = positions;
  400. scratchOptions.width = width;
  401. scratchOptions.height = height;
  402. scratchOptions.extrudedHeight = extrudedHeight;
  403. scratchOptions.cornerType = cornerType;
  404. scratchOptions.granularity = granularity;
  405. scratchOptions.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute;
  406. return new CorridorOutlineGeometry(scratchOptions);
  407. }
  408. result._positions = positions;
  409. result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid);
  410. result._width = width;
  411. result._height = height;
  412. result._extrudedHeight = extrudedHeight;
  413. result._cornerType = cornerType;
  414. result._granularity = granularity;
  415. result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute;
  416. return result;
  417. };
  418. /**
  419. * Computes the geometric representation of a corridor, including its vertices, indices, and a bounding sphere.
  420. *
  421. * @param {CorridorOutlineGeometry} corridorOutlineGeometry A description of the corridor.
  422. * @returns {Geometry|undefined} The computed vertices and indices.
  423. */
  424. CorridorOutlineGeometry.createGeometry = function(corridorOutlineGeometry) {
  425. var positions = corridorOutlineGeometry._positions;
  426. var width = corridorOutlineGeometry._width;
  427. var ellipsoid = corridorOutlineGeometry._ellipsoid;
  428. positions = scaleToSurface(positions, ellipsoid);
  429. var cleanPositions = arrayRemoveDuplicates(positions, Cartesian3.equalsEpsilon);
  430. if ((cleanPositions.length < 2) || (width <= 0)) {
  431. return;
  432. }
  433. var height = corridorOutlineGeometry._height;
  434. var extrudedHeight = corridorOutlineGeometry._extrudedHeight;
  435. var extrude = !CesiumMath.equalsEpsilon(height, extrudedHeight, 0, CesiumMath.EPSILON2);
  436. var params = {
  437. ellipsoid : ellipsoid,
  438. positions : cleanPositions,
  439. width : width,
  440. cornerType : corridorOutlineGeometry._cornerType,
  441. granularity : corridorOutlineGeometry._granularity,
  442. saveAttributes : false
  443. };
  444. var attr;
  445. if (extrude) {
  446. params.height = height;
  447. params.extrudedHeight = extrudedHeight;
  448. params.offsetAttribute = corridorOutlineGeometry._offsetAttribute;
  449. attr = computePositionsExtruded(params);
  450. } else {
  451. var computedPositions = CorridorGeometryLibrary.computePositions(params);
  452. attr = combine(computedPositions, params.cornerType);
  453. attr.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(attr.attributes.position.values, height, ellipsoid);
  454. if (defined(corridorOutlineGeometry._offsetAttribute)) {
  455. var length = attr.attributes.position.values.length;
  456. var applyOffset = new Uint8Array(length / 3);
  457. var offsetValue = corridorOutlineGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1;
  458. arrayFill(applyOffset, offsetValue);
  459. attr.attributes.applyOffset = new GeometryAttribute({
  460. componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
  461. componentsPerAttribute : 1,
  462. values: applyOffset
  463. });
  464. }
  465. }
  466. var attributes = attr.attributes;
  467. var boundingSphere = BoundingSphere.fromVertices(attributes.position.values, undefined, 3);
  468. return new Geometry({
  469. attributes : attributes,
  470. indices : attr.indices,
  471. primitiveType : PrimitiveType.LINES,
  472. boundingSphere : boundingSphere,
  473. offsetAttribute : corridorOutlineGeometry._offsetAttribute
  474. });
  475. };
  476. export default CorridorOutlineGeometry;