EllipseGeometryLibrary.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. import Cartesian3 from "./Cartesian3.js";
  2. import CesiumMath from "./Math.js";
  3. import Matrix3 from "./Matrix3.js";
  4. import Quaternion from "./Quaternion.js";
  5. var EllipseGeometryLibrary = {};
  6. var rotAxis = new Cartesian3();
  7. var tempVec = new Cartesian3();
  8. var unitQuat = new Quaternion();
  9. var rotMtx = new Matrix3();
  10. function pointOnEllipsoid(
  11. theta,
  12. rotation,
  13. northVec,
  14. eastVec,
  15. aSqr,
  16. ab,
  17. bSqr,
  18. mag,
  19. unitPos,
  20. result
  21. ) {
  22. var azimuth = theta + rotation;
  23. Cartesian3.multiplyByScalar(eastVec, Math.cos(azimuth), rotAxis);
  24. Cartesian3.multiplyByScalar(northVec, Math.sin(azimuth), tempVec);
  25. Cartesian3.add(rotAxis, tempVec, rotAxis);
  26. var cosThetaSquared = Math.cos(theta);
  27. cosThetaSquared = cosThetaSquared * cosThetaSquared;
  28. var sinThetaSquared = Math.sin(theta);
  29. sinThetaSquared = sinThetaSquared * sinThetaSquared;
  30. var radius = ab / Math.sqrt(bSqr * cosThetaSquared + aSqr * sinThetaSquared);
  31. var angle = radius / mag;
  32. // Create the quaternion to rotate the position vector to the boundary of the ellipse.
  33. Quaternion.fromAxisAngle(rotAxis, angle, unitQuat);
  34. Matrix3.fromQuaternion(unitQuat, rotMtx);
  35. Matrix3.multiplyByVector(rotMtx, unitPos, result);
  36. Cartesian3.normalize(result, result);
  37. Cartesian3.multiplyByScalar(result, mag, result);
  38. return result;
  39. }
  40. var scratchCartesian1 = new Cartesian3();
  41. var scratchCartesian2 = new Cartesian3();
  42. var scratchCartesian3 = new Cartesian3();
  43. var scratchNormal = new Cartesian3();
  44. /**
  45. * Returns the positions raised to the given heights
  46. * @private
  47. */
  48. EllipseGeometryLibrary.raisePositionsToHeight = function (
  49. positions,
  50. options,
  51. extrude
  52. ) {
  53. var ellipsoid = options.ellipsoid;
  54. var height = options.height;
  55. var extrudedHeight = options.extrudedHeight;
  56. var size = extrude ? (positions.length / 3) * 2 : positions.length / 3;
  57. var finalPositions = new Float64Array(size * 3);
  58. var length = positions.length;
  59. var bottomOffset = extrude ? length : 0;
  60. for (var i = 0; i < length; i += 3) {
  61. var i1 = i + 1;
  62. var i2 = i + 2;
  63. var position = Cartesian3.fromArray(positions, i, scratchCartesian1);
  64. ellipsoid.scaleToGeodeticSurface(position, position);
  65. var extrudedPosition = Cartesian3.clone(position, scratchCartesian2);
  66. var normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal);
  67. var scaledNormal = Cartesian3.multiplyByScalar(
  68. normal,
  69. height,
  70. scratchCartesian3
  71. );
  72. Cartesian3.add(position, scaledNormal, position);
  73. if (extrude) {
  74. Cartesian3.multiplyByScalar(normal, extrudedHeight, scaledNormal);
  75. Cartesian3.add(extrudedPosition, scaledNormal, extrudedPosition);
  76. finalPositions[i + bottomOffset] = extrudedPosition.x;
  77. finalPositions[i1 + bottomOffset] = extrudedPosition.y;
  78. finalPositions[i2 + bottomOffset] = extrudedPosition.z;
  79. }
  80. finalPositions[i] = position.x;
  81. finalPositions[i1] = position.y;
  82. finalPositions[i2] = position.z;
  83. }
  84. return finalPositions;
  85. };
  86. var unitPosScratch = new Cartesian3();
  87. var eastVecScratch = new Cartesian3();
  88. var northVecScratch = new Cartesian3();
  89. /**
  90. * Returns an array of positions that make up the ellipse.
  91. * @private
  92. */
  93. EllipseGeometryLibrary.computeEllipsePositions = function (
  94. options,
  95. addFillPositions,
  96. addEdgePositions
  97. ) {
  98. var semiMinorAxis = options.semiMinorAxis;
  99. var semiMajorAxis = options.semiMajorAxis;
  100. var rotation = options.rotation;
  101. var center = options.center;
  102. // Computing the arc-length of the ellipse is too expensive to be practical. Estimating it using the
  103. // arc length of the sphere is too inaccurate and creates sharp edges when either the semi-major or
  104. // semi-minor axis is much bigger than the other. Instead, scale the angle delta to make
  105. // the distance along the ellipse boundary more closely match the granularity.
  106. var granularity = options.granularity * 8.0;
  107. var aSqr = semiMinorAxis * semiMinorAxis;
  108. var bSqr = semiMajorAxis * semiMajorAxis;
  109. var ab = semiMajorAxis * semiMinorAxis;
  110. var mag = Cartesian3.magnitude(center);
  111. var unitPos = Cartesian3.normalize(center, unitPosScratch);
  112. var eastVec = Cartesian3.cross(Cartesian3.UNIT_Z, center, eastVecScratch);
  113. eastVec = Cartesian3.normalize(eastVec, eastVec);
  114. var northVec = Cartesian3.cross(unitPos, eastVec, northVecScratch);
  115. // The number of points in the first quadrant
  116. var numPts = 1 + Math.ceil(CesiumMath.PI_OVER_TWO / granularity);
  117. var deltaTheta = CesiumMath.PI_OVER_TWO / (numPts - 1);
  118. var theta = CesiumMath.PI_OVER_TWO - numPts * deltaTheta;
  119. if (theta < 0.0) {
  120. numPts -= Math.ceil(Math.abs(theta) / deltaTheta);
  121. }
  122. // If the number of points were three, the ellipse
  123. // would be tessellated like below:
  124. //
  125. // *---*
  126. // / | \ | \
  127. // *---*---*---*
  128. // / | \ | \ | \ | \
  129. // / .*---*---*---*. \
  130. // * ` | \ | \ | \ | `*
  131. // \`.*---*---*---*.`/
  132. // \ | \ | \ | \ | /
  133. // *---*---*---*
  134. // \ | \ | /
  135. // *---*
  136. // The first and last column have one position and fan to connect to the adjacent column.
  137. // Each other vertical column contains an even number of positions.
  138. var size = 2 * (numPts * (numPts + 2));
  139. var positions = addFillPositions ? new Array(size * 3) : undefined;
  140. var positionIndex = 0;
  141. var position = scratchCartesian1;
  142. var reflectedPosition = scratchCartesian2;
  143. var outerPositionsLength = numPts * 4 * 3;
  144. var outerRightIndex = outerPositionsLength - 1;
  145. var outerLeftIndex = 0;
  146. var outerPositions = addEdgePositions
  147. ? new Array(outerPositionsLength)
  148. : undefined;
  149. var i;
  150. var j;
  151. var numInterior;
  152. var t;
  153. var interiorPosition;
  154. // Compute points in the 'eastern' half of the ellipse
  155. theta = CesiumMath.PI_OVER_TWO;
  156. position = pointOnEllipsoid(
  157. theta,
  158. rotation,
  159. northVec,
  160. eastVec,
  161. aSqr,
  162. ab,
  163. bSqr,
  164. mag,
  165. unitPos,
  166. position
  167. );
  168. if (addFillPositions) {
  169. positions[positionIndex++] = position.x;
  170. positions[positionIndex++] = position.y;
  171. positions[positionIndex++] = position.z;
  172. }
  173. if (addEdgePositions) {
  174. outerPositions[outerRightIndex--] = position.z;
  175. outerPositions[outerRightIndex--] = position.y;
  176. outerPositions[outerRightIndex--] = position.x;
  177. }
  178. theta = CesiumMath.PI_OVER_TWO - deltaTheta;
  179. for (i = 1; i < numPts + 1; ++i) {
  180. position = pointOnEllipsoid(
  181. theta,
  182. rotation,
  183. northVec,
  184. eastVec,
  185. aSqr,
  186. ab,
  187. bSqr,
  188. mag,
  189. unitPos,
  190. position
  191. );
  192. reflectedPosition = pointOnEllipsoid(
  193. Math.PI - theta,
  194. rotation,
  195. northVec,
  196. eastVec,
  197. aSqr,
  198. ab,
  199. bSqr,
  200. mag,
  201. unitPos,
  202. reflectedPosition
  203. );
  204. if (addFillPositions) {
  205. positions[positionIndex++] = position.x;
  206. positions[positionIndex++] = position.y;
  207. positions[positionIndex++] = position.z;
  208. numInterior = 2 * i + 2;
  209. for (j = 1; j < numInterior - 1; ++j) {
  210. t = j / (numInterior - 1);
  211. interiorPosition = Cartesian3.lerp(
  212. position,
  213. reflectedPosition,
  214. t,
  215. scratchCartesian3
  216. );
  217. positions[positionIndex++] = interiorPosition.x;
  218. positions[positionIndex++] = interiorPosition.y;
  219. positions[positionIndex++] = interiorPosition.z;
  220. }
  221. positions[positionIndex++] = reflectedPosition.x;
  222. positions[positionIndex++] = reflectedPosition.y;
  223. positions[positionIndex++] = reflectedPosition.z;
  224. }
  225. if (addEdgePositions) {
  226. outerPositions[outerRightIndex--] = position.z;
  227. outerPositions[outerRightIndex--] = position.y;
  228. outerPositions[outerRightIndex--] = position.x;
  229. outerPositions[outerLeftIndex++] = reflectedPosition.x;
  230. outerPositions[outerLeftIndex++] = reflectedPosition.y;
  231. outerPositions[outerLeftIndex++] = reflectedPosition.z;
  232. }
  233. theta = CesiumMath.PI_OVER_TWO - (i + 1) * deltaTheta;
  234. }
  235. // Compute points in the 'western' half of the ellipse
  236. for (i = numPts; i > 1; --i) {
  237. theta = CesiumMath.PI_OVER_TWO - (i - 1) * deltaTheta;
  238. position = pointOnEllipsoid(
  239. -theta,
  240. rotation,
  241. northVec,
  242. eastVec,
  243. aSqr,
  244. ab,
  245. bSqr,
  246. mag,
  247. unitPos,
  248. position
  249. );
  250. reflectedPosition = pointOnEllipsoid(
  251. theta + Math.PI,
  252. rotation,
  253. northVec,
  254. eastVec,
  255. aSqr,
  256. ab,
  257. bSqr,
  258. mag,
  259. unitPos,
  260. reflectedPosition
  261. );
  262. if (addFillPositions) {
  263. positions[positionIndex++] = position.x;
  264. positions[positionIndex++] = position.y;
  265. positions[positionIndex++] = position.z;
  266. numInterior = 2 * (i - 1) + 2;
  267. for (j = 1; j < numInterior - 1; ++j) {
  268. t = j / (numInterior - 1);
  269. interiorPosition = Cartesian3.lerp(
  270. position,
  271. reflectedPosition,
  272. t,
  273. scratchCartesian3
  274. );
  275. positions[positionIndex++] = interiorPosition.x;
  276. positions[positionIndex++] = interiorPosition.y;
  277. positions[positionIndex++] = interiorPosition.z;
  278. }
  279. positions[positionIndex++] = reflectedPosition.x;
  280. positions[positionIndex++] = reflectedPosition.y;
  281. positions[positionIndex++] = reflectedPosition.z;
  282. }
  283. if (addEdgePositions) {
  284. outerPositions[outerRightIndex--] = position.z;
  285. outerPositions[outerRightIndex--] = position.y;
  286. outerPositions[outerRightIndex--] = position.x;
  287. outerPositions[outerLeftIndex++] = reflectedPosition.x;
  288. outerPositions[outerLeftIndex++] = reflectedPosition.y;
  289. outerPositions[outerLeftIndex++] = reflectedPosition.z;
  290. }
  291. }
  292. theta = CesiumMath.PI_OVER_TWO;
  293. position = pointOnEllipsoid(
  294. -theta,
  295. rotation,
  296. northVec,
  297. eastVec,
  298. aSqr,
  299. ab,
  300. bSqr,
  301. mag,
  302. unitPos,
  303. position
  304. );
  305. var r = {};
  306. if (addFillPositions) {
  307. positions[positionIndex++] = position.x;
  308. positions[positionIndex++] = position.y;
  309. positions[positionIndex++] = position.z;
  310. r.positions = positions;
  311. r.numPts = numPts;
  312. }
  313. if (addEdgePositions) {
  314. outerPositions[outerRightIndex--] = position.z;
  315. outerPositions[outerRightIndex--] = position.y;
  316. outerPositions[outerRightIndex--] = position.x;
  317. r.outerPositions = outerPositions;
  318. }
  319. return r;
  320. };
  321. export default EllipseGeometryLibrary;