PolygonGeometryLibrary.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. import ArcType from './ArcType.js';
  2. import arrayRemoveDuplicates from './arrayRemoveDuplicates.js';
  3. import Cartesian2 from './Cartesian2.js';
  4. import Cartesian3 from './Cartesian3.js';
  5. import Cartographic from './Cartographic.js';
  6. import ComponentDatatype from './ComponentDatatype.js';
  7. import defaultValue from './defaultValue.js';
  8. import defined from './defined.js';
  9. import Ellipsoid from './Ellipsoid.js';
  10. import EllipsoidRhumbLine from './EllipsoidRhumbLine.js';
  11. import Geometry from './Geometry.js';
  12. import GeometryAttribute from './GeometryAttribute.js';
  13. import GeometryAttributes from './GeometryAttributes.js';
  14. import GeometryPipeline from './GeometryPipeline.js';
  15. import IndexDatatype from './IndexDatatype.js';
  16. import CesiumMath from './Math.js';
  17. import Matrix3 from './Matrix3.js';
  18. import PolygonPipeline from './PolygonPipeline.js';
  19. import PrimitiveType from './PrimitiveType.js';
  20. import Quaternion from './Quaternion.js';
  21. import Queue from './Queue.js';
  22. import WindingOrder from './WindingOrder.js';
  23. /**
  24. * @private
  25. */
  26. var PolygonGeometryLibrary = {};
  27. PolygonGeometryLibrary.computeHierarchyPackedLength = function(polygonHierarchy) {
  28. var numComponents = 0;
  29. var stack = [polygonHierarchy];
  30. while (stack.length > 0) {
  31. var hierarchy = stack.pop();
  32. if (!defined(hierarchy)) {
  33. continue;
  34. }
  35. numComponents += 2;
  36. var positions = hierarchy.positions;
  37. var holes = hierarchy.holes;
  38. if (defined(positions)) {
  39. numComponents += positions.length * Cartesian3.packedLength;
  40. }
  41. if (defined(holes)) {
  42. var length = holes.length;
  43. for (var i = 0; i < length; ++i) {
  44. stack.push(holes[i]);
  45. }
  46. }
  47. }
  48. return numComponents;
  49. };
  50. PolygonGeometryLibrary.packPolygonHierarchy = function(polygonHierarchy, array, startingIndex) {
  51. var stack = [polygonHierarchy];
  52. while (stack.length > 0) {
  53. var hierarchy = stack.pop();
  54. if (!defined(hierarchy)) {
  55. continue;
  56. }
  57. var positions = hierarchy.positions;
  58. var holes = hierarchy.holes;
  59. array[startingIndex++] = defined(positions) ? positions.length : 0;
  60. array[startingIndex++] = defined(holes) ? holes.length : 0;
  61. if (defined(positions)) {
  62. var positionsLength = positions.length;
  63. for (var i = 0; i < positionsLength; ++i, startingIndex += 3) {
  64. Cartesian3.pack(positions[i], array, startingIndex);
  65. }
  66. }
  67. if (defined(holes)) {
  68. var holesLength = holes.length;
  69. for (var j = 0; j < holesLength; ++j) {
  70. stack.push(holes[j]);
  71. }
  72. }
  73. }
  74. return startingIndex;
  75. };
  76. PolygonGeometryLibrary.unpackPolygonHierarchy = function(array, startingIndex) {
  77. var positionsLength = array[startingIndex++];
  78. var holesLength = array[startingIndex++];
  79. var positions = new Array(positionsLength);
  80. var holes = holesLength > 0 ? new Array(holesLength) : undefined;
  81. for (var i = 0; i < positionsLength; ++i, startingIndex += Cartesian3.packedLength) {
  82. positions[i] = Cartesian3.unpack(array, startingIndex);
  83. }
  84. for (var j = 0; j < holesLength; ++j) {
  85. holes[j] = PolygonGeometryLibrary.unpackPolygonHierarchy(array, startingIndex);
  86. startingIndex = holes[j].startingIndex;
  87. delete holes[j].startingIndex;
  88. }
  89. return {
  90. positions : positions,
  91. holes : holes,
  92. startingIndex : startingIndex
  93. };
  94. };
  95. var distanceScratch = new Cartesian3();
  96. function getPointAtDistance(p0, p1, distance, length) {
  97. Cartesian3.subtract(p1, p0, distanceScratch);
  98. Cartesian3.multiplyByScalar(distanceScratch, distance / length, distanceScratch);
  99. Cartesian3.add(p0, distanceScratch, distanceScratch);
  100. return [distanceScratch.x, distanceScratch.y, distanceScratch.z];
  101. }
  102. PolygonGeometryLibrary.subdivideLineCount = function(p0, p1, minDistance) {
  103. var distance = Cartesian3.distance(p0, p1);
  104. var n = distance / minDistance;
  105. var countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n)));
  106. return Math.pow(2, countDivide);
  107. };
  108. var scratchCartographic0 = new Cartographic();
  109. var scratchCartographic1 = new Cartographic();
  110. var scratchCartographic2 = new Cartographic();
  111. var scratchCartesian0 = new Cartesian3();
  112. PolygonGeometryLibrary.subdivideRhumbLineCount = function(ellipsoid, p0, p1, minDistance) {
  113. var c0 = ellipsoid.cartesianToCartographic(p0, scratchCartographic0);
  114. var c1 = ellipsoid.cartesianToCartographic(p1, scratchCartographic1);
  115. var rhumb = new EllipsoidRhumbLine(c0, c1, ellipsoid);
  116. var n = rhumb.surfaceDistance / minDistance;
  117. var countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n)));
  118. return Math.pow(2, countDivide);
  119. };
  120. PolygonGeometryLibrary.subdivideLine = function(p0, p1, minDistance, result) {
  121. var numVertices = PolygonGeometryLibrary.subdivideLineCount(p0, p1, minDistance);
  122. var length = Cartesian3.distance(p0, p1);
  123. var distanceBetweenVertices = length / numVertices;
  124. if (!defined(result)) {
  125. result = [];
  126. }
  127. var positions = result;
  128. positions.length = numVertices * 3;
  129. var index = 0;
  130. for ( var i = 0; i < numVertices; i++) {
  131. var p = getPointAtDistance(p0, p1, i * distanceBetweenVertices, length);
  132. positions[index++] = p[0];
  133. positions[index++] = p[1];
  134. positions[index++] = p[2];
  135. }
  136. return positions;
  137. };
  138. PolygonGeometryLibrary.subdivideRhumbLine = function(ellipsoid, p0, p1, minDistance, result) {
  139. var c0 = ellipsoid.cartesianToCartographic(p0, scratchCartographic0);
  140. var c1 = ellipsoid.cartesianToCartographic(p1, scratchCartographic1);
  141. var rhumb = new EllipsoidRhumbLine(c0, c1, ellipsoid);
  142. var n = rhumb.surfaceDistance / minDistance;
  143. var countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n)));
  144. var numVertices = Math.pow(2, countDivide);
  145. var distanceBetweenVertices = rhumb.surfaceDistance / numVertices;
  146. if (!defined(result)) {
  147. result = [];
  148. }
  149. var positions = result;
  150. positions.length = numVertices * 3;
  151. var index = 0;
  152. for ( var i = 0; i < numVertices; i++) {
  153. var c = rhumb.interpolateUsingSurfaceDistance(i * distanceBetweenVertices, scratchCartographic2);
  154. var p = ellipsoid.cartographicToCartesian(c, scratchCartesian0);
  155. positions[index++] = p.x;
  156. positions[index++] = p.y;
  157. positions[index++] = p.z;
  158. }
  159. return positions;
  160. };
  161. var scaleToGeodeticHeightN1 = new Cartesian3();
  162. var scaleToGeodeticHeightN2 = new Cartesian3();
  163. var scaleToGeodeticHeightP1 = new Cartesian3();
  164. var scaleToGeodeticHeightP2 = new Cartesian3();
  165. PolygonGeometryLibrary.scaleToGeodeticHeightExtruded = function(geometry, maxHeight, minHeight, ellipsoid, perPositionHeight) {
  166. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  167. var n1 = scaleToGeodeticHeightN1;
  168. var n2 = scaleToGeodeticHeightN2;
  169. var p = scaleToGeodeticHeightP1;
  170. var p2 = scaleToGeodeticHeightP2;
  171. if (defined(geometry) && defined(geometry.attributes) && defined(geometry.attributes.position)) {
  172. var positions = geometry.attributes.position.values;
  173. var length = positions.length / 2;
  174. for ( var i = 0; i < length; i += 3) {
  175. Cartesian3.fromArray(positions, i, p);
  176. ellipsoid.geodeticSurfaceNormal(p, n1);
  177. p2 = ellipsoid.scaleToGeodeticSurface(p, p2);
  178. n2 = Cartesian3.multiplyByScalar(n1, minHeight, n2);
  179. n2 = Cartesian3.add(p2, n2, n2);
  180. positions[i + length] = n2.x;
  181. positions[i + 1 + length] = n2.y;
  182. positions[i + 2 + length] = n2.z;
  183. if (perPositionHeight) {
  184. p2 = Cartesian3.clone(p, p2);
  185. }
  186. n2 = Cartesian3.multiplyByScalar(n1, maxHeight, n2);
  187. n2 = Cartesian3.add(p2, n2, n2);
  188. positions[i] = n2.x;
  189. positions[i + 1] = n2.y;
  190. positions[i + 2] = n2.z;
  191. }
  192. }
  193. return geometry;
  194. };
  195. PolygonGeometryLibrary.polygonOutlinesFromHierarchy = function(polygonHierarchy, scaleToEllipsoidSurface, ellipsoid) {
  196. // create from a polygon hierarchy
  197. // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
  198. var polygons = [];
  199. var queue = new Queue();
  200. queue.enqueue(polygonHierarchy);
  201. var i;
  202. var j;
  203. var length;
  204. while (queue.length !== 0) {
  205. var outerNode = queue.dequeue();
  206. var outerRing = outerNode.positions;
  207. if (scaleToEllipsoidSurface) {
  208. length = outerRing.length;
  209. for (i = 0; i < length; i++) {
  210. ellipsoid.scaleToGeodeticSurface(outerRing[i], outerRing[i]);
  211. }
  212. }
  213. outerRing = arrayRemoveDuplicates(outerRing, Cartesian3.equalsEpsilon, true);
  214. if (outerRing.length < 3) {
  215. continue;
  216. }
  217. var numChildren = outerNode.holes ? outerNode.holes.length : 0;
  218. // The outer polygon contains inner polygons
  219. for (i = 0; i < numChildren; i++) {
  220. var hole = outerNode.holes[i];
  221. var holePositions = hole.positions;
  222. if (scaleToEllipsoidSurface) {
  223. length = holePositions.length;
  224. for (j = 0; j < length; ++j) {
  225. ellipsoid.scaleToGeodeticSurface(holePositions[j], holePositions[j]);
  226. }
  227. }
  228. holePositions = arrayRemoveDuplicates(holePositions, Cartesian3.equalsEpsilon, true);
  229. if (holePositions.length < 3) {
  230. continue;
  231. }
  232. polygons.push(holePositions);
  233. var numGrandchildren = 0;
  234. if (defined(hole.holes)) {
  235. numGrandchildren = hole.holes.length;
  236. }
  237. for (j = 0; j < numGrandchildren; j++) {
  238. queue.enqueue(hole.holes[j]);
  239. }
  240. }
  241. polygons.push(outerRing);
  242. }
  243. return polygons;
  244. };
  245. PolygonGeometryLibrary.polygonsFromHierarchy = function(polygonHierarchy, projectPointsTo2D, scaleToEllipsoidSurface, ellipsoid) {
  246. // create from a polygon hierarchy
  247. // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
  248. var hierarchy = [];
  249. var polygons = [];
  250. var queue = new Queue();
  251. queue.enqueue(polygonHierarchy);
  252. while (queue.length !== 0) {
  253. var outerNode = queue.dequeue();
  254. var outerRing = outerNode.positions;
  255. var holes = outerNode.holes;
  256. var i;
  257. var length;
  258. if (scaleToEllipsoidSurface) {
  259. length = outerRing.length;
  260. for (i = 0; i < length; i++) {
  261. ellipsoid.scaleToGeodeticSurface(outerRing[i], outerRing[i]);
  262. }
  263. }
  264. outerRing = arrayRemoveDuplicates(outerRing, Cartesian3.equalsEpsilon, true);
  265. if (outerRing.length < 3) {
  266. continue;
  267. }
  268. var positions2D = projectPointsTo2D(outerRing);
  269. if (!defined(positions2D)) {
  270. continue;
  271. }
  272. var holeIndices = [];
  273. var originalWindingOrder = PolygonPipeline.computeWindingOrder2D(positions2D);
  274. if (originalWindingOrder === WindingOrder.CLOCKWISE) {
  275. positions2D.reverse();
  276. outerRing = outerRing.slice().reverse();
  277. }
  278. var positions = outerRing.slice();
  279. var numChildren = defined(holes) ? holes.length : 0;
  280. var polygonHoles = [];
  281. var j;
  282. for (i = 0; i < numChildren; i++) {
  283. var hole = holes[i];
  284. var holePositions = hole.positions;
  285. if (scaleToEllipsoidSurface) {
  286. length = holePositions.length;
  287. for (j = 0; j < length; ++j) {
  288. ellipsoid.scaleToGeodeticSurface(holePositions[j], holePositions[j]);
  289. }
  290. }
  291. holePositions = arrayRemoveDuplicates(holePositions, Cartesian3.equalsEpsilon, true);
  292. if (holePositions.length < 3) {
  293. continue;
  294. }
  295. var holePositions2D = projectPointsTo2D(holePositions);
  296. if (!defined(holePositions2D)) {
  297. continue;
  298. }
  299. originalWindingOrder = PolygonPipeline.computeWindingOrder2D(holePositions2D);
  300. if (originalWindingOrder === WindingOrder.CLOCKWISE) {
  301. holePositions2D.reverse();
  302. holePositions = holePositions.slice().reverse();
  303. }
  304. polygonHoles.push(holePositions);
  305. holeIndices.push(positions.length);
  306. positions = positions.concat(holePositions);
  307. positions2D = positions2D.concat(holePositions2D);
  308. var numGrandchildren = 0;
  309. if (defined(hole.holes)) {
  310. numGrandchildren = hole.holes.length;
  311. }
  312. for (j = 0; j < numGrandchildren; j++) {
  313. queue.enqueue(hole.holes[j]);
  314. }
  315. }
  316. hierarchy.push({
  317. outerRing : outerRing,
  318. holes : polygonHoles
  319. });
  320. polygons.push({
  321. positions : positions,
  322. positions2D : positions2D,
  323. holes : holeIndices
  324. });
  325. }
  326. return {
  327. hierarchy : hierarchy,
  328. polygons : polygons
  329. };
  330. };
  331. var computeBoundingRectangleCartesian2 = new Cartesian2();
  332. var computeBoundingRectangleCartesian3 = new Cartesian3();
  333. var computeBoundingRectangleQuaternion = new Quaternion();
  334. var computeBoundingRectangleMatrix3 = new Matrix3();
  335. PolygonGeometryLibrary.computeBoundingRectangle = function (planeNormal, projectPointTo2D, positions, angle, result) {
  336. var rotation = Quaternion.fromAxisAngle(planeNormal, angle, computeBoundingRectangleQuaternion);
  337. var textureMatrix = Matrix3.fromQuaternion(rotation, computeBoundingRectangleMatrix3);
  338. var minX = Number.POSITIVE_INFINITY;
  339. var maxX = Number.NEGATIVE_INFINITY;
  340. var minY = Number.POSITIVE_INFINITY;
  341. var maxY = Number.NEGATIVE_INFINITY;
  342. var length = positions.length;
  343. for ( var i = 0; i < length; ++i) {
  344. var p = Cartesian3.clone(positions[i], computeBoundingRectangleCartesian3);
  345. Matrix3.multiplyByVector(textureMatrix, p, p);
  346. var st = projectPointTo2D(p, computeBoundingRectangleCartesian2);
  347. if (defined(st)) {
  348. minX = Math.min(minX, st.x);
  349. maxX = Math.max(maxX, st.x);
  350. minY = Math.min(minY, st.y);
  351. maxY = Math.max(maxY, st.y);
  352. }
  353. }
  354. result.x = minX;
  355. result.y = minY;
  356. result.width = maxX - minX;
  357. result.height = maxY - minY;
  358. return result;
  359. };
  360. PolygonGeometryLibrary.createGeometryFromPositions = function(ellipsoid, polygon, granularity, perPositionHeight, vertexFormat, arcType) {
  361. var indices = PolygonPipeline.triangulate(polygon.positions2D, polygon.holes);
  362. /* If polygon is completely unrenderable, just use the first three vertices */
  363. if (indices.length < 3) {
  364. indices = [0, 1, 2];
  365. }
  366. var positions = polygon.positions;
  367. if (perPositionHeight) {
  368. var length = positions.length;
  369. var flattenedPositions = new Array(length * 3);
  370. var index = 0;
  371. for ( var i = 0; i < length; i++) {
  372. var p = positions[i];
  373. flattenedPositions[index++] = p.x;
  374. flattenedPositions[index++] = p.y;
  375. flattenedPositions[index++] = p.z;
  376. }
  377. var geometry = new Geometry({
  378. attributes : {
  379. position : new GeometryAttribute({
  380. componentDatatype : ComponentDatatype.DOUBLE,
  381. componentsPerAttribute : 3,
  382. values : flattenedPositions
  383. })
  384. },
  385. indices : indices,
  386. primitiveType : PrimitiveType.TRIANGLES
  387. });
  388. if (vertexFormat.normal) {
  389. return GeometryPipeline.computeNormal(geometry);
  390. }
  391. return geometry;
  392. }
  393. if (arcType === ArcType.GEODESIC) {
  394. return PolygonPipeline.computeSubdivision(ellipsoid, positions, indices, granularity);
  395. } else if (arcType === ArcType.RHUMB) {
  396. return PolygonPipeline.computeRhumbLineSubdivision(ellipsoid, positions, indices, granularity);
  397. }
  398. };
  399. var computeWallIndicesSubdivided = [];
  400. var p1Scratch = new Cartesian3();
  401. var p2Scratch = new Cartesian3();
  402. PolygonGeometryLibrary.computeWallGeometry = function(positions, ellipsoid, granularity, perPositionHeight, arcType) {
  403. var edgePositions;
  404. var topEdgeLength;
  405. var i;
  406. var p1;
  407. var p2;
  408. var length = positions.length;
  409. var index = 0;
  410. if (!perPositionHeight) {
  411. var minDistance = CesiumMath.chordLength(granularity, ellipsoid.maximumRadius);
  412. var numVertices = 0;
  413. if (arcType === ArcType.GEODESIC) {
  414. for (i = 0; i < length; i++) {
  415. numVertices += PolygonGeometryLibrary.subdivideLineCount(positions[i], positions[(i + 1) % length], minDistance);
  416. }
  417. } else if (arcType === ArcType.RHUMB) {
  418. for (i = 0; i < length; i++) {
  419. numVertices += PolygonGeometryLibrary.subdivideRhumbLineCount(ellipsoid, positions[i], positions[(i + 1) % length], minDistance);
  420. }
  421. }
  422. topEdgeLength = (numVertices + length) * 3;
  423. edgePositions = new Array(topEdgeLength * 2);
  424. for (i = 0; i < length; i++) {
  425. p1 = positions[i];
  426. p2 = positions[(i + 1) % length];
  427. var tempPositions;
  428. if (arcType === ArcType.GEODESIC) {
  429. tempPositions = PolygonGeometryLibrary.subdivideLine(p1, p2, minDistance, computeWallIndicesSubdivided);
  430. } else if (arcType === ArcType.RHUMB) {
  431. tempPositions = PolygonGeometryLibrary.subdivideRhumbLine(ellipsoid, p1, p2, minDistance, computeWallIndicesSubdivided);
  432. }
  433. var tempPositionsLength = tempPositions.length;
  434. for (var j = 0; j < tempPositionsLength; ++j, ++index) {
  435. edgePositions[index] = tempPositions[j];
  436. edgePositions[index + topEdgeLength] = tempPositions[j];
  437. }
  438. edgePositions[index] = p2.x;
  439. edgePositions[index + topEdgeLength] = p2.x;
  440. ++index;
  441. edgePositions[index] = p2.y;
  442. edgePositions[index + topEdgeLength] = p2.y;
  443. ++index;
  444. edgePositions[index] = p2.z;
  445. edgePositions[index + topEdgeLength] = p2.z;
  446. ++index;
  447. }
  448. } else {
  449. topEdgeLength = length * 3 * 2;
  450. edgePositions = new Array(topEdgeLength * 2);
  451. for (i = 0; i < length; i++) {
  452. p1 = positions[i];
  453. p2 = positions[(i + 1) % length];
  454. edgePositions[index] = edgePositions[index + topEdgeLength] = p1.x;
  455. ++index;
  456. edgePositions[index] = edgePositions[index + topEdgeLength] = p1.y;
  457. ++index;
  458. edgePositions[index] = edgePositions[index + topEdgeLength] = p1.z;
  459. ++index;
  460. edgePositions[index] = edgePositions[index + topEdgeLength] = p2.x;
  461. ++index;
  462. edgePositions[index] = edgePositions[index + topEdgeLength] = p2.y;
  463. ++index;
  464. edgePositions[index] = edgePositions[index + topEdgeLength] = p2.z;
  465. ++index;
  466. }
  467. }
  468. length = edgePositions.length;
  469. var indices = IndexDatatype.createTypedArray(length / 3, length - positions.length * 6);
  470. var edgeIndex = 0;
  471. length /= 6;
  472. for (i = 0; i < length; i++) {
  473. var UL = i;
  474. var UR = UL + 1;
  475. var LL = UL + length;
  476. var LR = LL + 1;
  477. p1 = Cartesian3.fromArray(edgePositions, UL * 3, p1Scratch);
  478. p2 = Cartesian3.fromArray(edgePositions, UR * 3, p2Scratch);
  479. if (Cartesian3.equalsEpsilon(p1, p2, CesiumMath.EPSILON10, CesiumMath.EPSILON10)) {
  480. //skip corner
  481. continue;
  482. }
  483. indices[edgeIndex++] = UL;
  484. indices[edgeIndex++] = LL;
  485. indices[edgeIndex++] = UR;
  486. indices[edgeIndex++] = UR;
  487. indices[edgeIndex++] = LL;
  488. indices[edgeIndex++] = LR;
  489. }
  490. return new Geometry({
  491. attributes : new GeometryAttributes({
  492. position : new GeometryAttribute({
  493. componentDatatype : ComponentDatatype.DOUBLE,
  494. componentsPerAttribute : 3,
  495. values : edgePositions
  496. })
  497. }),
  498. indices : indices,
  499. primitiveType : PrimitiveType.TRIANGLES
  500. });
  501. };
  502. export default PolygonGeometryLibrary;