RectangleOutlineGeometry.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. import arrayFill from './arrayFill.js';
  2. import BoundingSphere from './BoundingSphere.js';
  3. import Cartesian3 from './Cartesian3.js';
  4. import Cartographic from './Cartographic.js';
  5. import ComponentDatatype from './ComponentDatatype.js';
  6. import defaultValue from './defaultValue.js';
  7. import defined from './defined.js';
  8. import DeveloperError from './DeveloperError.js';
  9. import Ellipsoid from './Ellipsoid.js';
  10. import Geometry from './Geometry.js';
  11. import GeometryAttribute from './GeometryAttribute.js';
  12. import GeometryAttributes from './GeometryAttributes.js';
  13. import GeometryOffsetAttribute from './GeometryOffsetAttribute.js';
  14. import IndexDatatype from './IndexDatatype.js';
  15. import CesiumMath from './Math.js';
  16. import PolygonPipeline from './PolygonPipeline.js';
  17. import PrimitiveType from './PrimitiveType.js';
  18. import Rectangle from './Rectangle.js';
  19. import RectangleGeometryLibrary from './RectangleGeometryLibrary.js';
  20. var bottomBoundingSphere = new BoundingSphere();
  21. var topBoundingSphere = new BoundingSphere();
  22. var positionScratch = new Cartesian3();
  23. var rectangleScratch = new Rectangle();
  24. function constructRectangle(geometry, computedOptions) {
  25. var ellipsoid = geometry._ellipsoid;
  26. var height = computedOptions.height;
  27. var width = computedOptions.width;
  28. var northCap = computedOptions.northCap;
  29. var southCap = computedOptions.southCap;
  30. var rowHeight = height;
  31. var widthMultiplier = 2;
  32. var size = 0;
  33. var corners = 4;
  34. if (northCap) {
  35. widthMultiplier -= 1;
  36. rowHeight -= 1;
  37. size += 1;
  38. corners -= 2;
  39. }
  40. if (southCap) {
  41. widthMultiplier -= 1;
  42. rowHeight -= 1;
  43. size += 1;
  44. corners -= 2;
  45. }
  46. size += (widthMultiplier * width + 2 * rowHeight - corners);
  47. var positions = new Float64Array(size * 3);
  48. var posIndex = 0;
  49. var row = 0;
  50. var col;
  51. var position = positionScratch;
  52. if (northCap) {
  53. RectangleGeometryLibrary.computePosition(computedOptions, ellipsoid, false, row, 0, position);
  54. positions[posIndex++] = position.x;
  55. positions[posIndex++] = position.y;
  56. positions[posIndex++] = position.z;
  57. } else {
  58. for (col = 0; col < width; col++) {
  59. RectangleGeometryLibrary.computePosition(computedOptions, ellipsoid, false, row, col, position);
  60. positions[posIndex++] = position.x;
  61. positions[posIndex++] = position.y;
  62. positions[posIndex++] = position.z;
  63. }
  64. }
  65. col = width - 1;
  66. for (row = 1; row < height; row++) {
  67. RectangleGeometryLibrary.computePosition(computedOptions, ellipsoid, false, row, col, position);
  68. positions[posIndex++] = position.x;
  69. positions[posIndex++] = position.y;
  70. positions[posIndex++] = position.z;
  71. }
  72. row = height - 1;
  73. if (!southCap) { // if southCap is true, we dont need to add any more points because the south pole point was added by the iteration above
  74. for (col = width - 2; col >= 0; col--) {
  75. RectangleGeometryLibrary.computePosition(computedOptions, ellipsoid, false, row, col, position);
  76. positions[posIndex++] = position.x;
  77. positions[posIndex++] = position.y;
  78. positions[posIndex++] = position.z;
  79. }
  80. }
  81. col = 0;
  82. for (row = height - 2; row > 0; row--) {
  83. RectangleGeometryLibrary.computePosition(computedOptions, ellipsoid, false, row, col, position);
  84. positions[posIndex++] = position.x;
  85. positions[posIndex++] = position.y;
  86. positions[posIndex++] = position.z;
  87. }
  88. var indicesSize = positions.length / 3 * 2;
  89. var indices = IndexDatatype.createTypedArray(positions.length / 3, indicesSize);
  90. var index = 0;
  91. for (var i = 0; i < (positions.length / 3) - 1; i++) {
  92. indices[index++] = i;
  93. indices[index++] = i + 1;
  94. }
  95. indices[index++] = (positions.length / 3) - 1;
  96. indices[index++] = 0;
  97. var geo = new Geometry({
  98. attributes : new GeometryAttributes(),
  99. primitiveType : PrimitiveType.LINES
  100. });
  101. geo.attributes.position = new GeometryAttribute({
  102. componentDatatype : ComponentDatatype.DOUBLE,
  103. componentsPerAttribute : 3,
  104. values : positions
  105. });
  106. geo.indices = indices;
  107. return geo;
  108. }
  109. function constructExtrudedRectangle(rectangleGeometry, computedOptions) {
  110. var surfaceHeight = rectangleGeometry._surfaceHeight;
  111. var extrudedHeight = rectangleGeometry._extrudedHeight;
  112. var ellipsoid = rectangleGeometry._ellipsoid;
  113. var minHeight = extrudedHeight;
  114. var maxHeight = surfaceHeight;
  115. var geo = constructRectangle(rectangleGeometry, computedOptions);
  116. var height = computedOptions.height;
  117. var width = computedOptions.width;
  118. var topPositions = PolygonPipeline.scaleToGeodeticHeight(geo.attributes.position.values, maxHeight, ellipsoid, false);
  119. var length = topPositions.length;
  120. var positions = new Float64Array(length * 2);
  121. positions.set(topPositions);
  122. var bottomPositions = PolygonPipeline.scaleToGeodeticHeight(geo.attributes.position.values, minHeight, ellipsoid);
  123. positions.set(bottomPositions, length);
  124. geo.attributes.position.values = positions;
  125. var northCap = computedOptions.northCap;
  126. var southCap = computedOptions.southCap;
  127. var corners = 4;
  128. if (northCap) {
  129. corners -= 1;
  130. }
  131. if (southCap) {
  132. corners -= 1;
  133. }
  134. var indicesSize = (positions.length / 3 + corners) * 2;
  135. var indices = IndexDatatype.createTypedArray(positions.length / 3, indicesSize);
  136. length = positions.length / 6;
  137. var index = 0;
  138. for (var i = 0; i < length - 1; i++) {
  139. indices[index++] = i;
  140. indices[index++] = i + 1;
  141. indices[index++] = i + length;
  142. indices[index++] = i + length + 1;
  143. }
  144. indices[index++] = length - 1;
  145. indices[index++] = 0;
  146. indices[index++] = length + length - 1;
  147. indices[index++] = length;
  148. indices[index++] = 0;
  149. indices[index++] = length;
  150. var bottomCorner;
  151. if (northCap) {
  152. bottomCorner = height - 1;
  153. } else {
  154. var topRightCorner = width - 1;
  155. indices[index++] = topRightCorner;
  156. indices[index++] = topRightCorner + length;
  157. bottomCorner = width + height - 2;
  158. }
  159. indices[index++] = bottomCorner;
  160. indices[index++] = bottomCorner + length;
  161. if (!southCap) {
  162. var bottomLeftCorner = width + bottomCorner - 1;
  163. indices[index++] = bottomLeftCorner;
  164. indices[index] = bottomLeftCorner + length;
  165. }
  166. geo.indices = indices;
  167. return geo;
  168. }
  169. /**
  170. * A description of the outline of a a cartographic rectangle on an ellipsoid centered at the origin.
  171. *
  172. * @alias RectangleOutlineGeometry
  173. * @constructor
  174. *
  175. * @param {Object} options Object with the following properties:
  176. * @param {Rectangle} options.rectangle A cartographic rectangle with north, south, east and west properties in radians.
  177. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rectangle lies.
  178. * @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.
  179. * @param {Number} [options.height=0.0] The distance in meters between the rectangle and the ellipsoid surface.
  180. * @param {Number} [options.rotation=0.0] The rotation of the rectangle, in radians. A positive rotation is counter-clockwise.
  181. * @param {Number} [options.extrudedHeight] The distance in meters between the rectangle's extruded face and the ellipsoid surface.
  182. *
  183. * @exception {DeveloperError} <code>options.rectangle.north</code> must be in the interval [<code>-Pi/2</code>, <code>Pi/2</code>].
  184. * @exception {DeveloperError} <code>options.rectangle.south</code> must be in the interval [<code>-Pi/2</code>, <code>Pi/2</code>].
  185. * @exception {DeveloperError} <code>options.rectangle.east</code> must be in the interval [<code>-Pi</code>, <code>Pi</code>].
  186. * @exception {DeveloperError} <code>options.rectangle.west</code> must be in the interval [<code>-Pi</code>, <code>Pi</code>].
  187. * @exception {DeveloperError} <code>options.rectangle.north</code> must be greater than <code>rectangle.south</code>.
  188. *
  189. * @see RectangleOutlineGeometry#createGeometry
  190. *
  191. * @example
  192. * var rectangle = new Cesium.RectangleOutlineGeometry({
  193. * ellipsoid : Cesium.Ellipsoid.WGS84,
  194. * rectangle : Cesium.Rectangle.fromDegrees(-80.0, 39.0, -74.0, 42.0),
  195. * height : 10000.0
  196. * });
  197. * var geometry = Cesium.RectangleOutlineGeometry.createGeometry(rectangle);
  198. */
  199. function RectangleOutlineGeometry(options) {
  200. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  201. var rectangle = options.rectangle;
  202. var granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE);
  203. var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
  204. var rotation = defaultValue(options.rotation, 0.0);
  205. //>>includeStart('debug', pragmas.debug);
  206. if (!defined(rectangle)) {
  207. throw new DeveloperError('rectangle is required.');
  208. }
  209. Rectangle.validate(rectangle);
  210. if (rectangle.north < rectangle.south) {
  211. throw new DeveloperError('options.rectangle.north must be greater than options.rectangle.south');
  212. }
  213. //>>includeEnd('debug');
  214. var height = defaultValue(options.height, 0.0);
  215. var extrudedHeight = defaultValue(options.extrudedHeight, height);
  216. this._rectangle = Rectangle.clone(rectangle);
  217. this._granularity = granularity;
  218. this._ellipsoid = ellipsoid;
  219. this._surfaceHeight = Math.max(height, extrudedHeight);
  220. this._rotation = rotation;
  221. this._extrudedHeight = Math.min(height, extrudedHeight);
  222. this._offsetAttribute = options.offsetAttribute;
  223. this._workerName = 'createRectangleOutlineGeometry';
  224. }
  225. /**
  226. * The number of elements used to pack the object into an array.
  227. * @type {Number}
  228. */
  229. RectangleOutlineGeometry.packedLength = Rectangle.packedLength + Ellipsoid.packedLength + 5;
  230. /**
  231. * Stores the provided instance into the provided array.
  232. *
  233. * @param {RectangleOutlineGeometry} value The value to pack.
  234. * @param {Number[]} array The array to pack into.
  235. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements.
  236. *
  237. * @returns {Number[]} The array that was packed into
  238. */
  239. RectangleOutlineGeometry.pack = function(value, array, startingIndex) {
  240. //>>includeStart('debug', pragmas.debug);
  241. if (!defined(value)) {
  242. throw new DeveloperError('value is required');
  243. }
  244. if (!defined(array)) {
  245. throw new DeveloperError('array is required');
  246. }
  247. //>>includeEnd('debug');
  248. startingIndex = defaultValue(startingIndex, 0);
  249. Rectangle.pack(value._rectangle, array, startingIndex);
  250. startingIndex += Rectangle.packedLength;
  251. Ellipsoid.pack(value._ellipsoid, array, startingIndex);
  252. startingIndex += Ellipsoid.packedLength;
  253. array[startingIndex++] = value._granularity;
  254. array[startingIndex++] = value._surfaceHeight;
  255. array[startingIndex++] = value._rotation;
  256. array[startingIndex++] = value._extrudedHeight;
  257. array[startingIndex] = defaultValue(value._offsetAttribute, -1);
  258. return array;
  259. };
  260. var scratchRectangle = new Rectangle();
  261. var scratchEllipsoid = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE);
  262. var scratchOptions = {
  263. rectangle : scratchRectangle,
  264. ellipsoid : scratchEllipsoid,
  265. granularity : undefined,
  266. height : undefined,
  267. rotation : undefined,
  268. extrudedHeight : undefined,
  269. offsetAttribute : undefined
  270. };
  271. /**
  272. * Retrieves an instance from a packed array.
  273. *
  274. * @param {Number[]} array The packed array.
  275. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked.
  276. * @param {RectangleOutlineGeometry} [result] The object into which to store the result.
  277. * @returns {RectangleOutlineGeometry} The modified result parameter or a new Quaternion instance if one was not provided.
  278. */
  279. RectangleOutlineGeometry.unpack = function(array, startingIndex, result) {
  280. //>>includeStart('debug', pragmas.debug);
  281. if (!defined(array)) {
  282. throw new DeveloperError('array is required');
  283. }
  284. //>>includeEnd('debug');
  285. startingIndex = defaultValue(startingIndex, 0);
  286. var rectangle = Rectangle.unpack(array, startingIndex, scratchRectangle);
  287. startingIndex += Rectangle.packedLength;
  288. var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid);
  289. startingIndex += Ellipsoid.packedLength;
  290. var granularity = array[startingIndex++];
  291. var height = array[startingIndex++];
  292. var rotation = array[startingIndex++];
  293. var extrudedHeight = array[startingIndex++];
  294. var offsetAttribute = array[startingIndex];
  295. if (!defined(result)) {
  296. scratchOptions.granularity = granularity;
  297. scratchOptions.height = height;
  298. scratchOptions.rotation = rotation;
  299. scratchOptions.extrudedHeight = extrudedHeight;
  300. scratchOptions.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute;
  301. return new RectangleOutlineGeometry(scratchOptions);
  302. }
  303. result._rectangle = Rectangle.clone(rectangle, result._rectangle);
  304. result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid);
  305. result._surfaceHeight = height;
  306. result._rotation = rotation;
  307. result._extrudedHeight = extrudedHeight;
  308. result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute;
  309. return result;
  310. };
  311. var nwScratch = new Cartographic();
  312. /**
  313. * Computes the geometric representation of an outline of a rectangle, including its vertices, indices, and a bounding sphere.
  314. *
  315. * @param {RectangleOutlineGeometry} rectangleGeometry A description of the rectangle outline.
  316. * @returns {Geometry|undefined} The computed vertices and indices.
  317. *
  318. * @exception {DeveloperError} Rotated rectangle is invalid.
  319. */
  320. RectangleOutlineGeometry.createGeometry = function(rectangleGeometry) {
  321. var rectangle = rectangleGeometry._rectangle;
  322. var ellipsoid = rectangleGeometry._ellipsoid;
  323. var computedOptions = RectangleGeometryLibrary.computeOptions(rectangle, rectangleGeometry._granularity, rectangleGeometry._rotation, 0, rectangleScratch, nwScratch);
  324. var geometry;
  325. var boundingSphere;
  326. if ((CesiumMath.equalsEpsilon(rectangle.north, rectangle.south, CesiumMath.EPSILON10) ||
  327. (CesiumMath.equalsEpsilon(rectangle.east, rectangle.west, CesiumMath.EPSILON10)))) {
  328. return undefined;
  329. }
  330. var surfaceHeight = rectangleGeometry._surfaceHeight;
  331. var extrudedHeight = rectangleGeometry._extrudedHeight;
  332. var extrude = !CesiumMath.equalsEpsilon(surfaceHeight, extrudedHeight, 0, CesiumMath.EPSILON2);
  333. var offsetValue;
  334. if (extrude) {
  335. geometry = constructExtrudedRectangle(rectangleGeometry, computedOptions);
  336. if (defined(rectangleGeometry._offsetAttribute)) {
  337. var size = geometry.attributes.position.values.length / 3;
  338. var offsetAttribute = new Uint8Array(size);
  339. if (rectangleGeometry._offsetAttribute === GeometryOffsetAttribute.TOP) {
  340. offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2);
  341. } else {
  342. offsetValue = rectangleGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1;
  343. offsetAttribute = arrayFill(offsetAttribute, offsetValue);
  344. }
  345. geometry.attributes.applyOffset = new GeometryAttribute({
  346. componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
  347. componentsPerAttribute : 1,
  348. values : offsetAttribute
  349. });
  350. }
  351. var topBS = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, surfaceHeight, topBoundingSphere);
  352. var bottomBS = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, extrudedHeight, bottomBoundingSphere);
  353. boundingSphere = BoundingSphere.union(topBS, bottomBS);
  354. } else {
  355. geometry = constructRectangle(rectangleGeometry, computedOptions);
  356. geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(geometry.attributes.position.values, surfaceHeight, ellipsoid, false);
  357. if (defined(rectangleGeometry._offsetAttribute)) {
  358. var length = geometry.attributes.position.values.length;
  359. var applyOffset = new Uint8Array(length / 3);
  360. offsetValue = rectangleGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1;
  361. arrayFill(applyOffset, offsetValue);
  362. geometry.attributes.applyOffset = new GeometryAttribute({
  363. componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
  364. componentsPerAttribute : 1,
  365. values : applyOffset
  366. });
  367. }
  368. boundingSphere = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, surfaceHeight);
  369. }
  370. return new Geometry({
  371. attributes : geometry.attributes,
  372. indices : geometry.indices,
  373. primitiveType : PrimitiveType.LINES,
  374. boundingSphere : boundingSphere,
  375. offsetAttribute : rectangleGeometry._offsetAttribute
  376. });
  377. };
  378. export default RectangleOutlineGeometry;