PolylineVolumeGeometryLibrary.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. import Cartesian2 from './Cartesian2.js';
  2. import Cartesian3 from './Cartesian3.js';
  3. import Cartesian4 from './Cartesian4.js';
  4. import Cartographic from './Cartographic.js';
  5. import CornerType from './CornerType.js';
  6. import EllipsoidTangentPlane from './EllipsoidTangentPlane.js';
  7. import CesiumMath from './Math.js';
  8. import Matrix3 from './Matrix3.js';
  9. import Matrix4 from './Matrix4.js';
  10. import PolylinePipeline from './PolylinePipeline.js';
  11. import Quaternion from './Quaternion.js';
  12. import Transforms from './Transforms.js';
  13. var scratch2Array = [new Cartesian3(), new Cartesian3()];
  14. var scratchCartesian1 = new Cartesian3();
  15. var scratchCartesian2 = new Cartesian3();
  16. var scratchCartesian3 = new Cartesian3();
  17. var scratchCartesian4 = new Cartesian3();
  18. var scratchCartesian5 = new Cartesian3();
  19. var scratchCartesian6 = new Cartesian3();
  20. var scratchCartesian7 = new Cartesian3();
  21. var scratchCartesian8 = new Cartesian3();
  22. var scratchCartesian9 = new Cartesian3();
  23. var scratch1 = new Cartesian3();
  24. var scratch2 = new Cartesian3();
  25. /**
  26. * @private
  27. */
  28. var PolylineVolumeGeometryLibrary = {};
  29. var cartographic = new Cartographic();
  30. function scaleToSurface(positions, ellipsoid) {
  31. var heights = new Array(positions.length);
  32. for (var i = 0; i < positions.length; i++) {
  33. var pos = positions[i];
  34. cartographic = ellipsoid.cartesianToCartographic(pos, cartographic);
  35. heights[i] = cartographic.height;
  36. positions[i] = ellipsoid.scaleToGeodeticSurface(pos, pos);
  37. }
  38. return heights;
  39. }
  40. function subdivideHeights(points, h0, h1, granularity) {
  41. var p0 = points[0];
  42. var p1 = points[1];
  43. var angleBetween = Cartesian3.angleBetween(p0, p1);
  44. var numPoints = Math.ceil(angleBetween / granularity);
  45. var heights = new Array(numPoints);
  46. var i;
  47. if (h0 === h1) {
  48. for (i = 0; i < numPoints; i++) {
  49. heights[i] = h0;
  50. }
  51. heights.push(h1);
  52. return heights;
  53. }
  54. var dHeight = h1 - h0;
  55. var heightPerVertex = dHeight / (numPoints);
  56. for (i = 1; i < numPoints; i++) {
  57. var h = h0 + i * heightPerVertex;
  58. heights[i] = h;
  59. }
  60. heights[0] = h0;
  61. heights.push(h1);
  62. return heights;
  63. }
  64. var nextScratch = new Cartesian3();
  65. var prevScratch = new Cartesian3();
  66. function computeRotationAngle(start, end, position, ellipsoid) {
  67. var tangentPlane = new EllipsoidTangentPlane(position, ellipsoid);
  68. var next = tangentPlane.projectPointOntoPlane(Cartesian3.add(position, start, nextScratch), nextScratch);
  69. var prev = tangentPlane.projectPointOntoPlane(Cartesian3.add(position, end, prevScratch), prevScratch);
  70. var angle = Cartesian2.angleBetween(next, prev);
  71. return (prev.x * next.y - prev.y * next.x >= 0.0) ? -angle : angle;
  72. }
  73. var negativeX = new Cartesian3(-1, 0, 0);
  74. var transform = new Matrix4();
  75. var translation = new Matrix4();
  76. var rotationZ = new Matrix3();
  77. var scaleMatrix = Matrix3.IDENTITY.clone();
  78. var westScratch = new Cartesian3();
  79. var finalPosScratch = new Cartesian4();
  80. var heightCartesian = new Cartesian3();
  81. function addPosition(center, left, shape, finalPositions, ellipsoid, height, xScalar, repeat) {
  82. var west = westScratch;
  83. var finalPosition = finalPosScratch;
  84. transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, transform);
  85. west = Matrix4.multiplyByPointAsVector(transform, negativeX, west);
  86. west = Cartesian3.normalize(west, west);
  87. var angle = computeRotationAngle(west, left, center, ellipsoid);
  88. rotationZ = Matrix3.fromRotationZ(angle, rotationZ);
  89. heightCartesian.z = height;
  90. transform = Matrix4.multiplyTransformation(transform, Matrix4.fromRotationTranslation(rotationZ, heightCartesian, translation), transform);
  91. var scale = scaleMatrix;
  92. scale[0] = xScalar;
  93. for (var j = 0; j < repeat; j++) {
  94. for (var i = 0; i < shape.length; i += 3) {
  95. finalPosition = Cartesian3.fromArray(shape, i, finalPosition);
  96. finalPosition = Matrix3.multiplyByVector(scale, finalPosition, finalPosition);
  97. finalPosition = Matrix4.multiplyByPoint(transform, finalPosition, finalPosition);
  98. finalPositions.push(finalPosition.x, finalPosition.y, finalPosition.z);
  99. }
  100. }
  101. return finalPositions;
  102. }
  103. var centerScratch = new Cartesian3();
  104. function addPositions(centers, left, shape, finalPositions, ellipsoid, heights, xScalar) {
  105. for (var i = 0; i < centers.length; i += 3) {
  106. var center = Cartesian3.fromArray(centers, i, centerScratch);
  107. finalPositions = addPosition(center, left, shape, finalPositions, ellipsoid, heights[i / 3], xScalar, 1);
  108. }
  109. return finalPositions;
  110. }
  111. function convertShapeTo3DDuplicate(shape2D, boundingRectangle) { //orientate 2D shape to XZ plane center at (0, 0, 0), duplicate points
  112. var length = shape2D.length;
  113. var shape = new Array(length * 6);
  114. var index = 0;
  115. var xOffset = boundingRectangle.x + boundingRectangle.width / 2;
  116. var yOffset = boundingRectangle.y + boundingRectangle.height / 2;
  117. var point = shape2D[0];
  118. shape[index++] = point.x - xOffset;
  119. shape[index++] = 0.0;
  120. shape[index++] = point.y - yOffset;
  121. for (var i = 1; i < length; i++) {
  122. point = shape2D[i];
  123. var x = point.x - xOffset;
  124. var z = point.y - yOffset;
  125. shape[index++] = x;
  126. shape[index++] = 0.0;
  127. shape[index++] = z;
  128. shape[index++] = x;
  129. shape[index++] = 0.0;
  130. shape[index++] = z;
  131. }
  132. point = shape2D[0];
  133. shape[index++] = point.x - xOffset;
  134. shape[index++] = 0.0;
  135. shape[index++] = point.y - yOffset;
  136. return shape;
  137. }
  138. function convertShapeTo3D(shape2D, boundingRectangle) { //orientate 2D shape to XZ plane center at (0, 0, 0)
  139. var length = shape2D.length;
  140. var shape = new Array(length * 3);
  141. var index = 0;
  142. var xOffset = boundingRectangle.x + boundingRectangle.width / 2;
  143. var yOffset = boundingRectangle.y + boundingRectangle.height / 2;
  144. for (var i = 0; i < length; i++) {
  145. shape[index++] = shape2D[i].x - xOffset;
  146. shape[index++] = 0;
  147. shape[index++] = shape2D[i].y - yOffset;
  148. }
  149. return shape;
  150. }
  151. var quaterion = new Quaternion();
  152. var startPointScratch = new Cartesian3();
  153. var rotMatrix = new Matrix3();
  154. function computeRoundCorner(pivot, startPoint, endPoint, cornerType, leftIsOutside, ellipsoid, finalPositions, shape, height, duplicatePoints) {
  155. var angle = Cartesian3.angleBetween(Cartesian3.subtract(startPoint, pivot, scratch1), Cartesian3.subtract(endPoint, pivot, scratch2));
  156. var granularity = (cornerType === CornerType.BEVELED) ? 0 : Math.ceil(angle / CesiumMath.toRadians(5));
  157. var m;
  158. if (leftIsOutside) {
  159. m = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(Cartesian3.negate(pivot, scratch1), angle / (granularity + 1), quaterion), rotMatrix);
  160. } else {
  161. m = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(pivot, angle / (granularity + 1), quaterion), rotMatrix);
  162. }
  163. var left;
  164. var surfacePoint;
  165. startPoint = Cartesian3.clone(startPoint, startPointScratch);
  166. if (granularity > 0) {
  167. var repeat = duplicatePoints ? 2 : 1;
  168. for (var i = 0; i < granularity; i++) {
  169. startPoint = Matrix3.multiplyByVector(m, startPoint, startPoint);
  170. left = Cartesian3.subtract(startPoint, pivot, scratch1);
  171. left = Cartesian3.normalize(left, left);
  172. if (!leftIsOutside) {
  173. left = Cartesian3.negate(left, left);
  174. }
  175. surfacePoint = ellipsoid.scaleToGeodeticSurface(startPoint, scratch2);
  176. finalPositions = addPosition(surfacePoint, left, shape, finalPositions, ellipsoid, height, 1, repeat);
  177. }
  178. } else {
  179. left = Cartesian3.subtract(startPoint, pivot, scratch1);
  180. left = Cartesian3.normalize(left, left);
  181. if (!leftIsOutside) {
  182. left = Cartesian3.negate(left, left);
  183. }
  184. surfacePoint = ellipsoid.scaleToGeodeticSurface(startPoint, scratch2);
  185. finalPositions = addPosition(surfacePoint, left, shape, finalPositions, ellipsoid, height, 1, 1);
  186. endPoint = Cartesian3.clone(endPoint, startPointScratch);
  187. left = Cartesian3.subtract(endPoint, pivot, scratch1);
  188. left = Cartesian3.normalize(left, left);
  189. if (!leftIsOutside) {
  190. left = Cartesian3.negate(left, left);
  191. }
  192. surfacePoint = ellipsoid.scaleToGeodeticSurface(endPoint, scratch2);
  193. finalPositions = addPosition(surfacePoint, left, shape, finalPositions, ellipsoid, height, 1, 1);
  194. }
  195. return finalPositions;
  196. }
  197. PolylineVolumeGeometryLibrary.removeDuplicatesFromShape = function(shapePositions) {
  198. var length = shapePositions.length;
  199. var cleanedPositions = [];
  200. for (var i0 = length - 1, i1 = 0; i1 < length; i0 = i1++) {
  201. var v0 = shapePositions[i0];
  202. var v1 = shapePositions[i1];
  203. if (!Cartesian2.equals(v0, v1)) {
  204. cleanedPositions.push(v1); // Shallow copy!
  205. }
  206. }
  207. return cleanedPositions;
  208. };
  209. PolylineVolumeGeometryLibrary.angleIsGreaterThanPi = function(forward, backward, position, ellipsoid) {
  210. var tangentPlane = new EllipsoidTangentPlane(position, ellipsoid);
  211. var next = tangentPlane.projectPointOntoPlane(Cartesian3.add(position, forward, nextScratch), nextScratch);
  212. var prev = tangentPlane.projectPointOntoPlane(Cartesian3.add(position, backward, prevScratch), prevScratch);
  213. return ((prev.x * next.y) - (prev.y * next.x)) >= 0.0;
  214. };
  215. var scratchForwardProjection = new Cartesian3();
  216. var scratchBackwardProjection = new Cartesian3();
  217. PolylineVolumeGeometryLibrary.computePositions = function(positions, shape2D, boundingRectangle, geometry, duplicatePoints) {
  218. var ellipsoid = geometry._ellipsoid;
  219. var heights = scaleToSurface(positions, ellipsoid);
  220. var granularity = geometry._granularity;
  221. var cornerType = geometry._cornerType;
  222. var shapeForSides = duplicatePoints ? convertShapeTo3DDuplicate(shape2D, boundingRectangle) : convertShapeTo3D(shape2D, boundingRectangle);
  223. var shapeForEnds = duplicatePoints ? convertShapeTo3D(shape2D, boundingRectangle) : undefined;
  224. var heightOffset = boundingRectangle.height / 2;
  225. var width = boundingRectangle.width / 2;
  226. var length = positions.length;
  227. var finalPositions = [];
  228. var ends = duplicatePoints ? [] : undefined;
  229. var forward = scratchCartesian1;
  230. var backward = scratchCartesian2;
  231. var cornerDirection = scratchCartesian3;
  232. var surfaceNormal = scratchCartesian4;
  233. var pivot = scratchCartesian5;
  234. var start = scratchCartesian6;
  235. var end = scratchCartesian7;
  236. var left = scratchCartesian8;
  237. var previousPosition = scratchCartesian9;
  238. var position = positions[0];
  239. var nextPosition = positions[1];
  240. surfaceNormal = ellipsoid.geodeticSurfaceNormal(position, surfaceNormal);
  241. forward = Cartesian3.subtract(nextPosition, position, forward);
  242. forward = Cartesian3.normalize(forward, forward);
  243. left = Cartesian3.cross(surfaceNormal, forward, left);
  244. left = Cartesian3.normalize(left, left);
  245. var h0 = heights[0];
  246. var h1 = heights[1];
  247. if (duplicatePoints) {
  248. ends = addPosition(position, left, shapeForEnds, ends, ellipsoid, h0 + heightOffset, 1, 1);
  249. }
  250. previousPosition = Cartesian3.clone(position, previousPosition);
  251. position = nextPosition;
  252. backward = Cartesian3.negate(forward, backward);
  253. var subdividedHeights;
  254. var subdividedPositions;
  255. for (var i = 1; i < length - 1; i++) {
  256. var repeat = duplicatePoints ? 2 : 1;
  257. nextPosition = positions[i + 1];
  258. forward = Cartesian3.subtract(nextPosition, position, forward);
  259. forward = Cartesian3.normalize(forward, forward);
  260. cornerDirection = Cartesian3.add(forward, backward, cornerDirection);
  261. cornerDirection = Cartesian3.normalize(cornerDirection, cornerDirection);
  262. surfaceNormal = ellipsoid.geodeticSurfaceNormal(position, surfaceNormal);
  263. var forwardProjection = Cartesian3.multiplyByScalar(surfaceNormal, Cartesian3.dot(forward, surfaceNormal), scratchForwardProjection);
  264. Cartesian3.subtract(forward, forwardProjection, forwardProjection);
  265. Cartesian3.normalize(forwardProjection, forwardProjection);
  266. var backwardProjection = Cartesian3.multiplyByScalar(surfaceNormal, Cartesian3.dot(backward, surfaceNormal), scratchBackwardProjection);
  267. Cartesian3.subtract(backward, backwardProjection, backwardProjection);
  268. Cartesian3.normalize(backwardProjection, backwardProjection);
  269. var doCorner = !CesiumMath.equalsEpsilon(Math.abs(Cartesian3.dot(forwardProjection, backwardProjection)), 1.0, CesiumMath.EPSILON7);
  270. if (doCorner) {
  271. cornerDirection = Cartesian3.cross(cornerDirection, surfaceNormal, cornerDirection);
  272. cornerDirection = Cartesian3.cross(surfaceNormal, cornerDirection, cornerDirection);
  273. cornerDirection = Cartesian3.normalize(cornerDirection, cornerDirection);
  274. var scalar = 1 / Math.max(0.25, (Cartesian3.magnitude(Cartesian3.cross(cornerDirection, backward, scratch1))));
  275. var leftIsOutside = PolylineVolumeGeometryLibrary.angleIsGreaterThanPi(forward, backward, position, ellipsoid);
  276. if (leftIsOutside) {
  277. pivot = Cartesian3.add(position, Cartesian3.multiplyByScalar(cornerDirection, scalar * width, cornerDirection), pivot);
  278. start = Cartesian3.add(pivot, Cartesian3.multiplyByScalar(left, width, start), start);
  279. scratch2Array[0] = Cartesian3.clone(previousPosition, scratch2Array[0]);
  280. scratch2Array[1] = Cartesian3.clone(start, scratch2Array[1]);
  281. subdividedHeights = subdivideHeights(scratch2Array, h0 + heightOffset, h1 + heightOffset, granularity);
  282. subdividedPositions = PolylinePipeline.generateArc({
  283. positions: scratch2Array,
  284. granularity: granularity,
  285. ellipsoid: ellipsoid
  286. });
  287. finalPositions = addPositions(subdividedPositions, left, shapeForSides, finalPositions, ellipsoid, subdividedHeights, 1);
  288. left = Cartesian3.cross(surfaceNormal, forward, left);
  289. left = Cartesian3.normalize(left, left);
  290. end = Cartesian3.add(pivot, Cartesian3.multiplyByScalar(left, width, end), end);
  291. if (cornerType === CornerType.ROUNDED || cornerType === CornerType.BEVELED) {
  292. computeRoundCorner(pivot, start, end, cornerType, leftIsOutside, ellipsoid, finalPositions, shapeForSides, h1 + heightOffset, duplicatePoints);
  293. } else {
  294. cornerDirection = Cartesian3.negate(cornerDirection, cornerDirection);
  295. finalPositions = addPosition(position, cornerDirection, shapeForSides, finalPositions, ellipsoid, h1 + heightOffset, scalar, repeat);
  296. }
  297. previousPosition = Cartesian3.clone(end, previousPosition);
  298. } else {
  299. pivot = Cartesian3.add(position, Cartesian3.multiplyByScalar(cornerDirection, scalar * width, cornerDirection), pivot);
  300. start = Cartesian3.add(pivot, Cartesian3.multiplyByScalar(left, -width, start), start);
  301. scratch2Array[0] = Cartesian3.clone(previousPosition, scratch2Array[0]);
  302. scratch2Array[1] = Cartesian3.clone(start, scratch2Array[1]);
  303. subdividedHeights = subdivideHeights(scratch2Array, h0 + heightOffset, h1 + heightOffset, granularity);
  304. subdividedPositions = PolylinePipeline.generateArc({
  305. positions: scratch2Array,
  306. granularity: granularity,
  307. ellipsoid: ellipsoid
  308. });
  309. finalPositions = addPositions(subdividedPositions, left, shapeForSides, finalPositions, ellipsoid, subdividedHeights, 1);
  310. left = Cartesian3.cross(surfaceNormal, forward, left);
  311. left = Cartesian3.normalize(left, left);
  312. end = Cartesian3.add(pivot, Cartesian3.multiplyByScalar(left, -width, end), end);
  313. if (cornerType === CornerType.ROUNDED || cornerType === CornerType.BEVELED) {
  314. computeRoundCorner(pivot, start, end, cornerType, leftIsOutside, ellipsoid, finalPositions, shapeForSides, h1 + heightOffset, duplicatePoints);
  315. } else {
  316. finalPositions = addPosition(position, cornerDirection, shapeForSides, finalPositions, ellipsoid, h1 + heightOffset, scalar, repeat);
  317. }
  318. previousPosition = Cartesian3.clone(end, previousPosition);
  319. }
  320. backward = Cartesian3.negate(forward, backward);
  321. } else {
  322. finalPositions = addPosition(previousPosition, left, shapeForSides, finalPositions, ellipsoid, h0 + heightOffset, 1, 1);
  323. previousPosition = position;
  324. }
  325. h0 = h1;
  326. h1 = heights[i + 1];
  327. position = nextPosition;
  328. }
  329. scratch2Array[0] = Cartesian3.clone(previousPosition, scratch2Array[0]);
  330. scratch2Array[1] = Cartesian3.clone(position, scratch2Array[1]);
  331. subdividedHeights = subdivideHeights(scratch2Array, h0 + heightOffset, h1 + heightOffset, granularity);
  332. subdividedPositions = PolylinePipeline.generateArc({
  333. positions: scratch2Array,
  334. granularity: granularity,
  335. ellipsoid: ellipsoid
  336. });
  337. finalPositions = addPositions(subdividedPositions, left, shapeForSides, finalPositions, ellipsoid, subdividedHeights, 1);
  338. if (duplicatePoints) {
  339. ends = addPosition(position, left, shapeForEnds, ends, ellipsoid, h1 + heightOffset, 1, 1);
  340. }
  341. length = finalPositions.length;
  342. var posLength = duplicatePoints ? length + ends.length : length;
  343. var combinedPositions = new Float64Array(posLength);
  344. combinedPositions.set(finalPositions);
  345. if (duplicatePoints) {
  346. combinedPositions.set(ends, length);
  347. }
  348. return combinedPositions;
  349. };
  350. export default PolylineVolumeGeometryLibrary;