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

PathVisualizer.js 31KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135
  1. import AssociativeArray from "../Core/AssociativeArray.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import CesiumMath from "../Core/Math.js";
  4. import defined from "../Core/defined.js";
  5. import destroyObject from "../Core/destroyObject.js";
  6. import DeveloperError from "../Core/DeveloperError.js";
  7. import Entity from "./Entity.js";
  8. import JulianDate from "../Core/JulianDate.js";
  9. import Matrix3 from "../Core/Matrix3.js";
  10. import Matrix4 from "../Core/Matrix4.js";
  11. import ReferenceFrame from "../Core/ReferenceFrame.js";
  12. import TimeInterval from "../Core/TimeInterval.js";
  13. import Transforms from "../Core/Transforms.js";
  14. import PolylineCollection from "../Scene/PolylineCollection.js";
  15. import SceneMode from "../Scene/SceneMode.js";
  16. import CallbackPositionProperty from "./CallbackPositionProperty.js";
  17. import CompositePositionProperty from "./CompositePositionProperty.js";
  18. import ConstantPositionProperty from "./ConstantPositionProperty.js";
  19. import MaterialProperty from "./MaterialProperty.js";
  20. import Property from "./Property.js";
  21. import ReferenceProperty from "./ReferenceProperty.js";
  22. import SampledPositionProperty from "./SampledPositionProperty.js";
  23. import ScaledPositionProperty from "./ScaledPositionProperty.js";
  24. import TimeIntervalCollectionPositionProperty from "./TimeIntervalCollectionPositionProperty.js";
  25. import Quaternion from "../Core/Quaternion.js";
  26. const update3DMatrix3Scratch1 = new Matrix3();
  27. const update3DMatrix3Scratch2 = new Matrix3();
  28. const update3DMatrix3Scratch3 = new Matrix3();
  29. const update3DCartesian3Scratch0 = new Cartesian3();
  30. const update3DCartesian3Scratch1 = new Cartesian3();
  31. const update3DCartesian3Scratch2 = new Cartesian3();
  32. const update3DCartesian3Scratch3 = new Cartesian3();
  33. const transformOrientationScratch = new Quaternion();
  34. const transformVvlhScratch = new Matrix4();
  35. const transformRotationScratch = new Matrix3();
  36. /**
  37. * Transforms a path entity's position into the local frame of the reference entity.
  38. * If the reference entity has an orientation, uses that orientation to define the local frame.
  39. * Otherwise, falls back to a VVLH (Vehicle Velocity Local Horizontal) frame derived from the reference entity's velocity.
  40. *
  41. * @param {JulianDate} time The time at which to evaluate the orientation or VVLH frame.
  42. * @param {Cartesian3} pathEntityPos The position of the path entity in the FIXED reference frame.
  43. * @param {Cartesian3} refEntityPos The position of the reference entity in the FIXED reference frame.
  44. * @param {Entity} refEntity The reference entity whose frame to transform into.
  45. * @param {Cartesian3} result The object onto which to store the result.
  46. * @returns {Cartesian3 | undefined} The transformed position in the reference entity's local frame, or undefined if either input position is undefined.
  47. */
  48. function transformToEntityFrame(
  49. time,
  50. pathEntityPos,
  51. refEntityPos,
  52. refEntity,
  53. result,
  54. ) {
  55. if (!defined(pathEntityPos) || !defined(refEntityPos)) {
  56. return undefined;
  57. }
  58. Cartesian3.subtract(pathEntityPos, refEntityPos, result);
  59. if (defined(refEntity.orientation)) {
  60. if (refEntity.orientation.getValue(time, transformOrientationScratch)) {
  61. Quaternion.conjugate(
  62. transformOrientationScratch,
  63. transformOrientationScratch,
  64. );
  65. Matrix3.fromQuaternion(
  66. transformOrientationScratch,
  67. transformRotationScratch,
  68. );
  69. Matrix3.multiplyByVector(transformRotationScratch, result, result);
  70. }
  71. } else if (
  72. defined(
  73. computeVvlhTransform(time, refEntity.position, transformVvlhScratch),
  74. )
  75. ) {
  76. Matrix4.inverse(transformVvlhScratch, transformVvlhScratch);
  77. Matrix4.getRotation(transformVvlhScratch, transformRotationScratch);
  78. Matrix3.multiplyByVector(transformRotationScratch, result, result);
  79. } else {
  80. // If neither ref entity's orientation nor VVLH are defined, return undefined
  81. // This could happen if, for a given position of the entity we are drawing the path for,
  82. // the ref entity doesn't have a position defined
  83. return undefined;
  84. }
  85. return result;
  86. }
  87. /**
  88. * Compute the vehicle velocity, local horizontal (VVLH) transform for a position property at a given time.
  89. * The VVLH axes is defined based on the motion of the provided position point as follows:
  90. * - The X axis is directed toward the point's velocity vector, in the direction of motion.
  91. * - The Y axis is along the angular momentum vector.
  92. * - The Z axis is along the position vector.
  93. *
  94. * @param {JulianDate} time The time at which to compute the VVLH transform.
  95. * @param {PositionProperty} positionProperty The position to compute the VVLH frame for.
  96. * @param {Matrix4} result The object onto which to store the result.
  97. * @returns {Matrix4} The VVLH transform.
  98. */
  99. function computeVvlhTransform(time, positionProperty, result) {
  100. const cartesian = positionProperty.getValue(time, update3DCartesian3Scratch0);
  101. if (defined(cartesian)) {
  102. // The time delta was determined based on how fast satellites move compared to vehicles near the surface.
  103. // Slower moving vehicles will most likely default to east-north-up, while faster ones will be LVLH.
  104. const deltaTime = JulianDate.addSeconds(time, 0.01, new JulianDate());
  105. const deltaCartesian = positionProperty.getValue(
  106. deltaTime,
  107. update3DCartesian3Scratch1,
  108. );
  109. if (
  110. defined(deltaCartesian) &&
  111. !Cartesian3.equalsEpsilon(cartesian, deltaCartesian, CesiumMath.EPSILON16)
  112. ) {
  113. let toInertial = Transforms.computeFixedToIcrfMatrix(
  114. time,
  115. update3DMatrix3Scratch1,
  116. );
  117. let toInertialDelta = Transforms.computeFixedToIcrfMatrix(
  118. deltaTime,
  119. update3DMatrix3Scratch2,
  120. );
  121. let toFixed;
  122. if (!defined(toInertial) || !defined(toInertialDelta)) {
  123. toFixed = Transforms.computeTemeToPseudoFixedMatrix(
  124. time,
  125. update3DMatrix3Scratch3,
  126. );
  127. toInertial = Matrix3.transpose(toFixed, update3DMatrix3Scratch1);
  128. toInertialDelta = Transforms.computeTemeToPseudoFixedMatrix(
  129. deltaTime,
  130. update3DMatrix3Scratch2,
  131. );
  132. Matrix3.transpose(toInertialDelta, toInertialDelta);
  133. } else {
  134. toFixed = Matrix3.transpose(toInertial, update3DMatrix3Scratch3);
  135. }
  136. // Z along the position
  137. const zBasis = update3DCartesian3Scratch2;
  138. Cartesian3.normalize(cartesian, zBasis);
  139. Cartesian3.normalize(deltaCartesian, deltaCartesian);
  140. Matrix3.multiplyByVector(toInertial, zBasis, zBasis);
  141. Matrix3.multiplyByVector(toInertialDelta, deltaCartesian, deltaCartesian);
  142. // Y is along the angular momentum vector (e.g. "orbit normal")
  143. const yBasis = Cartesian3.cross(
  144. zBasis,
  145. deltaCartesian,
  146. update3DCartesian3Scratch3,
  147. );
  148. if (
  149. !Cartesian3.equalsEpsilon(yBasis, Cartesian3.ZERO, CesiumMath.EPSILON16)
  150. ) {
  151. // X is along the cross of y and z (right handed basis / in the direction of motion)
  152. const xBasis = Cartesian3.cross(
  153. yBasis,
  154. zBasis,
  155. update3DCartesian3Scratch1,
  156. );
  157. Matrix3.multiplyByVector(toFixed, xBasis, xBasis);
  158. Matrix3.multiplyByVector(toFixed, yBasis, yBasis);
  159. Matrix3.multiplyByVector(toFixed, zBasis, zBasis);
  160. Cartesian3.normalize(xBasis, xBasis);
  161. Cartesian3.normalize(yBasis, yBasis);
  162. Cartesian3.normalize(zBasis, zBasis);
  163. if (!defined(result)) {
  164. result = new Matrix4();
  165. }
  166. result[0] = xBasis.x;
  167. result[1] = xBasis.y;
  168. result[2] = xBasis.z;
  169. result[3] = 0.0;
  170. result[4] = yBasis.x;
  171. result[5] = yBasis.y;
  172. result[6] = yBasis.z;
  173. result[7] = 0.0;
  174. result[8] = zBasis.x;
  175. result[9] = zBasis.y;
  176. result[10] = zBasis.z;
  177. result[11] = 0.0;
  178. result[12] = cartesian.x;
  179. result[13] = cartesian.y;
  180. result[14] = cartesian.z;
  181. result[15] = 1.0;
  182. return result;
  183. }
  184. }
  185. }
  186. return undefined;
  187. }
  188. const defaultResolution = 60.0;
  189. const defaultWidth = 1.0;
  190. const scratchTimeInterval = new TimeInterval();
  191. const subSampleCompositePropertyScratch = new TimeInterval();
  192. const subSampleIntervalPropertyScratch = new TimeInterval();
  193. function EntityData(entity) {
  194. this.entity = entity;
  195. this.polyline = undefined;
  196. this.index = undefined;
  197. this.updater = undefined;
  198. }
  199. const sampleScratch = new Cartesian3();
  200. function subSampleSampledProperty(
  201. property,
  202. start,
  203. stop,
  204. times,
  205. updateTime,
  206. referenceFrame,
  207. maximumStep,
  208. startingIndex,
  209. result,
  210. ) {
  211. let refEntity;
  212. let refPosition;
  213. let entityFrame = false;
  214. if (referenceFrame instanceof Entity) {
  215. refEntity = referenceFrame;
  216. refPosition = refEntity.position;
  217. referenceFrame = ReferenceFrame.FIXED;
  218. entityFrame = true;
  219. }
  220. let r = startingIndex;
  221. //Always step exactly on start (but only use it if it exists.)
  222. let tmp;
  223. let tmp2;
  224. tmp = property.getValueInReferenceFrame(start, referenceFrame, result[r]);
  225. if (!entityFrame) {
  226. if (defined(tmp)) {
  227. result[r++] = tmp;
  228. }
  229. } else {
  230. tmp2 = refPosition.getValueInReferenceFrame(
  231. start,
  232. referenceFrame,
  233. sampleScratch,
  234. );
  235. // Transform to frame of reference - either reference entity's orientation, or VVLH
  236. tmp = transformToEntityFrame(start, tmp, tmp2, refEntity, tmp);
  237. if (defined(tmp)) {
  238. result[r++] = tmp;
  239. }
  240. }
  241. let steppedOnNow =
  242. !defined(updateTime) ||
  243. JulianDate.lessThanOrEquals(updateTime, start) ||
  244. JulianDate.greaterThanOrEquals(updateTime, stop);
  245. //Iterate over all interval times and add the ones that fall in our
  246. //time range. Note that times can contain data outside of
  247. //the intervals range. This is by design for use with interpolation.
  248. let t = 0;
  249. const len = times.length;
  250. let current = times[t];
  251. const loopStop = stop;
  252. let sampling = false;
  253. let sampleStepsToTake;
  254. let sampleStepsTaken;
  255. let sampleStepSize;
  256. while (t < len) {
  257. if (!steppedOnNow && JulianDate.greaterThanOrEquals(current, updateTime)) {
  258. tmp = property.getValueInReferenceFrame(
  259. updateTime,
  260. referenceFrame,
  261. result[r],
  262. );
  263. if (!entityFrame) {
  264. if (defined(tmp)) {
  265. result[r++] = tmp;
  266. }
  267. } else {
  268. tmp2 = refPosition.getValueInReferenceFrame(
  269. updateTime,
  270. referenceFrame,
  271. sampleScratch,
  272. );
  273. if (defined(tmp) && defined(tmp2)) {
  274. tmp = transformToEntityFrame(updateTime, tmp, tmp2, refEntity, tmp);
  275. if (defined(tmp)) {
  276. result[r++] = tmp;
  277. }
  278. }
  279. }
  280. steppedOnNow = true;
  281. }
  282. if (
  283. JulianDate.greaterThan(current, start) &&
  284. JulianDate.lessThan(current, loopStop) &&
  285. !current.equals(updateTime)
  286. ) {
  287. tmp = property.getValueInReferenceFrame(
  288. current,
  289. referenceFrame,
  290. result[r],
  291. );
  292. if (!entityFrame) {
  293. if (defined(tmp)) {
  294. result[r++] = tmp;
  295. }
  296. } else {
  297. tmp2 = refPosition.getValueInReferenceFrame(
  298. current,
  299. referenceFrame,
  300. sampleScratch,
  301. );
  302. if (defined(tmp) && defined(tmp2)) {
  303. tmp = transformToEntityFrame(current, tmp, tmp2, refEntity, tmp);
  304. if (defined(tmp)) {
  305. result[r++] = tmp;
  306. }
  307. }
  308. }
  309. }
  310. if (t < len - 1) {
  311. if (maximumStep > 0 && !sampling) {
  312. const next = times[t + 1];
  313. const secondsUntilNext = JulianDate.secondsDifference(next, current);
  314. sampling = secondsUntilNext > maximumStep;
  315. if (sampling) {
  316. sampleStepsToTake = Math.ceil(secondsUntilNext / maximumStep);
  317. sampleStepsTaken = 0;
  318. sampleStepSize = secondsUntilNext / Math.max(sampleStepsToTake, 2);
  319. sampleStepsToTake = Math.max(sampleStepsToTake - 1, 1);
  320. }
  321. }
  322. if (sampling && sampleStepsTaken < sampleStepsToTake) {
  323. current = JulianDate.addSeconds(
  324. current,
  325. sampleStepSize,
  326. new JulianDate(),
  327. );
  328. sampleStepsTaken++;
  329. continue;
  330. }
  331. }
  332. sampling = false;
  333. t++;
  334. current = times[t];
  335. }
  336. //Always step exactly on stop (but only use it if it exists.)
  337. tmp = property.getValueInReferenceFrame(stop, referenceFrame, result[r]);
  338. if (!entityFrame) {
  339. if (defined(tmp)) {
  340. result[r++] = tmp;
  341. }
  342. } else {
  343. tmp2 = refPosition.getValueInReferenceFrame(
  344. stop,
  345. referenceFrame,
  346. sampleScratch,
  347. );
  348. if (defined(tmp) && defined(tmp2)) {
  349. tmp = transformToEntityFrame(stop, tmp, tmp2, refEntity, tmp);
  350. if (defined(tmp)) {
  351. result[r++] = tmp;
  352. }
  353. }
  354. }
  355. return r;
  356. }
  357. function subSampleCallbackPositionProperty(
  358. property,
  359. start,
  360. stop,
  361. updateTime,
  362. referenceFrame,
  363. maximumStep,
  364. startingIndex,
  365. result,
  366. ) {
  367. let tmp;
  368. let i = 0;
  369. let index = startingIndex;
  370. let time = start;
  371. let steppedOnNow =
  372. !defined(updateTime) ||
  373. JulianDate.lessThanOrEquals(updateTime, start) ||
  374. JulianDate.greaterThanOrEquals(updateTime, stop);
  375. while (JulianDate.lessThan(time, stop)) {
  376. if (!steppedOnNow && JulianDate.greaterThanOrEquals(time, updateTime)) {
  377. steppedOnNow = true;
  378. tmp = property.getValueInReferenceFrame(
  379. updateTime,
  380. referenceFrame,
  381. result[index],
  382. );
  383. if (defined(tmp)) {
  384. result[index] = tmp;
  385. index++;
  386. }
  387. }
  388. tmp = property.getValueInReferenceFrame(
  389. time,
  390. referenceFrame,
  391. result[index],
  392. );
  393. if (defined(tmp)) {
  394. result[index] = tmp;
  395. index++;
  396. }
  397. i++;
  398. time = JulianDate.addSeconds(start, maximumStep * i, new JulianDate());
  399. }
  400. //Always sample stop.
  401. tmp = property.getValueInReferenceFrame(stop, referenceFrame, result[index]);
  402. if (defined(tmp)) {
  403. result[index] = tmp;
  404. index++;
  405. }
  406. return index;
  407. }
  408. function subSampleGenericProperty(
  409. property,
  410. start,
  411. stop,
  412. updateTime,
  413. referenceFrame,
  414. maximumStep,
  415. startingIndex,
  416. result,
  417. ) {
  418. let tmp;
  419. let i = 0;
  420. let index = startingIndex;
  421. let time = start;
  422. const stepSize = Math.max(maximumStep, 60);
  423. let steppedOnNow =
  424. !defined(updateTime) ||
  425. JulianDate.lessThanOrEquals(updateTime, start) ||
  426. JulianDate.greaterThanOrEquals(updateTime, stop);
  427. while (JulianDate.lessThan(time, stop)) {
  428. if (!steppedOnNow && JulianDate.greaterThanOrEquals(time, updateTime)) {
  429. steppedOnNow = true;
  430. tmp = property.getValueInReferenceFrame(
  431. updateTime,
  432. referenceFrame,
  433. result[index],
  434. );
  435. if (defined(tmp)) {
  436. result[index] = tmp;
  437. index++;
  438. }
  439. }
  440. tmp = property.getValueInReferenceFrame(
  441. time,
  442. referenceFrame,
  443. result[index],
  444. );
  445. if (defined(tmp)) {
  446. result[index] = tmp;
  447. index++;
  448. }
  449. i++;
  450. time = JulianDate.addSeconds(start, stepSize * i, new JulianDate());
  451. }
  452. //Always sample stop.
  453. tmp = property.getValueInReferenceFrame(stop, referenceFrame, result[index]);
  454. if (defined(tmp)) {
  455. result[index] = tmp;
  456. index++;
  457. }
  458. return index;
  459. }
  460. function subSampleIntervalProperty(
  461. property,
  462. start,
  463. stop,
  464. updateTime,
  465. referenceFrame,
  466. maximumStep,
  467. startingIndex,
  468. result,
  469. ) {
  470. subSampleIntervalPropertyScratch.start = start;
  471. subSampleIntervalPropertyScratch.stop = stop;
  472. let index = startingIndex;
  473. const intervals = property.intervals;
  474. for (let i = 0; i < intervals.length; i++) {
  475. const interval = intervals.get(i);
  476. if (
  477. !TimeInterval.intersect(
  478. interval,
  479. subSampleIntervalPropertyScratch,
  480. scratchTimeInterval,
  481. ).isEmpty
  482. ) {
  483. let time = interval.start;
  484. if (!interval.isStartIncluded) {
  485. if (interval.isStopIncluded) {
  486. time = interval.stop;
  487. } else {
  488. time = JulianDate.addSeconds(
  489. interval.start,
  490. JulianDate.secondsDifference(interval.stop, interval.start) / 2,
  491. new JulianDate(),
  492. );
  493. }
  494. }
  495. const tmp = property.getValueInReferenceFrame(
  496. time,
  497. referenceFrame,
  498. result[index],
  499. );
  500. if (defined(tmp)) {
  501. result[index] = tmp;
  502. index++;
  503. }
  504. }
  505. }
  506. return index;
  507. }
  508. function subSampleConstantProperty(
  509. property,
  510. start,
  511. stop,
  512. updateTime,
  513. referenceFrame,
  514. maximumStep,
  515. startingIndex,
  516. result,
  517. ) {
  518. const tmp = property.getValueInReferenceFrame(
  519. start,
  520. referenceFrame,
  521. result[startingIndex],
  522. );
  523. if (defined(tmp)) {
  524. result[startingIndex++] = tmp;
  525. }
  526. return startingIndex;
  527. }
  528. function subSampleCompositeProperty(
  529. property,
  530. start,
  531. stop,
  532. updateTime,
  533. referenceFrame,
  534. maximumStep,
  535. startingIndex,
  536. result,
  537. ) {
  538. subSampleCompositePropertyScratch.start = start;
  539. subSampleCompositePropertyScratch.stop = stop;
  540. let index = startingIndex;
  541. const intervals = property.intervals;
  542. for (let i = 0; i < intervals.length; i++) {
  543. const interval = intervals.get(i);
  544. if (
  545. !TimeInterval.intersect(
  546. interval,
  547. subSampleCompositePropertyScratch,
  548. scratchTimeInterval,
  549. ).isEmpty
  550. ) {
  551. const intervalStart = interval.start;
  552. const intervalStop = interval.stop;
  553. let sampleStart = start;
  554. if (JulianDate.greaterThan(intervalStart, sampleStart)) {
  555. sampleStart = intervalStart;
  556. }
  557. let sampleStop = stop;
  558. if (JulianDate.lessThan(intervalStop, sampleStop)) {
  559. sampleStop = intervalStop;
  560. }
  561. index = reallySubSample(
  562. interval.data,
  563. sampleStart,
  564. sampleStop,
  565. updateTime,
  566. referenceFrame,
  567. maximumStep,
  568. index,
  569. result,
  570. );
  571. }
  572. }
  573. return index;
  574. }
  575. function reallySubSample(
  576. property,
  577. start,
  578. stop,
  579. updateTime,
  580. referenceFrame,
  581. maximumStep,
  582. index,
  583. result,
  584. ) {
  585. //Unwrap any references until we have the actual property.
  586. while (property instanceof ReferenceProperty) {
  587. property = property.resolvedProperty;
  588. }
  589. if (property instanceof SampledPositionProperty) {
  590. const times = property._property._times;
  591. index = subSampleSampledProperty(
  592. property,
  593. start,
  594. stop,
  595. times,
  596. updateTime,
  597. referenceFrame,
  598. maximumStep,
  599. index,
  600. result,
  601. );
  602. } else if (property instanceof CallbackPositionProperty) {
  603. index = subSampleCallbackPositionProperty(
  604. property,
  605. start,
  606. stop,
  607. updateTime,
  608. referenceFrame,
  609. maximumStep,
  610. index,
  611. result,
  612. );
  613. } else if (property instanceof CompositePositionProperty) {
  614. index = subSampleCompositeProperty(
  615. property,
  616. start,
  617. stop,
  618. updateTime,
  619. referenceFrame,
  620. maximumStep,
  621. index,
  622. result,
  623. );
  624. } else if (property instanceof TimeIntervalCollectionPositionProperty) {
  625. index = subSampleIntervalProperty(
  626. property,
  627. start,
  628. stop,
  629. updateTime,
  630. referenceFrame,
  631. maximumStep,
  632. index,
  633. result,
  634. );
  635. } else if (
  636. property instanceof ConstantPositionProperty ||
  637. (property instanceof ScaledPositionProperty &&
  638. Property.isConstant(property))
  639. ) {
  640. index = subSampleConstantProperty(
  641. property,
  642. start,
  643. stop,
  644. updateTime,
  645. referenceFrame,
  646. maximumStep,
  647. index,
  648. result,
  649. );
  650. } else {
  651. //Fallback to generic sampling.
  652. index = subSampleGenericProperty(
  653. property,
  654. start,
  655. stop,
  656. updateTime,
  657. referenceFrame,
  658. maximumStep,
  659. index,
  660. result,
  661. );
  662. }
  663. return index;
  664. }
  665. function subSample(
  666. property,
  667. start,
  668. stop,
  669. updateTime,
  670. referenceFrame,
  671. maximumStep,
  672. result,
  673. ) {
  674. if (!defined(result)) {
  675. result = [];
  676. }
  677. const length = reallySubSample(
  678. property,
  679. start,
  680. stop,
  681. updateTime,
  682. referenceFrame,
  683. maximumStep,
  684. 0,
  685. result,
  686. );
  687. result.length = length;
  688. return result;
  689. }
  690. const toFixedScratch = new Matrix3();
  691. const updateOrientationScratch = new Quaternion();
  692. const updateRotationScratch = new Matrix3();
  693. function PolylineUpdater(scene, referenceFrame) {
  694. this._unusedIndexes = [];
  695. this._polylineCollection = new PolylineCollection();
  696. this._scene = scene;
  697. this._referenceFrame = referenceFrame;
  698. scene.primitives.add(this._polylineCollection);
  699. }
  700. PolylineUpdater.prototype.update = function (time) {
  701. const frame = this._referenceFrame;
  702. if (frame === ReferenceFrame.INERTIAL) {
  703. let toFixed = Transforms.computeIcrfToFixedMatrix(time, toFixedScratch);
  704. if (!defined(toFixed)) {
  705. toFixed = Transforms.computeTemeToPseudoFixedMatrix(time, toFixedScratch);
  706. }
  707. Matrix4.fromRotationTranslation(
  708. toFixed,
  709. Cartesian3.ZERO,
  710. this._polylineCollection.modelMatrix,
  711. );
  712. } else if (frame instanceof Entity) {
  713. const position = frame.position.getValue(time);
  714. // Use the reference frame entity's orientation if it has one
  715. if (defined(frame.orientation)) {
  716. if (defined(frame.orientation.getValue(time, updateOrientationScratch))) {
  717. // Calculate the model matrix that places the body-frame path points into the world
  718. Matrix3.fromQuaternion(updateOrientationScratch, updateRotationScratch);
  719. Matrix4.fromRotationTranslation(
  720. updateRotationScratch,
  721. position,
  722. this._polylineCollection.modelMatrix,
  723. );
  724. }
  725. } else {
  726. computeVvlhTransform(
  727. time,
  728. frame.position,
  729. this._polylineCollection.modelMatrix,
  730. );
  731. }
  732. }
  733. };
  734. PolylineUpdater.prototype.updateObject = function (time, item) {
  735. const entity = item.entity;
  736. const pathGraphics = entity._path;
  737. const positionProperty = entity._position;
  738. let sampleStart;
  739. let sampleStop;
  740. const showProperty = pathGraphics._show;
  741. let polyline = item.polyline;
  742. let show =
  743. entity.isShowing &&
  744. entity.isAvailable(time) &&
  745. (!defined(showProperty) || showProperty.getValue(time));
  746. //While we want to show the path, there may not actually be anything to show
  747. //depending on lead/trail settings. Compute the interval of the path to
  748. //show and check against actual availability.
  749. if (show) {
  750. const leadTime = Property.getValueOrUndefined(pathGraphics._leadTime, time);
  751. const trailTime = Property.getValueOrUndefined(
  752. pathGraphics._trailTime,
  753. time,
  754. );
  755. const availability = entity._availability;
  756. const hasAvailability = defined(availability);
  757. const hasLeadTime = defined(leadTime);
  758. const hasTrailTime = defined(trailTime);
  759. //Objects need to have either defined availability or both a lead and trail time in order to
  760. //draw a path (since we can't draw "infinite" paths.
  761. show = hasAvailability || (hasLeadTime && hasTrailTime);
  762. //The final step is to compute the actual start/stop times of the path to show.
  763. //If current time is outside of the availability interval, there's a chance that
  764. //we won't have to draw anything anyway.
  765. if (show) {
  766. if (hasTrailTime) {
  767. sampleStart = JulianDate.addSeconds(time, -trailTime, new JulianDate());
  768. }
  769. if (hasLeadTime) {
  770. sampleStop = JulianDate.addSeconds(time, leadTime, new JulianDate());
  771. }
  772. if (hasAvailability) {
  773. const start = availability.start;
  774. const stop = availability.stop;
  775. if (!hasTrailTime || JulianDate.greaterThan(start, sampleStart)) {
  776. sampleStart = start;
  777. }
  778. if (!hasLeadTime || JulianDate.lessThan(stop, sampleStop)) {
  779. sampleStop = stop;
  780. }
  781. }
  782. show = JulianDate.lessThan(sampleStart, sampleStop);
  783. }
  784. }
  785. if (!show) {
  786. //don't bother creating or updating anything else
  787. if (defined(polyline)) {
  788. this._unusedIndexes.push(item.index);
  789. item.polyline = undefined;
  790. polyline.show = false;
  791. item.index = undefined;
  792. }
  793. return;
  794. }
  795. if (!defined(polyline)) {
  796. const unusedIndexes = this._unusedIndexes;
  797. const length = unusedIndexes.length;
  798. if (length > 0) {
  799. const index = unusedIndexes.pop();
  800. polyline = this._polylineCollection.get(index);
  801. item.index = index;
  802. } else {
  803. item.index = this._polylineCollection.length;
  804. polyline = this._polylineCollection.add();
  805. }
  806. polyline.id = entity;
  807. item.polyline = polyline;
  808. }
  809. const resolution = Property.getValueOrDefault(
  810. pathGraphics._resolution,
  811. time,
  812. defaultResolution,
  813. );
  814. const positions = subSample(
  815. positionProperty,
  816. sampleStart,
  817. sampleStop,
  818. time,
  819. this._referenceFrame,
  820. resolution,
  821. polyline.positions.slice(),
  822. );
  823. // If the path only has one point, don't show it
  824. // This can happen if the position is sampled at a time when it is only defined at a single point
  825. if (positions.length < 2) {
  826. polyline.show = false;
  827. return;
  828. }
  829. polyline.show = true;
  830. polyline.positions = positions;
  831. polyline.material = MaterialProperty.getValue(
  832. time,
  833. pathGraphics._material,
  834. polyline.material,
  835. );
  836. polyline.width = Property.getValueOrDefault(
  837. pathGraphics._width,
  838. time,
  839. defaultWidth,
  840. );
  841. polyline.distanceDisplayCondition = Property.getValueOrUndefined(
  842. pathGraphics._distanceDisplayCondition,
  843. time,
  844. polyline.distanceDisplayCondition,
  845. );
  846. };
  847. PolylineUpdater.prototype.removeObject = function (item) {
  848. const polyline = item.polyline;
  849. if (defined(polyline)) {
  850. this._unusedIndexes.push(item.index);
  851. item.polyline = undefined;
  852. polyline.show = false;
  853. polyline.id = undefined;
  854. item.index = undefined;
  855. }
  856. };
  857. PolylineUpdater.prototype.destroy = function () {
  858. this._scene.primitives.remove(this._polylineCollection);
  859. return destroyObject(this);
  860. };
  861. /**
  862. * A {@link Visualizer} which maps {@link Entity#path} to a {@link Polyline}.
  863. * @alias PathVisualizer
  864. * @constructor
  865. *
  866. * @param {Scene} scene The scene the primitives will be rendered in.
  867. * @param {EntityCollection} entityCollection The entityCollection to visualize.
  868. */
  869. function PathVisualizer(scene, entityCollection) {
  870. //>>includeStart('debug', pragmas.debug);
  871. if (!defined(scene)) {
  872. throw new DeveloperError("scene is required.");
  873. }
  874. if (!defined(entityCollection)) {
  875. throw new DeveloperError("entityCollection is required.");
  876. }
  877. //>>includeEnd('debug');
  878. entityCollection.collectionChanged.addEventListener(
  879. PathVisualizer.prototype._onCollectionChanged,
  880. this,
  881. );
  882. this._scene = scene;
  883. this._updaters = {};
  884. this._entityCollection = entityCollection;
  885. this._items = new AssociativeArray();
  886. this._onCollectionChanged(entityCollection, entityCollection.values, [], []);
  887. }
  888. /**
  889. * Updates all of the primitives created by this visualizer to match their
  890. * Entity counterpart at the given time.
  891. *
  892. * @param {JulianDate} time The time to update to.
  893. * @returns {boolean} This function always returns true.
  894. */
  895. PathVisualizer.prototype.update = function (time) {
  896. //>>includeStart('debug', pragmas.debug);
  897. if (!defined(time)) {
  898. throw new DeveloperError("time is required.");
  899. }
  900. //>>includeEnd('debug');
  901. const updaters = this._updaters;
  902. for (const key in updaters) {
  903. if (updaters.hasOwnProperty(key)) {
  904. updaters[key].update(time);
  905. }
  906. }
  907. const items = this._items.values;
  908. if (
  909. items.length === 0 &&
  910. defined(this._updaters) &&
  911. Object.keys(this._updaters).length > 0
  912. ) {
  913. for (const u in updaters) {
  914. if (updaters.hasOwnProperty(u)) {
  915. updaters[u].destroy();
  916. }
  917. }
  918. this._updaters = {};
  919. }
  920. for (let i = 0, len = items.length; i < len; i++) {
  921. const item = items[i];
  922. const entity = item.entity;
  923. const positionProperty = entity._position;
  924. const pathGraphics = entity._path;
  925. const lastUpdater = item.updater;
  926. let isRelative = false;
  927. let frameToVisualize = ReferenceFrame.FIXED;
  928. let frameToVisualizeKey = frameToVisualize.toString();
  929. if (this._scene.mode === SceneMode.SCENE3D) {
  930. const relativeTo = Property.getValueOrUndefined(
  931. pathGraphics.relativeTo,
  932. time,
  933. );
  934. if (defined(relativeTo)) {
  935. if (relativeTo === "FIXED") {
  936. frameToVisualize = ReferenceFrame.FIXED;
  937. frameToVisualizeKey = frameToVisualize.toString();
  938. } else if (relativeTo === "INERTIAL") {
  939. frameToVisualize = ReferenceFrame.INERTIAL;
  940. frameToVisualizeKey = frameToVisualize.toString();
  941. } else {
  942. // Path should be relative to entity
  943. // Current implementation uses VVLH, ignores entity orientation
  944. isRelative = true;
  945. frameToVisualize = this._entityCollection.getById(relativeTo);
  946. frameToVisualizeKey = relativeTo;
  947. }
  948. } else {
  949. frameToVisualize = positionProperty.referenceFrame;
  950. frameToVisualizeKey = frameToVisualize.toString();
  951. }
  952. }
  953. let currentUpdater = this._updaters[frameToVisualizeKey];
  954. if (lastUpdater === currentUpdater && defined(currentUpdater)) {
  955. currentUpdater.updateObject(time, item);
  956. continue;
  957. }
  958. if (defined(lastUpdater)) {
  959. lastUpdater.removeObject(item);
  960. }
  961. if (isRelative && !defined(frameToVisualize)) {
  962. continue;
  963. }
  964. if (!defined(currentUpdater)) {
  965. currentUpdater = new PolylineUpdater(this._scene, frameToVisualize);
  966. currentUpdater.update(time);
  967. this._updaters[frameToVisualizeKey] = currentUpdater;
  968. }
  969. item.updater = currentUpdater;
  970. if (defined(currentUpdater)) {
  971. currentUpdater.updateObject(time, item);
  972. }
  973. }
  974. return true;
  975. };
  976. /**
  977. * Returns true if this object was destroyed; otherwise, false.
  978. *
  979. * @returns {boolean} True if this object was destroyed; otherwise, false.
  980. */
  981. PathVisualizer.prototype.isDestroyed = function () {
  982. return false;
  983. };
  984. /**
  985. * Removes and destroys all primitives created by this instance.
  986. */
  987. PathVisualizer.prototype.destroy = function () {
  988. this._entityCollection.collectionChanged.removeEventListener(
  989. PathVisualizer.prototype._onCollectionChanged,
  990. this,
  991. );
  992. const updaters = this._updaters;
  993. for (const key in updaters) {
  994. if (updaters.hasOwnProperty(key)) {
  995. updaters[key].destroy();
  996. }
  997. }
  998. return destroyObject(this);
  999. };
  1000. PathVisualizer.prototype._onCollectionChanged = function (
  1001. entityCollection,
  1002. added,
  1003. removed,
  1004. changed,
  1005. ) {
  1006. let i;
  1007. let entity;
  1008. let item;
  1009. const items = this._items;
  1010. for (i = added.length - 1; i > -1; i--) {
  1011. entity = added[i];
  1012. if (defined(entity._path) && defined(entity._position)) {
  1013. items.set(entity.id, new EntityData(entity));
  1014. }
  1015. }
  1016. for (i = changed.length - 1; i > -1; i--) {
  1017. entity = changed[i];
  1018. if (defined(entity._path) && defined(entity._position)) {
  1019. if (!items.contains(entity.id)) {
  1020. items.set(entity.id, new EntityData(entity));
  1021. }
  1022. } else {
  1023. item = items.get(entity.id);
  1024. if (defined(item)) {
  1025. if (defined(item.updater)) {
  1026. item.updater.removeObject(item);
  1027. }
  1028. items.remove(entity.id);
  1029. }
  1030. }
  1031. }
  1032. for (i = removed.length - 1; i > -1; i--) {
  1033. entity = removed[i];
  1034. item = items.get(entity.id);
  1035. if (defined(item)) {
  1036. if (defined(item.updater)) {
  1037. item.updater.removeObject(item);
  1038. }
  1039. items.remove(entity.id);
  1040. }
  1041. }
  1042. };
  1043. //for testing
  1044. PathVisualizer._subSample = subSample;
  1045. PathVisualizer._computeVvlhTransform = computeVvlhTransform;
  1046. PathVisualizer._transformToEntityFrame = transformToEntityFrame;
  1047. export default PathVisualizer;