|
2 | 2 |
|
3 | 3 | #include "physics/discrete_trajectory_segment.hpp"
|
4 | 4 |
|
| 5 | +#include <algorithm> |
5 | 6 | #include <iterator>
|
| 7 | +#include <list> |
| 8 | +#include <vector> |
6 | 9 |
|
7 | 10 | #include "astronomy/epoch.hpp"
|
| 11 | +#include "geometry/named_quantities.hpp" |
8 | 12 | #include "glog/logging.h"
|
| 13 | +#include "numerics/fit_hermite_spline.hpp" |
9 | 14 |
|
10 | 15 | namespace principia {
|
11 | 16 | namespace physics {
|
12 | 17 | namespace internal_discrete_trajectory_segment {
|
13 | 18 |
|
14 | 19 | using astronomy::InfiniteFuture;
|
15 | 20 | using astronomy::InfinitePast;
|
| 21 | +using geometry::Position; |
| 22 | +using numerics::FitHermiteSpline; |
16 | 23 |
|
17 | 24 | template<typename Frame>
|
18 | 25 | DiscreteTrajectorySegment<Frame>::DiscreteTrajectorySegment(
|
@@ -160,34 +167,114 @@ absl::Status DiscreteTrajectorySegment<Frame>::Append(
|
160 | 167 | << "Append out of order at " << t << ", last time is "
|
161 | 168 | << timeline_.crbegin()->first;
|
162 | 169 |
|
163 |
| - // TODO(phl): Downsampling. |
164 |
| - return absl::OkStatus(); |
| 170 | + if (downsampling_parameters_.has_value()) { |
| 171 | + return DownsampleIfNeeded(); |
| 172 | + } else { |
| 173 | + return absl::OkStatus(); |
| 174 | + } |
165 | 175 | }
|
166 | 176 |
|
167 | 177 | template<typename Frame>
|
168 | 178 | void DiscreteTrajectorySegment<Frame>::ForgetAfter(Instant const& t) {
|
169 | 179 | ForgetAfter(timeline_.lower_bound(t));
|
170 |
| - // TODO(phl): Downsampling. |
171 | 180 | }
|
172 | 181 |
|
173 | 182 | template<typename Frame>
|
174 | 183 | void DiscreteTrajectorySegment<Frame>::ForgetAfter(
|
175 | 184 | typename Timeline::const_iterator const begin) {
|
176 |
| - timeline_.erase(begin, timeline_.end()); |
177 |
| - // TODO(phl): Downsampling. |
| 185 | + std::int64_t number_of_points_to_remove = |
| 186 | + std::distance(begin, timeline_.cend()); |
| 187 | + number_of_dense_points_ = |
| 188 | + std::max(0LL, number_of_dense_points_ - number_of_points_to_remove); |
| 189 | + |
| 190 | + timeline_.erase(begin, timeline_.cend()); |
178 | 191 | }
|
179 | 192 |
|
180 | 193 | template<typename Frame>
|
181 | 194 | void DiscreteTrajectorySegment<Frame>::ForgetBefore(Instant const& t) {
|
182 | 195 | ForgetBefore(timeline_.lower_bound(t));
|
183 |
| - // TODO(phl): Downsampling. |
184 | 196 | }
|
185 | 197 |
|
186 | 198 | template<typename Frame>
|
187 | 199 | void DiscreteTrajectorySegment<Frame>::ForgetBefore(
|
188 | 200 | typename Timeline::const_iterator const end) {
|
189 |
| - timeline_.erase(timeline_.begin(), end); |
190 |
| - // TODO(phl): Downsampling. |
| 201 | + std::int64_t number_of_points_to_remove = |
| 202 | + std::distance(timeline_.cbegin(), end); |
| 203 | + number_of_dense_points_ = |
| 204 | + std::max(0LL, number_of_dense_points_ - number_of_points_to_remove); |
| 205 | + |
| 206 | + timeline_.erase(timeline_.cbegin(), end); |
| 207 | +} |
| 208 | + |
| 209 | +template<typename Frame> |
| 210 | +void DiscreteTrajectorySegment<Frame>::SetDownsampling( |
| 211 | + DownsamplingParameters const& downsampling_parameters) { |
| 212 | + // The semantics of changing downsampling on a segment that has 2 points or |
| 213 | + // more are unclear. Let's not do that. |
| 214 | + CHECK_LE(timeline_.size(), 1); |
| 215 | + downsampling_parameters_ = downsampling_parameters; |
| 216 | + number_of_dense_points_ = timeline_.empty() ? 0 : 1; |
| 217 | +} |
| 218 | + |
| 219 | +template<typename Frame> |
| 220 | +void DiscreteTrajectorySegment<Frame>::ClearDownsampling() { |
| 221 | + downsampling_parameters_ = std::nullopt; |
| 222 | +} |
| 223 | + |
| 224 | +template<typename Frame> |
| 225 | +absl::Status DiscreteTrajectorySegment<Frame>::DownsampleIfNeeded() { |
| 226 | + ++number_of_dense_points_; |
| 227 | + // Points, hence one more than intervals. |
| 228 | + if (number_of_dense_points_ > |
| 229 | + downsampling_parameters_->max_dense_intervals) { |
| 230 | + // Obtain iterators for all the dense points of the segment. |
| 231 | + using ConstIterators = std::vector<typename Timeline::const_iterator>; |
| 232 | + ConstIterators dense_iterators(number_of_dense_points_); |
| 233 | + CHECK_LE(dense_iterators.size(), timeline_.size()); |
| 234 | + auto it = timeline_.crbegin(); |
| 235 | + for (int i = dense_iterators.size() - 1; i >= 0; --i) { |
| 236 | + dense_iterators[i] = std::prev(it.base()); |
| 237 | + ++it; |
| 238 | + } |
| 239 | + |
| 240 | + absl::StatusOr<std::list<ConstIterators::const_iterator>> |
| 241 | + right_endpoints = FitHermiteSpline<Instant, Position<Frame>>( |
| 242 | + dense_iterators, |
| 243 | + [](auto&& it) -> auto&& { return it->first; }, |
| 244 | + [](auto&& it) -> auto&& { return it->second.position(); }, |
| 245 | + [](auto&& it) -> auto&& { return it->second.velocity(); }, |
| 246 | + downsampling_parameters_->tolerance); |
| 247 | + if (!right_endpoints.ok()) { |
| 248 | + // Note that the actual appending took place; the propagated status only |
| 249 | + // reflects a lack of downsampling. |
| 250 | + return right_endpoints.status(); |
| 251 | + } |
| 252 | + |
| 253 | + if (right_endpoints->empty()) { |
| 254 | + number_of_dense_points_ = timeline_.empty() ? 0 : 1; |
| 255 | + return absl::OkStatus(); |
| 256 | + } |
| 257 | + |
| 258 | + // Obtain the times for the right endpoints. This is necessary because we |
| 259 | + // cannot use iterators for erasing points, as they would get invalidated |
| 260 | + // after the first erasure. |
| 261 | + std::vector<Instant> right_endpoints_times; |
| 262 | + right_endpoints_times.reserve(right_endpoints.value().size()); |
| 263 | + for (auto const& it_in_dense_iterators : right_endpoints.value()) { |
| 264 | + right_endpoints_times.push_back((*it_in_dense_iterators)->first); |
| 265 | + } |
| 266 | + |
| 267 | + // Poke holes in the timeline at the places given by |
| 268 | + // |right_endpoints_times|. This requires one lookup per erasure. |
| 269 | + auto left_it = dense_iterators.front(); |
| 270 | + for (Instant const& right : right_endpoints_times) { |
| 271 | + ++left_it; |
| 272 | + auto const right_it = timeline_.find(right); |
| 273 | + left_it = timeline_.erase(left_it, right_it); |
| 274 | + } |
| 275 | + number_of_dense_points_ = std::distance(left_it, timeline_.cend()); |
| 276 | + } |
| 277 | + return absl::OkStatus(); |
191 | 278 | }
|
192 | 279 |
|
193 | 280 | template<typename Frame>
|
|
0 commit comments