CoplanarPolygonGeometry.js 16 KB

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