GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
47 / 75
62.7%
Branches:
38 / 90
42.2%
Functions:
7 / 15
46.7%

scene/time_samples.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file time_samples.cpp
3 /// @brief Bracketing time codes among samples, and interpolating between them.
4 #include "ernie/scene/time_samples.h"
5
6 #include <glm/gtc/quaternion.hpp>
7
8 #include <algorithm>
9 #include <vector>
10
11 namespace ernie {
12 namespace {
13
14 2 template <typename T> std::vector<T> Mix(const T* a, const T* b, std::size_t count, double alpha) {
15 2 std::vector<T> out(count);
16
2/4
std::vector<double, std::allocator<double> > ernie::(anonymous namespace)::Mix<double>(double const*, double const*, unsigned long, double):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
std::vector<float, std::allocator<float> > ernie::(anonymous namespace)::Mix<float>(float const*, float const*, unsigned long, double):
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 for (std::size_t i = 0; i < count; ++i) {
17 5 out[i] = static_cast<T>(a[i] + (b[i] - a[i]) * alpha);
18 }
19 2 return out;
20 }
21
22 } // namespace
23
24 38 SampleBracket Bracket(std::span<const TimeSample> samples, double time) {
25 38 const auto above = std::ranges::upper_bound(samples, time, {}, &TimeSample::time);
26
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 37 times.
38 if (above == samples.begin()) {
27 1 return {};
28 }
29
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 15 times.
37 const auto lower = static_cast<std::size_t>(above - samples.begin()) - 1;
30
4/4
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 9 times.
37 if (above == samples.end() || time <= samples[lower].time) {
31 28 return {lower, lower, 0.0};
32 }
33 9 const double t0 = samples[lower].time;
34 9 return {lower, lower + 1, (time - t0) / (above->time - t0)};
35 }
36
37 12 Value Interpolate(const Value& a, const Value& b, double alpha) {
38
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if (alpha <= 0.0 || a.Raw().index() != b.Raw().index()) {
39 ✗ return a;
40 }
41 12 return std::visit(
42 2 [&](const auto& x) -> Value {
43 using T = std::decay_t<decltype(x)>;
44
2/20
ernie::Value ernie::Interpolate(ernie::Value const&, ernie::Value const&, double)::{lambda(auto:1 const&)#1}::operator()<glm::mat<4, 4, double, (glm::qualifier)0> >(glm::mat<4, 4, double, (glm::qualifier)0> const&) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
ernie::Value ernie::Interpolate(ernie::Value const&, ernie::Value const&, double)::{lambda(auto:1 const&)#1}::operator()<glm::qua<double, (glm::qualifier)0> >(glm::qua<double, (glm::qualifier)0> const&) const:
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
ernie::Value ernie::Interpolate(ernie::Value const&, ernie::Value const&, double)::{lambda(auto:1 const&)#1}::operator()<glm::vec<2, double, (glm::qualifier)0> >(glm::vec<2, double, (glm::qualifier)0> const&) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
ernie::Value ernie::Interpolate(ernie::Value const&, ernie::Value const&, double)::{lambda(auto:1 const&)#1}::operator()<glm::vec<2, float, (glm::qualifier)0> >(glm::vec<2, float, (glm::qualifier)0> const&) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
ernie::Value ernie::Interpolate(ernie::Value const&, ernie::Value const&, double)::{lambda(auto:1 const&)#1}::operator()<glm::vec<3, double, (glm::qualifier)0> >(glm::vec<3, double, (glm::qualifier)0> const&) const:
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
ernie::Value ernie::Interpolate(ernie::Value const&, ernie::Value const&, double)::{lambda(auto:1 const&)#1}::operator()<glm::vec<3, float, (glm::qualifier)0> >(glm::vec<3, float, (glm::qualifier)0> const&) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
ernie::Value ernie::Interpolate(ernie::Value const&, ernie::Value const&, double)::{lambda(auto:1 const&)#1}::operator()<glm::vec<4, double, (glm::qualifier)0> >(glm::vec<4, double, (glm::qualifier)0> const&) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
ernie::Value ernie::Interpolate(ernie::Value const&, ernie::Value const&, double)::{lambda(auto:1 const&)#1}::operator()<glm::vec<4, float, (glm::qualifier)0> >(glm::vec<4, float, (glm::qualifier)0> const&) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
ernie::Value ernie::Interpolate(ernie::Value const&, ernie::Value const&, double)::{lambda(auto:1 const&)#1}::operator()<std::vector<float, std::allocator<float> > >(std::vector<float, std::allocator<float> > const&) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 const T& y = *b.Get<T>();
45 if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
46 return static_cast<T>(x + (y - x) * alpha);
47 } else if constexpr (std::is_same_v<T, glm::dquat>) {
48 1 return glm::slerp(x, y, alpha);
49 } else if constexpr (requires { T::length(); }) {
50 // Vectors and matrices of floats or doubles; integer vectors hold.
51 if constexpr (std::is_floating_point_v<typename T::value_type>) {
52 1 return x + (y - x) * static_cast<typename T::value_type>(alpha);
53 } else {
54 return a;
55 }
56 } else if constexpr (std::is_same_v<T, std::vector<float>>) {
57
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ return x.size() == y.size() ? Value(Mix(x.data(), y.data(), x.size(), alpha)) : a;
58 } else {
59 return a;
60 }
61 },
62 12 a.Raw());
63 }
64
65 3 std::optional<Tensor> Interpolate(const Tensor& a, const Tensor& b, double alpha) {
66
4/8
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
3 const bool mixable = !a.Empty() && a.OnHost() && b.OnHost() && a.Type() == b.Type() &&
67
3/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 time.
6 a.Shape() == b.Shape() &&
68
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 (a.Type() == DType::Float32 || a.Type() == DType::Float64);
69 3 if (!mixable) {
70 1 return std::nullopt;
71 }
72 2 const Tensor x = a.Contiguous();
73
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const Tensor y = b.Contiguous();
74
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const auto count = static_cast<std::size_t>(x.Elements());
75
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (a.Type() == DType::Float32) {
76
5/10
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
4 return Tensor::Copy<float>(Mix(x.As<float>(), y.As<float>(), count, alpha), a.Shape());
77 }
78
0/10
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ return Tensor::Copy<double>(Mix(x.As<double>(), y.As<double>(), count, alpha), a.Shape());
79 2 }
80
81 34 TimeSample Evaluate(std::span<const TimeSample> samples, double time) {
82 34 const SampleBracket bracket = Bracket(samples, time);
83
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 8 times.
34 const TimeSample& lower = samples[bracket.lower];
84 34 const TimeSample& upper = samples[bracket.upper];
85
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 8 times.
34 if (bracket.alpha == 0.0) {
86 26 return lower;
87 }
88
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 7 times.
8 if (!lower.tensor.Empty()) {
89 1 return {time,
90 {},
91
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
2 Interpolate(lower.tensor, upper.tensor, bracket.alpha).value_or(lower.tensor)};
92 }
93 7 return {time, Interpolate(lower.value, upper.value, bracket.alpha), {}};
94
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
9 }
95
96 } // namespace ernie
97