CoplanarPolygonGeometry.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  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 Check from "./Check.js";
  7. import ComponentDatatype from "./ComponentDatatype.js";
  8. import CoplanarPolygonGeometryLibrary from "./CoplanarPolygonGeometryLibrary.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 GeometryInstance from "./GeometryInstance.js";
  16. import GeometryPipeline from "./GeometryPipeline.js";
  17. import IndexDatatype from "./IndexDatatype.js";
  18. import CesiumMath from "./Math.js";
  19. import Matrix3 from "./Matrix3.js";
  20. import PolygonGeometryLibrary from "./PolygonGeometryLibrary.js";
  21. import PolygonPipeline from "./PolygonPipeline.js";
  22. import PrimitiveType from "./PrimitiveType.js";
  23. import Quaternion from "./Quaternion.js";
  24. import VertexFormat from "./VertexFormat.js";
  25. const scratchPosition = new Cartesian3();
  26. const scratchBR = new BoundingRectangle();
  27. const stScratch = new Cartesian2();
  28. const textureCoordinatesOrigin = new Cartesian2();
  29. const scratchNormal = new Cartesian3();
  30. const scratchTangent = new Cartesian3();
  31. const scratchBitangent = new Cartesian3();
  32. const centerScratch = new Cartesian3();
  33. const axis1Scratch = new Cartesian3();
  34. const axis2Scratch = new Cartesian3();
  35. const quaternionScratch = new Quaternion();
  36. const textureMatrixScratch = new Matrix3();
  37. const tangentRotationScratch = new Matrix3();
  38. const surfaceNormalScratch = new Cartesian3();
  39. function createGeometryFromPolygon(
  40. polygon,
  41. vertexFormat,
  42. boundingRectangle,
  43. stRotation,
  44. hardcodedTextureCoordinates,
  45. projectPointTo2D,
  46. normal,
  47. tangent,
  48. bitangent
  49. ) {
  50. const positions = polygon.positions;
  51. let indices = PolygonPipeline.triangulate(polygon.positions2D, polygon.holes);
  52. /* If polygon is completely unrenderable, just use the first three vertices */
  53. if (indices.length < 3) {
  54. indices = [0, 1, 2];
  55. }
  56. const newIndices = IndexDatatype.createTypedArray(
  57. positions.length,
  58. indices.length
  59. );
  60. newIndices.set(indices);
  61. let textureMatrix = textureMatrixScratch;
  62. if (stRotation !== 0.0) {
  63. let rotation = Quaternion.fromAxisAngle(
  64. normal,
  65. stRotation,
  66. quaternionScratch
  67. );
  68. textureMatrix = Matrix3.fromQuaternion(rotation, textureMatrix);
  69. if (vertexFormat.tangent || vertexFormat.bitangent) {
  70. rotation = Quaternion.fromAxisAngle(
  71. normal,
  72. -stRotation,
  73. quaternionScratch
  74. );
  75. const tangentRotation = Matrix3.fromQuaternion(
  76. rotation,
  77. tangentRotationScratch
  78. );
  79. tangent = Cartesian3.normalize(
  80. Matrix3.multiplyByVector(tangentRotation, tangent, tangent),
  81. tangent
  82. );
  83. if (vertexFormat.bitangent) {
  84. bitangent = Cartesian3.normalize(
  85. Cartesian3.cross(normal, tangent, bitangent),
  86. bitangent
  87. );
  88. }
  89. }
  90. } else {
  91. textureMatrix = Matrix3.clone(Matrix3.IDENTITY, textureMatrix);
  92. }
  93. const stOrigin = textureCoordinatesOrigin;
  94. if (vertexFormat.st) {
  95. stOrigin.x = boundingRectangle.x;
  96. stOrigin.y = boundingRectangle.y;
  97. }
  98. const length = positions.length;
  99. const size = length * 3;
  100. const flatPositions = new Float64Array(size);
  101. const normals = vertexFormat.normal ? new Float32Array(size) : undefined;
  102. const tangents = vertexFormat.tangent ? new Float32Array(size) : undefined;
  103. const bitangents = vertexFormat.bitangent
  104. ? new Float32Array(size)
  105. : undefined;
  106. const textureCoordinates = vertexFormat.st
  107. ? new Float32Array(length * 2)
  108. : undefined;
  109. let positionIndex = 0;
  110. let normalIndex = 0;
  111. let bitangentIndex = 0;
  112. let tangentIndex = 0;
  113. let stIndex = 0;
  114. for (let i = 0; i < length; i++) {
  115. const position = positions[i];
  116. flatPositions[positionIndex++] = position.x;
  117. flatPositions[positionIndex++] = position.y;
  118. flatPositions[positionIndex++] = position.z;
  119. if (vertexFormat.st) {
  120. if (
  121. defined(hardcodedTextureCoordinates) &&
  122. hardcodedTextureCoordinates.positions.length === length
  123. ) {
  124. textureCoordinates[stIndex++] =
  125. hardcodedTextureCoordinates.positions[i].x;
  126. textureCoordinates[stIndex++] =
  127. hardcodedTextureCoordinates.positions[i].y;
  128. } else {
  129. const p = Matrix3.multiplyByVector(
  130. textureMatrix,
  131. position,
  132. scratchPosition
  133. );
  134. const st = projectPointTo2D(p, stScratch);
  135. Cartesian2.subtract(st, stOrigin, st);
  136. const stx = CesiumMath.clamp(st.x / boundingRectangle.width, 0, 1);
  137. const sty = CesiumMath.clamp(st.y / boundingRectangle.height, 0, 1);
  138. textureCoordinates[stIndex++] = stx;
  139. textureCoordinates[stIndex++] = sty;
  140. }
  141. }
  142. if (vertexFormat.normal) {
  143. normals[normalIndex++] = normal.x;
  144. normals[normalIndex++] = normal.y;
  145. normals[normalIndex++] = normal.z;
  146. }
  147. if (vertexFormat.tangent) {
  148. tangents[tangentIndex++] = tangent.x;
  149. tangents[tangentIndex++] = tangent.y;
  150. tangents[tangentIndex++] = tangent.z;
  151. }
  152. if (vertexFormat.bitangent) {
  153. bitangents[bitangentIndex++] = bitangent.x;
  154. bitangents[bitangentIndex++] = bitangent.y;
  155. bitangents[bitangentIndex++] = bitangent.z;
  156. }
  157. }
  158. const attributes = new GeometryAttributes();
  159. if (vertexFormat.position) {
  160. attributes.position = new GeometryAttribute({
  161. componentDatatype: ComponentDatatype.DOUBLE,
  162. componentsPerAttribute: 3,
  163. values: flatPositions,
  164. });
  165. }
  166. if (vertexFormat.normal) {
  167. attributes.normal = new GeometryAttribute({
  168. componentDatatype: ComponentDatatype.FLOAT,
  169. componentsPerAttribute: 3,
  170. values: normals,
  171. });
  172. }
  173. if (vertexFormat.tangent) {
  174. attributes.tangent = new GeometryAttribute({
  175. componentDatatype: ComponentDatatype.FLOAT,
  176. componentsPerAttribute: 3,
  177. values: tangents,
  178. });
  179. }
  180. if (vertexFormat.bitangent) {
  181. attributes.bitangent = new GeometryAttribute({
  182. componentDatatype: ComponentDatatype.FLOAT,
  183. componentsPerAttribute: 3,
  184. values: bitangents,
  185. });
  186. }
  187. if (vertexFormat.st) {
  188. attributes.st = new GeometryAttribute({
  189. componentDatatype: ComponentDatatype.FLOAT,
  190. componentsPerAttribute: 2,
  191. values: textureCoordinates,
  192. });
  193. }
  194. return new Geometry({
  195. attributes: attributes,
  196. indices: newIndices,
  197. primitiveType: PrimitiveType.TRIANGLES,
  198. });
  199. }
  200. /**
  201. * A description of a polygon composed of arbitrary coplanar positions.
  202. *
  203. * @alias CoplanarPolygonGeometry
  204. * @constructor
  205. *
  206. * @param {object} options Object with the following properties:
  207. * @param {PolygonHierarchy} options.polygonHierarchy A polygon hierarchy that can include holes.
  208. * @param {number} [options.stRotation=0.0] The rotation of the texture coordinates, in radians. A positive rotation is counter-clockwise.
  209. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
  210. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference.
  211. * @param {PolygonHierarchy} [options.textureCoordinates] Texture coordinates as a {@link PolygonHierarchy} of {@link Cartesian2} points.
  212. *
  213. * @example
  214. * const polygonGeometry = new Cesium.CoplanarPolygonGeometry({
  215. * polygonHierarchy: new Cesium.PolygonHierarchy(
  216. * Cesium.Cartesian3.fromDegreesArrayHeights([
  217. * -90.0, 30.0, 0.0,
  218. * -90.0, 30.0, 300000.0,
  219. * -80.0, 30.0, 300000.0,
  220. * -80.0, 30.0, 0.0
  221. * ]))
  222. * });
  223. *
  224. */
  225. function CoplanarPolygonGeometry(options) {
  226. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  227. const polygonHierarchy = options.polygonHierarchy;
  228. const textureCoordinates = options.textureCoordinates;
  229. //>>includeStart('debug', pragmas.debug);
  230. Check.defined("options.polygonHierarchy", polygonHierarchy);
  231. //>>includeEnd('debug');
  232. const vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT);
  233. this._vertexFormat = VertexFormat.clone(vertexFormat);
  234. this._polygonHierarchy = polygonHierarchy;
  235. this._stRotation = defaultValue(options.stRotation, 0.0);
  236. this._ellipsoid = Ellipsoid.clone(
  237. defaultValue(options.ellipsoid, Ellipsoid.WGS84)
  238. );
  239. this._workerName = "createCoplanarPolygonGeometry";
  240. this._textureCoordinates = textureCoordinates;
  241. /**
  242. * The number of elements used to pack the object into an array.
  243. * @type {number}
  244. */
  245. this.packedLength =
  246. PolygonGeometryLibrary.computeHierarchyPackedLength(
  247. polygonHierarchy,
  248. Cartesian3
  249. ) +
  250. VertexFormat.packedLength +
  251. Ellipsoid.packedLength +
  252. (defined(textureCoordinates)
  253. ? PolygonGeometryLibrary.computeHierarchyPackedLength(
  254. textureCoordinates,
  255. Cartesian2
  256. )
  257. : 1) +
  258. 2;
  259. }
  260. /**
  261. * A description of a coplanar polygon from an array of positions.
  262. *
  263. * @param {object} options Object with the following properties:
  264. * @param {Cartesian3[]} options.positions An array of positions that defined the corner points of the polygon.
  265. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
  266. * @param {number} [options.stRotation=0.0] The rotation of the texture coordinates, in radians. A positive rotation is counter-clockwise.
  267. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference.
  268. * @param {PolygonHierarchy} [options.textureCoordinates] Texture coordinates as a {@link PolygonHierarchy} of {@link Cartesian2} points.
  269. * @returns {CoplanarPolygonGeometry}
  270. *
  271. * @example
  272. * // create a polygon from points
  273. * const polygon = Cesium.CoplanarPolygonGeometry.fromPositions({
  274. * positions : Cesium.Cartesian3.fromDegreesArray([
  275. * -72.0, 40.0,
  276. * -70.0, 35.0,
  277. * -75.0, 30.0,
  278. * -70.0, 30.0,
  279. * -68.0, 40.0
  280. * ])
  281. * });
  282. * const geometry = Cesium.PolygonGeometry.createGeometry(polygon);
  283. *
  284. * @see PolygonGeometry#createGeometry
  285. */
  286. CoplanarPolygonGeometry.fromPositions = function (options) {
  287. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  288. //>>includeStart('debug', pragmas.debug);
  289. Check.defined("options.positions", options.positions);
  290. //>>includeEnd('debug');
  291. const newOptions = {
  292. polygonHierarchy: {
  293. positions: options.positions,
  294. },
  295. vertexFormat: options.vertexFormat,
  296. stRotation: options.stRotation,
  297. ellipsoid: options.ellipsoid,
  298. textureCoordinates: options.textureCoordinates,
  299. };
  300. return new CoplanarPolygonGeometry(newOptions);
  301. };
  302. /**
  303. * Stores the provided instance into the provided array.
  304. *
  305. * @param {CoplanarPolygonGeometry} value The value to pack.
  306. * @param {number[]} array The array to pack into.
  307. * @param {number} [startingIndex=0] The index into the array at which to start packing the elements.
  308. *
  309. * @returns {number[]} The array that was packed into
  310. */
  311. CoplanarPolygonGeometry.pack = function (value, array, startingIndex) {
  312. //>>includeStart('debug', pragmas.debug);
  313. Check.typeOf.object("value", value);
  314. Check.defined("array", array);
  315. //>>includeEnd('debug');
  316. startingIndex = defaultValue(startingIndex, 0);
  317. startingIndex = PolygonGeometryLibrary.packPolygonHierarchy(
  318. value._polygonHierarchy,
  319. array,
  320. startingIndex,
  321. Cartesian3
  322. );
  323. Ellipsoid.pack(value._ellipsoid, array, startingIndex);
  324. startingIndex += Ellipsoid.packedLength;
  325. VertexFormat.pack(value._vertexFormat, array, startingIndex);
  326. startingIndex += VertexFormat.packedLength;
  327. array[startingIndex++] = value._stRotation;
  328. if (defined(value._textureCoordinates)) {
  329. startingIndex = PolygonGeometryLibrary.packPolygonHierarchy(
  330. value._textureCoordinates,
  331. array,
  332. startingIndex,
  333. Cartesian2
  334. );
  335. } else {
  336. array[startingIndex++] = -1.0;
  337. }
  338. array[startingIndex++] = value.packedLength;
  339. return array;
  340. };
  341. const scratchEllipsoid = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE);
  342. const scratchVertexFormat = new VertexFormat();
  343. const scratchOptions = {
  344. polygonHierarchy: {},
  345. };
  346. /**
  347. * Retrieves an instance from a packed array.
  348. *
  349. * @param {number[]} array The packed array.
  350. * @param {number} [startingIndex=0] The starting index of the element to be unpacked.
  351. * @param {CoplanarPolygonGeometry} [result] The object into which to store the result.
  352. * @returns {CoplanarPolygonGeometry} The modified result parameter or a new CoplanarPolygonGeometry instance if one was not provided.
  353. */
  354. CoplanarPolygonGeometry.unpack = function (array, startingIndex, result) {
  355. //>>includeStart('debug', pragmas.debug);
  356. Check.defined("array", array);
  357. //>>includeEnd('debug');
  358. startingIndex = defaultValue(startingIndex, 0);
  359. const polygonHierarchy = PolygonGeometryLibrary.unpackPolygonHierarchy(
  360. array,
  361. startingIndex,
  362. Cartesian3
  363. );
  364. startingIndex = polygonHierarchy.startingIndex;
  365. delete polygonHierarchy.startingIndex;
  366. const ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid);
  367. startingIndex += Ellipsoid.packedLength;
  368. const vertexFormat = VertexFormat.unpack(
  369. array,
  370. startingIndex,
  371. scratchVertexFormat
  372. );
  373. startingIndex += VertexFormat.packedLength;
  374. const stRotation = array[startingIndex++];
  375. const textureCoordinates =
  376. array[startingIndex] === -1.0
  377. ? undefined
  378. : PolygonGeometryLibrary.unpackPolygonHierarchy(
  379. array,
  380. startingIndex,
  381. Cartesian2
  382. );
  383. if (defined(textureCoordinates)) {
  384. startingIndex = textureCoordinates.startingIndex;
  385. delete textureCoordinates.startingIndex;
  386. } else {
  387. startingIndex++;
  388. }
  389. const packedLength = array[startingIndex++];
  390. if (!defined(result)) {
  391. result = new CoplanarPolygonGeometry(scratchOptions);
  392. }
  393. result._polygonHierarchy = polygonHierarchy;
  394. result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid);
  395. result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat);
  396. result._stRotation = stRotation;
  397. result._textureCoordinates = textureCoordinates;
  398. result.packedLength = packedLength;
  399. return result;
  400. };
  401. /**
  402. * Computes the geometric representation of an arbitrary coplanar polygon, including its vertices, indices, and a bounding sphere.
  403. *
  404. * @param {CoplanarPolygonGeometry} polygonGeometry A description of the polygon.
  405. * @returns {Geometry|undefined} The computed vertices and indices.
  406. */
  407. CoplanarPolygonGeometry.createGeometry = function (polygonGeometry) {
  408. const vertexFormat = polygonGeometry._vertexFormat;
  409. const polygonHierarchy = polygonGeometry._polygonHierarchy;
  410. const stRotation = polygonGeometry._stRotation;
  411. const textureCoordinates = polygonGeometry._textureCoordinates;
  412. const hasTextureCoordinates = defined(textureCoordinates);
  413. let outerPositions = polygonHierarchy.positions;
  414. outerPositions = arrayRemoveDuplicates(
  415. outerPositions,
  416. Cartesian3.equalsEpsilon,
  417. true
  418. );
  419. if (outerPositions.length < 3) {
  420. return;
  421. }
  422. let normal = scratchNormal;
  423. let tangent = scratchTangent;
  424. let bitangent = scratchBitangent;
  425. let axis1 = axis1Scratch;
  426. const axis2 = axis2Scratch;
  427. const validGeometry = CoplanarPolygonGeometryLibrary.computeProjectTo2DArguments(
  428. outerPositions,
  429. centerScratch,
  430. axis1,
  431. axis2
  432. );
  433. if (!validGeometry) {
  434. return undefined;
  435. }
  436. normal = Cartesian3.cross(axis1, axis2, normal);
  437. normal = Cartesian3.normalize(normal, normal);
  438. if (
  439. !Cartesian3.equalsEpsilon(
  440. centerScratch,
  441. Cartesian3.ZERO,
  442. CesiumMath.EPSILON6
  443. )
  444. ) {
  445. const surfaceNormal = polygonGeometry._ellipsoid.geodeticSurfaceNormal(
  446. centerScratch,
  447. surfaceNormalScratch
  448. );
  449. if (Cartesian3.dot(normal, surfaceNormal) < 0) {
  450. normal = Cartesian3.negate(normal, normal);
  451. axis1 = Cartesian3.negate(axis1, axis1);
  452. }
  453. }
  454. const projectPoints = CoplanarPolygonGeometryLibrary.createProjectPointsTo2DFunction(
  455. centerScratch,
  456. axis1,
  457. axis2
  458. );
  459. const projectPoint = CoplanarPolygonGeometryLibrary.createProjectPointTo2DFunction(
  460. centerScratch,
  461. axis1,
  462. axis2
  463. );
  464. if (vertexFormat.tangent) {
  465. tangent = Cartesian3.clone(axis1, tangent);
  466. }
  467. if (vertexFormat.bitangent) {
  468. bitangent = Cartesian3.clone(axis2, bitangent);
  469. }
  470. const results = PolygonGeometryLibrary.polygonsFromHierarchy(
  471. polygonHierarchy,
  472. hasTextureCoordinates,
  473. projectPoints,
  474. false
  475. );
  476. const hierarchy = results.hierarchy;
  477. const polygons = results.polygons;
  478. const dummyFunction = function (identity) {
  479. return identity;
  480. };
  481. const textureCoordinatePolygons = hasTextureCoordinates
  482. ? PolygonGeometryLibrary.polygonsFromHierarchy(
  483. textureCoordinates,
  484. true,
  485. dummyFunction,
  486. false
  487. ).polygons
  488. : undefined;
  489. if (hierarchy.length === 0) {
  490. return;
  491. }
  492. outerPositions = hierarchy[0].outerRing;
  493. const boundingSphere = BoundingSphere.fromPoints(outerPositions);
  494. const boundingRectangle = PolygonGeometryLibrary.computeBoundingRectangle(
  495. normal,
  496. projectPoint,
  497. outerPositions,
  498. stRotation,
  499. scratchBR
  500. );
  501. const geometries = [];
  502. for (let i = 0; i < polygons.length; i++) {
  503. const geometryInstance = new GeometryInstance({
  504. geometry: createGeometryFromPolygon(
  505. polygons[i],
  506. vertexFormat,
  507. boundingRectangle,
  508. stRotation,
  509. hasTextureCoordinates ? textureCoordinatePolygons[i] : undefined,
  510. projectPoint,
  511. normal,
  512. tangent,
  513. bitangent
  514. ),
  515. });
  516. geometries.push(geometryInstance);
  517. }
  518. const geometry = GeometryPipeline.combineInstances(geometries)[0];
  519. geometry.attributes.position.values = new Float64Array(
  520. geometry.attributes.position.values
  521. );
  522. geometry.indices = IndexDatatype.createTypedArray(
  523. geometry.attributes.position.values.length / 3,
  524. geometry.indices
  525. );
  526. const attributes = geometry.attributes;
  527. if (!vertexFormat.position) {
  528. delete attributes.position;
  529. }
  530. return new Geometry({
  531. attributes: attributes,
  532. indices: geometry.indices,
  533. primitiveType: geometry.primitiveType,
  534. boundingSphere: boundingSphere,
  535. });
  536. };
  537. export default CoplanarPolygonGeometry;