智慧水务管理系统 - 精河县供水工程综合管理平台

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. import binarySearch from "../Core/binarySearch.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import CesiumMath from "../Core/Math.js";
  4. import defined from "../Core/defined.js";
  5. import DeveloperError from "../Core/DeveloperError.js";
  6. import KeyframeNode from "./KeyframeNode.js";
  7. import Matrix3 from "../Core/Matrix3.js";
  8. import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
  9. /**
  10. * @alias SpatialNode
  11. * @constructor
  12. *
  13. * @param {number} level
  14. * @param {number} x
  15. * @param {number} y
  16. * @param {number} z
  17. * @param {SpatialNode} parent
  18. * @param {VoxelShape} shape
  19. * @param {Cartesian3} voxelDimensions
  20. *
  21. * @private
  22. */
  23. function SpatialNode(level, x, y, z, parent, shape, voxelDimensions) {
  24. /**
  25. * @type {SpatialNode[]}
  26. */
  27. this.children = undefined;
  28. this.parent = parent;
  29. this.level = level;
  30. this.x = x;
  31. this.y = y;
  32. this.z = z;
  33. /**
  34. * @type {Cartesian3}
  35. */
  36. this.dimensions = Cartesian3.clone(voxelDimensions);
  37. /**
  38. * @type {KeyframeNode[]}
  39. */
  40. this.keyframeNodes = [];
  41. /**
  42. * @type {KeyframeNode[]}
  43. */
  44. this.renderableKeyframeNodes = [];
  45. this.renderableKeyframeNodeLerp = 0.0;
  46. /**
  47. * @type {KeyframeNode}
  48. */
  49. this.renderableKeyframeNodePrevious = undefined;
  50. /**
  51. * @type {KeyframeNode}
  52. */
  53. this.renderableKeyframeNodeNext = undefined;
  54. this.orientedBoundingBox = new OrientedBoundingBox();
  55. this.approximateVoxelSize = 0.0;
  56. this.screenSpaceError = 0.0;
  57. this.visitedFrameNumber = -1;
  58. this.computeBoundingVolumes(shape);
  59. }
  60. const scratchObbHalfScale = new Cartesian3();
  61. /**
  62. * @param {VoxelShape} shape
  63. */
  64. SpatialNode.prototype.computeBoundingVolumes = function (shape) {
  65. this.orientedBoundingBox = shape.computeOrientedBoundingBoxForTile(
  66. this.level,
  67. this.x,
  68. this.y,
  69. this.z,
  70. this.orientedBoundingBox,
  71. );
  72. const halfScale = Matrix3.getScale(
  73. this.orientedBoundingBox.halfAxes,
  74. scratchObbHalfScale,
  75. );
  76. const maximumScale = 2.0 * Cartesian3.maximumComponent(halfScale);
  77. this.approximateVoxelSize =
  78. maximumScale / Cartesian3.minimumComponent(this.dimensions);
  79. };
  80. /**
  81. * @param {VoxelShape} shape The shape of the parent VoxelPrimitive
  82. * @private
  83. */
  84. SpatialNode.prototype.constructChildNodes = function (shape) {
  85. const { level, x, y, z } = this;
  86. const xMin = x * 2;
  87. const yMin = y * 2;
  88. const zMin = z * 2;
  89. const yMax = yMin + 1;
  90. const xMax = xMin + 1;
  91. const zMax = zMin + 1;
  92. const childLevel = level + 1;
  93. const childCoords = [
  94. [childLevel, xMin, yMin, zMin],
  95. [childLevel, xMax, yMin, zMin],
  96. [childLevel, xMin, yMax, zMin],
  97. [childLevel, xMax, yMax, zMin],
  98. [childLevel, xMin, yMin, zMax],
  99. [childLevel, xMax, yMin, zMax],
  100. [childLevel, xMin, yMax, zMax],
  101. [childLevel, xMax, yMax, zMax],
  102. ];
  103. this.children = childCoords.map(([level, x, y, z]) => {
  104. return new SpatialNode(level, x, y, z, this, shape, this.dimensions);
  105. });
  106. };
  107. /**
  108. * @param {FrameState} frameState
  109. * @param {number} visibilityPlaneMask
  110. * @returns {number} A plane mask as described in {@link CullingVolume#computeVisibilityWithPlaneMask}.
  111. */
  112. SpatialNode.prototype.visibility = function (frameState, visibilityPlaneMask) {
  113. const obb = this.orientedBoundingBox;
  114. const cullingVolume = frameState.cullingVolume;
  115. return cullingVolume.computeVisibilityWithPlaneMask(obb, visibilityPlaneMask);
  116. };
  117. /**
  118. * @param {Cartesian3} cameraPosition
  119. * @param {number} screenSpaceErrorMultiplier
  120. */
  121. SpatialNode.prototype.computeScreenSpaceError = function (
  122. cameraPosition,
  123. screenSpaceErrorMultiplier,
  124. ) {
  125. const obb = this.orientedBoundingBox;
  126. let distance = Math.sqrt(obb.distanceSquaredTo(cameraPosition));
  127. // Avoid divide-by-zero when viewer is inside the tile.
  128. distance = Math.max(distance, CesiumMath.EPSILON7);
  129. const approximateVoxelSize = this.approximateVoxelSize;
  130. const error = screenSpaceErrorMultiplier * (approximateVoxelSize / distance);
  131. this.screenSpaceError = error;
  132. };
  133. // This object imitates a KeyframeNode. Only used for binary search function.
  134. const scratchBinarySearchKeyframeNode = {
  135. keyframe: 0,
  136. };
  137. /**
  138. * Find the index of a given key frame position within an array of KeyframeNodes,
  139. * or the complement (~) of the index where it would be in the sorted array.
  140. * @param {number} keyframe
  141. * @param {KeyframeNode[]} keyframeNodes
  142. * @returns {number}
  143. * @private
  144. */
  145. function findKeyframeIndex(keyframe, keyframeNodes) {
  146. scratchBinarySearchKeyframeNode.keyframe = keyframe;
  147. return binarySearch(
  148. keyframeNodes,
  149. scratchBinarySearchKeyframeNode,
  150. KeyframeNode.searchComparator,
  151. );
  152. }
  153. /**
  154. * Computes the most suitable keyframes for rendering, balancing between temporal and visual quality.
  155. *
  156. * @param {number} keyframeLocation
  157. */
  158. SpatialNode.prototype.computeSurroundingRenderableKeyframeNodes = function (
  159. keyframeLocation,
  160. ) {
  161. let spatialNode = this;
  162. const startLevel = spatialNode.level;
  163. const targetKeyframePrev = Math.floor(keyframeLocation);
  164. const targetKeyframeNext = Math.ceil(keyframeLocation);
  165. let bestKeyframeNodePrev;
  166. let bestKeyframeNodeNext;
  167. let minimumDistancePrev = +Number.MAX_VALUE;
  168. let minimumDistanceNext = +Number.MAX_VALUE;
  169. while (defined(spatialNode)) {
  170. const { renderableKeyframeNodes } = spatialNode;
  171. if (renderableKeyframeNodes.length >= 1) {
  172. const indexPrev = getKeyframeIndexPrev(
  173. targetKeyframePrev,
  174. renderableKeyframeNodes,
  175. );
  176. const keyframeNodePrev = renderableKeyframeNodes[indexPrev];
  177. const indexNext =
  178. targetKeyframeNext === targetKeyframePrev ||
  179. targetKeyframePrev < keyframeNodePrev.keyframe
  180. ? indexPrev
  181. : Math.min(indexPrev + 1, renderableKeyframeNodes.length - 1);
  182. const keyframeNodeNext = renderableKeyframeNodes[indexNext];
  183. const distancePrev = targetKeyframePrev - keyframeNodePrev.keyframe;
  184. const weightedDistancePrev = getWeightedKeyframeDistance(
  185. startLevel - spatialNode.level,
  186. distancePrev,
  187. );
  188. if (weightedDistancePrev < minimumDistancePrev) {
  189. minimumDistancePrev = weightedDistancePrev;
  190. bestKeyframeNodePrev = keyframeNodePrev;
  191. }
  192. const distanceNext = keyframeNodeNext.keyframe - targetKeyframeNext;
  193. const weightedDistanceNext = getWeightedKeyframeDistance(
  194. startLevel - spatialNode.level,
  195. distanceNext,
  196. );
  197. if (weightedDistanceNext < minimumDistanceNext) {
  198. minimumDistanceNext = weightedDistanceNext;
  199. bestKeyframeNodeNext = keyframeNodeNext;
  200. }
  201. if (distancePrev === 0 && distanceNext === 0) {
  202. // Nothing higher up will be better, so break early.
  203. break;
  204. }
  205. }
  206. spatialNode = spatialNode.parent;
  207. }
  208. this.renderableKeyframeNodePrevious = bestKeyframeNodePrev;
  209. this.renderableKeyframeNodeNext = bestKeyframeNodeNext;
  210. if (!defined(bestKeyframeNodePrev) || !defined(bestKeyframeNodeNext)) {
  211. return;
  212. }
  213. const bestKeyframePrev = bestKeyframeNodePrev.keyframe;
  214. const bestKeyframeNext = bestKeyframeNodeNext.keyframe;
  215. this.renderableKeyframeNodeLerp =
  216. bestKeyframePrev === bestKeyframeNext
  217. ? 0.0
  218. : CesiumMath.clamp(
  219. (keyframeLocation - bestKeyframePrev) /
  220. (bestKeyframeNext - bestKeyframePrev),
  221. 0.0,
  222. 1.0,
  223. );
  224. };
  225. function getKeyframeIndexPrev(targetKeyframe, keyframeNodes) {
  226. const keyframeIndex = findKeyframeIndex(targetKeyframe, keyframeNodes);
  227. return keyframeIndex < 0
  228. ? CesiumMath.clamp(~keyframeIndex - 1, 0, keyframeNodes.length - 1)
  229. : keyframeIndex;
  230. }
  231. function getWeightedKeyframeDistance(levelDistance, keyframeDistance) {
  232. // Balance quality between visual (levelDistance) and temporal (keyframeDistance)
  233. const levelWeight = Math.exp(levelDistance * 4.0);
  234. // Keyframes on the opposite of the desired direction are deprioritized.
  235. const keyframeWeight = keyframeDistance >= 0 ? 1.0 : -200.0;
  236. return levelDistance * levelWeight + keyframeDistance * keyframeWeight;
  237. }
  238. /**
  239. * @param {number} frameNumber
  240. * @returns {boolean}
  241. */
  242. SpatialNode.prototype.isVisited = function (frameNumber) {
  243. return this.visitedFrameNumber === frameNumber;
  244. };
  245. /**
  246. * @param {number} keyframe
  247. */
  248. SpatialNode.prototype.createKeyframeNode = function (keyframe) {
  249. let index = findKeyframeIndex(keyframe, this.keyframeNodes);
  250. if (index < 0) {
  251. index = ~index; // convert to insertion index
  252. const keyframeNode = new KeyframeNode(this, keyframe);
  253. this.keyframeNodes.splice(index, 0, keyframeNode);
  254. }
  255. };
  256. /**
  257. * @param {KeyframeNode} keyframeNode
  258. * @param {Megatexture[]} megatextures
  259. */
  260. SpatialNode.prototype.destroyKeyframeNode = function (
  261. keyframeNode,
  262. megatextures,
  263. ) {
  264. const keyframe = keyframeNode.keyframe;
  265. const keyframeIndex = findKeyframeIndex(keyframe, this.keyframeNodes);
  266. if (keyframeIndex < 0) {
  267. throw new DeveloperError("Keyframe node does not exist.");
  268. }
  269. this.keyframeNodes.splice(keyframeIndex, 1);
  270. if (keyframeNode.megatextureIndex !== -1) {
  271. for (let i = 0; i < megatextures.length; i++) {
  272. megatextures[i].remove(keyframeNode.megatextureIndex);
  273. }
  274. const renderableKeyframeNodeIndex = findKeyframeIndex(
  275. keyframe,
  276. this.renderableKeyframeNodes,
  277. );
  278. if (renderableKeyframeNodeIndex < 0) {
  279. throw new DeveloperError("Renderable keyframe node does not exist.");
  280. }
  281. this.renderableKeyframeNodes.splice(renderableKeyframeNodeIndex, 1);
  282. }
  283. keyframeNode.unload();
  284. };
  285. /**
  286. * @param {KeyframeNode} keyframeNode
  287. * @param {Megatexture[]} megatextures
  288. */
  289. SpatialNode.prototype.addKeyframeNodeToMegatextures = function (
  290. keyframeNode,
  291. megatextures,
  292. ) {
  293. if (
  294. keyframeNode.megatextureIndex !== -1 ||
  295. keyframeNode.content.metadata.length !== megatextures.length
  296. ) {
  297. throw new DeveloperError("Keyframe node cannot be added to megatexture");
  298. }
  299. const { metadata } = keyframeNode.content;
  300. for (let i = 0; i < megatextures.length; i++) {
  301. const megatexture = megatextures[i];
  302. keyframeNode.megatextureIndex = megatexture.add(metadata[i]);
  303. }
  304. const renderableKeyframeNodes = this.renderableKeyframeNodes;
  305. let renderableKeyframeNodeIndex = findKeyframeIndex(
  306. keyframeNode.keyframe,
  307. renderableKeyframeNodes,
  308. );
  309. if (renderableKeyframeNodeIndex >= 0) {
  310. throw new DeveloperError("Keyframe already renderable");
  311. }
  312. renderableKeyframeNodeIndex = ~renderableKeyframeNodeIndex;
  313. renderableKeyframeNodes.splice(renderableKeyframeNodeIndex, 0, keyframeNode);
  314. };
  315. /**
  316. * @param {number} frameNumber
  317. * @returns {boolean}
  318. */
  319. SpatialNode.prototype.isRenderable = function (frameNumber) {
  320. const previousNode = this.renderableKeyframeNodePrevious;
  321. const nextNode = this.renderableKeyframeNodeNext;
  322. const level = this.level;
  323. return (
  324. defined(previousNode) &&
  325. defined(nextNode) &&
  326. (previousNode.spatialNode.level === level ||
  327. nextNode.spatialNode.level === level) &&
  328. this.visitedFrameNumber === frameNumber
  329. );
  330. };
  331. export default SpatialNode;