react-native-cesium 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +100 -26
  2. package/android/CMakeLists.txt +1 -2
  3. package/android/src/main/cpp/CesiumBridgeAndroid.cpp +264 -130
  4. package/android/src/main/cpp/CesiumBridgeAndroid.h +70 -27
  5. package/android/src/main/java/com/margelo/nitro/reactnativecesium/CesiumBridgeJNI.kt +68 -44
  6. package/android/src/main/java/com/margelo/nitro/reactnativecesium/HybridCesiumView.kt +160 -187
  7. package/cpp/engine/CameraIntegrator.cpp +374 -0
  8. package/cpp/engine/CameraIntegrator.hpp +146 -0
  9. package/cpp/engine/CesiumEngine.cpp +3 -1
  10. package/cpp/engine/EngineTunables.hpp +80 -21
  11. package/cpp/engine/GlobeCamera.cpp +17 -6
  12. package/cpp/engine/GlobeCamera.hpp +11 -8
  13. package/cpp/engine/test/CameraIntegratorTest.cpp +205 -0
  14. package/cpp/engine/test/build_and_run.sh +27 -0
  15. package/example/README.md +1 -1
  16. package/ios/CesiumBridge.h +53 -28
  17. package/ios/CesiumBridge.mm +172 -86
  18. package/ios/HybridCesiumView.swift +213 -145
  19. package/lib/typescript/src/specs/cesium-view.nitro.d.ts +77 -13
  20. package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -1
  21. package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.cpp +90 -9
  22. package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.hpp +19 -3
  23. package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +20 -0
  24. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +57 -3
  25. package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.hpp +9 -9
  26. package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.hpp +82 -9
  27. package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +25 -0
  28. package/nitrogen/generated/ios/swift/HybridCesiumViewSpec.swift +14 -3
  29. package/nitrogen/generated/ios/swift/HybridCesiumViewSpec_cxx.swift +210 -16
  30. package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.cpp +19 -3
  31. package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.hpp +20 -4
  32. package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +55 -0
  33. package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +5 -0
  34. package/nitrogen/generated/shared/json/CesiumViewConfig.json +5 -0
  35. package/package.json +2 -2
  36. package/src/specs/cesium-view.nitro.ts +93 -14
  37. package/cpp/engine/CameraSmoother.cpp +0 -71
  38. package/cpp/engine/CameraSmoother.hpp +0 -25
  39. package/cpp/engine/CameraTargetState.cpp +0 -140
  40. package/cpp/engine/CameraTargetState.hpp +0 -103
@@ -1,25 +0,0 @@
1
- #pragma once
2
-
3
- #include "GlobeCamera.hpp"
4
-
5
- namespace reactnativecesium {
6
-
7
- // Stateless helper. Given a current camera state and a target camera state,
8
- // returns the next camera state for a frame of duration `dt` seconds.
9
- //
10
- // Per-DoF smoothing rules:
11
- // - lat / lon: snapped directly to target. The target itself is already a
12
- // linearly-interpolated position produced by CameraTargetState::snapshot(),
13
- // so snapping here gives constant-velocity motion between position fixes.
14
- // - altitude / heading / pitch / roll / viewCorrection: exponential decay
15
- // `1 - exp(-k * dt)` per DoF with k taken from EngineTunables.
16
- // - When the delta drops below the matching epsilon, the value is snapped
17
- // to the target so we eventually reach exact equality.
18
- class CameraSmoother {
19
- public:
20
- static CameraParams step(const CameraParams& current,
21
- const CameraParams& target,
22
- double dt);
23
- };
24
-
25
- } // namespace reactnativecesium
@@ -1,140 +0,0 @@
1
- #include "CameraTargetState.hpp"
2
-
3
- #include <algorithm>
4
- #include <cmath>
5
-
6
- namespace reactnativecesium {
7
-
8
- namespace {
9
-
10
- inline double angleDeltaAbsDeg(double a, double b) {
11
- return std::abs(std::fmod(b - a + 540.0, 360.0) - 180.0);
12
- }
13
-
14
- inline double quatDotAbs(const glm::dquat& a, const glm::dquat& b) {
15
- return std::abs(glm::dot(glm::normalize(a), glm::normalize(b)));
16
- }
17
-
18
- // Shortest signed angular delta for longitude, wrapped to [-180, 180].
19
- inline double lonDeltaDeg(double from, double to) {
20
- return std::fmod(to - from + 540.0, 360.0) - 180.0;
21
- }
22
-
23
- } // namespace
24
-
25
- // ── Public writers ────────────────────────────────────────────────────────────
26
-
27
- void CameraTargetState::setAll(const CameraParams& target) {
28
- {
29
- std::lock_guard<std::mutex> g(mutex_);
30
- target_ = target;
31
- // Deliberately do NOT touch interpolation state: setAll is engine-init only
32
- // and the first frame must snap to the requested position immediately.
33
- }
34
- dirty_.store(true, std::memory_order_release);
35
- forceRender_.store(true, std::memory_order_release);
36
- }
37
-
38
- void CameraTargetState::setHpr(double lat, double lon, double alt,
39
- double heading, double pitch, double roll) {
40
- {
41
- std::lock_guard<std::mutex> g(mutex_);
42
-
43
- const bool posChanged =
44
- std::abs(lat - target_.latitude) > tunables::kEpsLatLon ||
45
- std::abs(lon - target_.longitude) > tunables::kEpsLatLon;
46
-
47
- if (posChanged) {
48
- const auto now = std::chrono::steady_clock::now();
49
-
50
- if (!hasInterpolation_) {
51
- // First runtime lat/lon update: seed posFrom = destination so that
52
- // lerp(dest, dest, t) = dest for all t — clean snap, no startup glide.
53
- posFromLat_ = lat;
54
- posFromLon_ = lon;
55
- ewmaIntervalSec_ = tunables::kSmoothPositionTauMax / tunables::kSmoothPositionAlpha;
56
- posUpdateTime_ = now;
57
- hasInterpolation_ = true;
58
- } else {
59
- // Subsequent update: record where we're moving FROM (the previous
60
- // target), update the EWMA interval, and reset the clock.
61
- posFromLat_ = target_.latitude;
62
- posFromLon_ = target_.longitude;
63
-
64
- const double dt = std::chrono::duration<double>(now - posUpdateTime_).count();
65
- if (dt > 0.0) {
66
- ewmaIntervalSec_ = ewmaIntervalSec_ +
67
- tunables::kEwmaIntervalAlpha * (dt - ewmaIntervalSec_);
68
- }
69
- posUpdateTime_ = now;
70
- }
71
- }
72
-
73
- target_.latitude = lat;
74
- target_.longitude = lon;
75
- target_.altitude = alt;
76
- target_.heading = heading;
77
- target_.pitch = pitch;
78
- target_.roll = roll;
79
- }
80
- dirty_.store(true, std::memory_order_release);
81
- forceRender_.store(true, std::memory_order_release);
82
- }
83
-
84
- void CameraTargetState::setViewCorrection(const glm::dquat& q) {
85
- {
86
- std::lock_guard<std::mutex> g(mutex_);
87
- target_.viewCorrection = q;
88
- // Position cadence intentionally NOT updated — viewCorrection changes
89
- // carry no lat/lon information and must not affect the interpolation window.
90
- }
91
- dirty_.store(true, std::memory_order_release);
92
- forceRender_.store(true, std::memory_order_release);
93
- }
94
-
95
- // ── Snapshot (render-thread read) ─────────────────────────────────────────────
96
-
97
- CameraParams CameraTargetState::snapshot() const {
98
- const auto now = std::chrono::steady_clock::now();
99
- std::lock_guard<std::mutex> g(mutex_);
100
-
101
- CameraParams result = target_;
102
-
103
- if (hasInterpolation_ && ewmaIntervalSec_ > 0.0) {
104
- const double dLat = target_.latitude - posFromLat_;
105
- const double dLon = lonDeltaDeg(posFromLon_, target_.longitude);
106
-
107
- // Snap-through guard: genuine teleports (setCamera(NewYork → Tokyo)) bypass
108
- // the interpolation so they remain instant rather than gliding across the planet.
109
- const bool snapThrough =
110
- std::abs(dLat) > tunables::kPosSnapThroughDeg ||
111
- std::abs(dLon) > tunables::kPosSnapThroughDeg;
112
-
113
- if (!snapThrough) {
114
- const double elapsed =
115
- std::chrono::duration<double>(now - posUpdateTime_).count();
116
- const double t = std::min(elapsed / ewmaIntervalSec_, 1.0);
117
-
118
- result.latitude = posFromLat_ + t * dLat;
119
- result.longitude = posFromLon_ + t * dLon;
120
- }
121
- }
122
-
123
- return result;
124
- }
125
-
126
- // ── Epsilon check (shared) ────────────────────────────────────────────────────
127
-
128
- bool CameraTargetState::deltaExceedsEpsilon(
129
- const CameraParams& a, const CameraParams& b) {
130
- return std::abs(a.latitude - b.latitude) > tunables::kEpsLatLon
131
- || std::abs(a.longitude - b.longitude) > tunables::kEpsLatLon
132
- || std::abs(a.altitude - b.altitude) > tunables::kEpsAltitudeMeters
133
- || angleDeltaAbsDeg(a.heading, b.heading) > tunables::kEpsAngleDeg
134
- || angleDeltaAbsDeg(a.pitch, b.pitch) > tunables::kEpsAngleDeg
135
- || angleDeltaAbsDeg(a.roll, b.roll) > tunables::kEpsAngleDeg
136
- || (1.0 - quatDotAbs(a.viewCorrection, b.viewCorrection))
137
- > tunables::kEpsQuatDot;
138
- }
139
-
140
- } // namespace reactnativecesium
@@ -1,103 +0,0 @@
1
- #pragma once
2
-
3
- #include "EngineTunables.hpp"
4
- #include "GlobeCamera.hpp"
5
-
6
- #include <atomic>
7
- #include <chrono>
8
- #include <mutex>
9
-
10
- namespace reactnativecesium {
11
-
12
- // Thread-safe holder for the *demand* camera (what the JS side last asked for).
13
- //
14
- // Writers: the React Native JS / worklet thread, via setCamera / setCameraQuaternion.
15
- // Readers: the dedicated render thread (per frame), via shouldRenderNextFrame /
16
- // renderFrame.
17
- //
18
- // We use a small mutex around `target_` because the writes are uncontended and
19
- // the read side only happens once per frame; the cost is negligible compared to
20
- // the cost of *not* synchronising a 56-byte struct that contains a glm::dquat.
21
- //
22
- // `dirty_` and `forceRender_` are independent atomic flags so the read side can
23
- // poll without taking the lock when the camera has not moved.
24
- //
25
- // Lat/lon interpolation
26
- // ─────────────────────
27
- // When setHpr is called with a new lat/lon, `posFrom` (old target) and
28
- // `posUpdateTime` are recorded inside the mutex. snapshot() then linearly
29
- // interpolates posFrom → target over `ewmaIntervalSec` (EWMA of inter-arrival
30
- // times). This produces constant-velocity motion between position fixes —
31
- // realistic for any vehicle/GPS source — without any changes to the public API.
32
- //
33
- // Special cases handled in snapshot():
34
- // - t >= 1 : camera has reached the target, returns target_ directly.
35
- // - Snap-through guard: deltas > kPosSnapThroughDeg bypass interpolation so
36
- // deliberate teleports (setCamera(NewYork → Tokyo)) remain instant.
37
- // - First setHpr for a given lat/lon: posFrom is set to the *destination*
38
- // so lerp(dest, dest, t) = dest — no startup glide.
39
- // - setAll (initialisation only): does not touch interpolation state; always
40
- // snaps directly.
41
- class CameraTargetState {
42
- public:
43
- CameraTargetState() = default;
44
-
45
- // Replace the entire target. Used only for engine initialisation; does NOT
46
- // update position-interpolation state so the first rendered frame snaps.
47
- void setAll(const CameraParams& target);
48
-
49
- // Update lat/lon/alt + heading/pitch/roll, leaving viewCorrection unchanged.
50
- // When lat/lon actually changes, updates the linear-interpolation bookkeeping.
51
- void setHpr(double lat, double lon, double alt,
52
- double heading, double pitch, double roll);
53
-
54
- void setViewCorrection(const glm::dquat& q);
55
-
56
- // Snapshot the demand target for this render frame. Lat/lon are linearly
57
- // interpolated between the previous target (posFrom) and the current target
58
- // over the EWMA inter-arrival interval. All other DoFs are returned as-is
59
- // (the smoother handles their exponential curves).
60
- CameraParams snapshot() const;
61
-
62
- // Lightweight, lock-free check used by the render thread to decide whether
63
- // it should bother taking a full snapshot at all.
64
- bool isDirty() const noexcept {
65
- return dirty_.load(std::memory_order_acquire);
66
- }
67
- void clearDirty() noexcept {
68
- dirty_.store(false, std::memory_order_release);
69
- }
70
-
71
- bool consumeForceRender() noexcept {
72
- return forceRender_.exchange(false, std::memory_order_acq_rel);
73
- }
74
- void requestForceRender() noexcept {
75
- forceRender_.store(true, std::memory_order_release);
76
- }
77
-
78
- // Compares snapshot to "current" (the engine's current camera) and returns
79
- // true if any DoF differs by more than the matching epsilon. Used to decide
80
- // whether to render this frame.
81
- static bool deltaExceedsEpsilon(const CameraParams& a, const CameraParams& b);
82
-
83
- private:
84
- mutable std::mutex mutex_;
85
- CameraParams target_;
86
- std::atomic<bool> dirty_{false};
87
- std::atomic<bool> forceRender_{true};
88
-
89
- // ── Lat/lon linear interpolation state (all protected by mutex_) ──────────
90
- // posFromLat_/posFromLon_: the target position when the last update arrived
91
- // (i.e. where we are smoothly moving FROM).
92
- // posUpdateTime_: wall-clock time of the last lat/lon change.
93
- // ewmaIntervalSec_: EWMA of inter-arrival times of lat/lon changes.
94
- // hasInterpolation_: true once we have valid posFrom + posUpdateTime
95
- // data (false until the second distinct lat/lon value).
96
- double posFromLat_{0.0};
97
- double posFromLon_{0.0};
98
- std::chrono::steady_clock::time_point posUpdateTime_{};
99
- double ewmaIntervalSec_{0.0};
100
- bool hasInterpolation_{false};
101
- };
102
-
103
- } // namespace reactnativecesium