react-native-cesium 1.2.1 → 1.2.2
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.
- package/README.md +2 -2
- package/android/src/main/cpp/CesiumBridgeAndroid.cpp +1 -2
- package/cpp/engine/CameraSmoother.cpp +7 -35
- package/cpp/engine/CameraSmoother.hpp +4 -8
- package/cpp/engine/CameraTargetState.cpp +112 -21
- package/cpp/engine/CameraTargetState.hpp +40 -58
- package/ios/CesiumBridge.mm +1 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -399,11 +399,11 @@ Avoid assuming **main-thread-only** vs **JS-thread-only** labels beyond the abov
|
|
|
399
399
|
|
|
400
400
|
#### Smoothing of incoming camera updates
|
|
401
401
|
|
|
402
|
-
Every value you pass to `setCamera` / `setCameraQuaternion` is treated as a **demand** target — what you would *like* the camera to be — rather than a per-frame command. The native render thread
|
|
402
|
+
Every value you pass to `setCamera` / `setCameraQuaternion` is treated as a **demand** target — what you would *like* the camera to be — rather than a per-frame command. The native render thread interpolates the displayed camera toward your demand target every frame, which means a coarse 1 Hz position feed (e.g. external GPS) does **not** teleport the view once a second. Instead it moves at constant speed — exactly like looking out the window of the vehicle being tracked.
|
|
403
403
|
|
|
404
404
|
| DoF | Smoothing |
|
|
405
405
|
| --- | --- |
|
|
406
|
-
| `latitude` / `longitude` | **
|
|
406
|
+
| `latitude` / `longitude` | **Linear interpolation** at constant speed between position fixes. When a new lat/lon arrives, the native engine records the previous target as the "from" position and linearly lerps toward the new target over the EWMA inter-arrival interval (e.g. a 1 Hz GPS feed gives a 1 s travel time, so the camera moves at exactly the vehicle's speed between fixes). At 60 Hz gestures the interval collapses to ~16 ms making the interpolation indistinguishable from an instant snap. Cross-planet jumps (Δ > ~0.5°) bypass interpolation so deliberate teleports remain instant. Antimeridian crossings always rotate the short way. |
|
|
407
407
|
| `altitude` | Exponential, fixed `τ ≈ 40 ms`. |
|
|
408
408
|
| `heading` / `pitch` / `roll` | Exponential on the shortest-arc angular delta, fixed `τ ≈ 20–33 ms`. |
|
|
409
409
|
| `viewCorrection` (quaternion) | SLERP-based exponential, fixed `τ ≈ 20 ms`, hemisphere-corrected so it always takes the short rotation. |
|
|
@@ -244,8 +244,7 @@ void CesiumBridgeAndroid::renderFrame(double dt) {
|
|
|
244
244
|
|
|
245
245
|
const auto cur = engine_->camera().getParams();
|
|
246
246
|
const auto tgt = target_.snapshot();
|
|
247
|
-
auto smooth = reactnativecesium::CameraSmoother::step(
|
|
248
|
-
cur, tgt, dt, target_.recentIntervalSec());
|
|
247
|
+
auto smooth = reactnativecesium::CameraSmoother::step(cur, tgt, dt);
|
|
249
248
|
|
|
250
249
|
// ── Terrain floor clamp ───────────────────────────────────────────────
|
|
251
250
|
const float minAbove = engine_->getConfig().minAltitudeAboveTerrain;
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
#include "EngineTunables.hpp"
|
|
4
4
|
|
|
5
|
-
#include <algorithm>
|
|
6
5
|
#include <cmath>
|
|
7
6
|
|
|
8
7
|
namespace reactnativecesium {
|
|
@@ -24,40 +23,18 @@ inline double quatDotAbs(const glm::dquat& a, const glm::dquat& b) {
|
|
|
24
23
|
return std::abs(glm::dot(glm::normalize(a), glm::normalize(b)));
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
inline double wrapLonDeltaDeg(double a, double b) {
|
|
28
|
-
return std::fmod(b - a + 540.0, 360.0) - 180.0;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
26
|
} // namespace
|
|
32
27
|
|
|
33
28
|
CameraParams CameraSmoother::step(
|
|
34
|
-
const CameraParams& current, const CameraParams& target, double dt
|
|
35
|
-
double recentIntervalSec) {
|
|
29
|
+
const CameraParams& current, const CameraParams& target, double dt) {
|
|
36
30
|
CameraParams next = current;
|
|
37
31
|
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
tunables::kSmoothPositionTauMin,
|
|
45
|
-
tunables::kSmoothPositionTauMax);
|
|
46
|
-
const double aPos = 1.0 - std::exp(-dt / tau);
|
|
47
|
-
|
|
48
|
-
const double dLat = target.latitude - current.latitude;
|
|
49
|
-
const double dLon = wrapLonDeltaDeg(current.longitude, target.longitude);
|
|
50
|
-
const bool snapThrough =
|
|
51
|
-
std::abs(dLat) > tunables::kPosSnapThroughDeg ||
|
|
52
|
-
std::abs(dLon) > tunables::kPosSnapThroughDeg;
|
|
53
|
-
|
|
54
|
-
if (snapThrough) {
|
|
55
|
-
next.latitude = target.latitude;
|
|
56
|
-
next.longitude = target.longitude;
|
|
57
|
-
} else {
|
|
58
|
-
next.latitude = current.latitude + aPos * dLat;
|
|
59
|
-
next.longitude = lerpAngleDeg(current.longitude, target.longitude, aPos);
|
|
60
|
-
}
|
|
32
|
+
// lat/lon: snap directly to the target.
|
|
33
|
+
// CameraTargetState::snapshot() already produces a linearly-interpolated
|
|
34
|
+
// position that advances at constant speed between position fixes, so
|
|
35
|
+
// snapping here gives smooth, constant-velocity motion with no ease-in/out.
|
|
36
|
+
next.latitude = target.latitude;
|
|
37
|
+
next.longitude = target.longitude;
|
|
61
38
|
|
|
62
39
|
const double aAlt = 1.0 - std::exp(-tunables::kSmoothAltitude * dt);
|
|
63
40
|
const double aHdg = 1.0 - std::exp(-tunables::kSmoothHeading * dt);
|
|
@@ -76,11 +53,6 @@ CameraParams CameraSmoother::step(
|
|
|
76
53
|
next.viewCorrection = glm::normalize(glm::slerp(cq, tq, aViewQ));
|
|
77
54
|
|
|
78
55
|
// Eventually-exact convergence: snap once the residual is sub-epsilon.
|
|
79
|
-
if (std::abs(target.latitude - next.latitude) <= tunables::kEpsLatLon)
|
|
80
|
-
next.latitude = target.latitude;
|
|
81
|
-
if (std::abs(wrapLonDeltaDeg(next.longitude, target.longitude))
|
|
82
|
-
<= tunables::kEpsLatLon)
|
|
83
|
-
next.longitude = target.longitude;
|
|
84
56
|
if (std::abs(target.altitude - next.altitude) <= tunables::kEpsAltitudeMeters)
|
|
85
57
|
next.altitude = target.altitude;
|
|
86
58
|
if (angleDeltaAbsDeg(next.heading, target.heading) <= tunables::kEpsAngleDeg)
|
|
@@ -8,12 +8,9 @@ namespace reactnativecesium {
|
|
|
8
8
|
// returns the next camera state for a frame of duration `dt` seconds.
|
|
9
9
|
//
|
|
10
10
|
// Per-DoF smoothing rules:
|
|
11
|
-
// - lat / lon:
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// perceptible lag; sparse updates (1 Hz GPS) widen τ so each sample
|
|
15
|
-
// interpolates smoothly across its interval. Snap-through guard for
|
|
16
|
-
// genuine teleports keeps cross-planet `setCamera` jumps instant.
|
|
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.
|
|
17
14
|
// - altitude / heading / pitch / roll / viewCorrection: exponential decay
|
|
18
15
|
// `1 - exp(-k * dt)` per DoF with k taken from EngineTunables.
|
|
19
16
|
// - When the delta drops below the matching epsilon, the value is snapped
|
|
@@ -22,8 +19,7 @@ class CameraSmoother {
|
|
|
22
19
|
public:
|
|
23
20
|
static CameraParams step(const CameraParams& current,
|
|
24
21
|
const CameraParams& target,
|
|
25
|
-
double dt
|
|
26
|
-
double recentIntervalSec);
|
|
22
|
+
double dt);
|
|
27
23
|
};
|
|
28
24
|
|
|
29
25
|
} // namespace reactnativecesium
|
|
@@ -1,18 +1,130 @@
|
|
|
1
1
|
#include "CameraTargetState.hpp"
|
|
2
2
|
|
|
3
|
+
#include <algorithm>
|
|
3
4
|
#include <cmath>
|
|
4
5
|
|
|
5
6
|
namespace reactnativecesium {
|
|
6
7
|
|
|
7
8
|
namespace {
|
|
9
|
+
|
|
8
10
|
inline double angleDeltaAbsDeg(double a, double b) {
|
|
9
11
|
return std::abs(std::fmod(b - a + 540.0, 360.0) - 180.0);
|
|
10
12
|
}
|
|
13
|
+
|
|
11
14
|
inline double quatDotAbs(const glm::dquat& a, const glm::dquat& b) {
|
|
12
15
|
return std::abs(glm::dot(glm::normalize(a), glm::normalize(b)));
|
|
13
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
|
+
|
|
14
23
|
} // namespace
|
|
15
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
|
+
|
|
16
128
|
bool CameraTargetState::deltaExceedsEpsilon(
|
|
17
129
|
const CameraParams& a, const CameraParams& b) {
|
|
18
130
|
return std::abs(a.latitude - b.latitude) > tunables::kEpsLatLon
|
|
@@ -25,25 +137,4 @@ bool CameraTargetState::deltaExceedsEpsilon(
|
|
|
25
137
|
> tunables::kEpsQuatDot;
|
|
26
138
|
}
|
|
27
139
|
|
|
28
|
-
void CameraTargetState::noteUpdateCadence() noexcept {
|
|
29
|
-
const auto now = std::chrono::steady_clock::now();
|
|
30
|
-
if (!hasLastUpdate_) {
|
|
31
|
-
// Seed the EWMA with τmax so the first sample after a quiet period uses
|
|
32
|
-
// the full smoothing budget rather than the floor (5 ms ≈ snap).
|
|
33
|
-
ewmaIntervalSec_.store(tunables::kSmoothPositionTauMax / tunables::kSmoothPositionAlpha,
|
|
34
|
-
std::memory_order_release);
|
|
35
|
-
lastUpdateTime_ = now;
|
|
36
|
-
hasLastUpdate_ = true;
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const double dt = std::chrono::duration<double>(now - lastUpdateTime_).count();
|
|
41
|
-
lastUpdateTime_ = now;
|
|
42
|
-
if (dt <= 0.0) return; // back-to-back writes from the same frame: ignore.
|
|
43
|
-
|
|
44
|
-
const double prev = ewmaIntervalSec_.load(std::memory_order_relaxed);
|
|
45
|
-
const double next = prev + tunables::kEwmaIntervalAlpha * (dt - prev);
|
|
46
|
-
ewmaIntervalSec_.store(next, std::memory_order_release);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
140
|
} // namespace reactnativecesium
|
|
@@ -21,54 +21,43 @@ namespace reactnativecesium {
|
|
|
21
21
|
//
|
|
22
22
|
// `dirty_` and `forceRender_` are independent atomic flags so the read side can
|
|
23
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.
|
|
24
41
|
class CameraTargetState {
|
|
25
42
|
public:
|
|
26
43
|
CameraTargetState() = default;
|
|
27
44
|
|
|
28
|
-
// Replace the entire target
|
|
29
|
-
//
|
|
30
|
-
void setAll(const CameraParams& target)
|
|
31
|
-
{
|
|
32
|
-
std::lock_guard<std::mutex> g(mutex_);
|
|
33
|
-
target_ = target;
|
|
34
|
-
}
|
|
35
|
-
noteUpdateCadence();
|
|
36
|
-
dirty_.store(true, std::memory_order_release);
|
|
37
|
-
forceRender_.store(true, std::memory_order_release);
|
|
38
|
-
}
|
|
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);
|
|
39
48
|
|
|
40
49
|
// Update lat/lon/alt + heading/pitch/roll, leaving viewCorrection unchanged.
|
|
50
|
+
// When lat/lon actually changes, updates the linear-interpolation bookkeeping.
|
|
41
51
|
void setHpr(double lat, double lon, double alt,
|
|
42
|
-
double heading, double pitch, double roll)
|
|
43
|
-
{
|
|
44
|
-
std::lock_guard<std::mutex> g(mutex_);
|
|
45
|
-
target_.latitude = lat;
|
|
46
|
-
target_.longitude = lon;
|
|
47
|
-
target_.altitude = alt;
|
|
48
|
-
target_.heading = heading;
|
|
49
|
-
target_.pitch = pitch;
|
|
50
|
-
target_.roll = roll;
|
|
51
|
-
}
|
|
52
|
-
noteUpdateCadence();
|
|
53
|
-
dirty_.store(true, std::memory_order_release);
|
|
54
|
-
forceRender_.store(true, std::memory_order_release);
|
|
55
|
-
}
|
|
52
|
+
double heading, double pitch, double roll);
|
|
56
53
|
|
|
57
|
-
void setViewCorrection(const glm::dquat& q)
|
|
58
|
-
{
|
|
59
|
-
std::lock_guard<std::mutex> g(mutex_);
|
|
60
|
-
target_.viewCorrection = q;
|
|
61
|
-
}
|
|
62
|
-
noteUpdateCadence();
|
|
63
|
-
dirty_.store(true, std::memory_order_release);
|
|
64
|
-
forceRender_.store(true, std::memory_order_release);
|
|
65
|
-
}
|
|
54
|
+
void setViewCorrection(const glm::dquat& q);
|
|
66
55
|
|
|
67
|
-
// Snapshot the demand target
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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;
|
|
72
61
|
|
|
73
62
|
// Lightweight, lock-free check used by the render thread to decide whether
|
|
74
63
|
// it should bother taking a full snapshot at all.
|
|
@@ -91,31 +80,24 @@ public:
|
|
|
91
80
|
// whether to render this frame.
|
|
92
81
|
static bool deltaExceedsEpsilon(const CameraParams& a, const CameraParams& b);
|
|
93
82
|
|
|
94
|
-
// EWMA of the inter-arrival interval (seconds) of demand updates. Used by
|
|
95
|
-
// CameraSmoother to scale the lat/lon time constant: high update rate ⇒
|
|
96
|
-
// tiny τ (snappy gestures); low update rate ⇒ larger τ (smooth GPS catch-up).
|
|
97
|
-
// Lock-free read on the render-thread hot path.
|
|
98
|
-
double recentIntervalSec() const noexcept {
|
|
99
|
-
return ewmaIntervalSec_.load(std::memory_order_acquire);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
83
|
private:
|
|
103
|
-
// Folds the time since the previous demand update into ewmaIntervalSec_.
|
|
104
|
-
// Called under (or after) the target_ mutex; the EWMA itself is atomic so
|
|
105
|
-
// the read side never blocks. Writes are uncontended (only JS-thread).
|
|
106
|
-
void noteUpdateCadence() noexcept;
|
|
107
|
-
|
|
108
84
|
mutable std::mutex mutex_;
|
|
109
85
|
CameraParams target_;
|
|
110
86
|
std::atomic<bool> dirty_{false};
|
|
111
87
|
std::atomic<bool> forceRender_{true};
|
|
112
88
|
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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};
|
|
119
101
|
};
|
|
120
102
|
|
|
121
103
|
} // namespace reactnativecesium
|
package/ios/CesiumBridge.mm
CHANGED
|
@@ -277,8 +277,7 @@
|
|
|
277
277
|
|
|
278
278
|
const auto cur = _engine->camera().getParams();
|
|
279
279
|
const auto tgt = _target.snapshot();
|
|
280
|
-
auto smooth = reactnativecesium::CameraSmoother::step(
|
|
281
|
-
cur, tgt, dt, _target.recentIntervalSec());
|
|
280
|
+
auto smooth = reactnativecesium::CameraSmoother::step(cur, tgt, dt);
|
|
282
281
|
|
|
283
282
|
// ── Terrain floor clamp ───────────────────────────────────────────────
|
|
284
283
|
// Uses terrain floor from the previous frame (one-frame lag is
|
package/package.json
CHANGED