GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
506 / 547
92.5%
Branches:
355 / 646
55.0%
Functions:
103 / 106
97.2%

scene/scene.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file scene.cpp
3 /// @brief Prims, attributes, transforms and bounds over the columnar store.
4 #include "ernie/scene/scene.h"
5
6 #include "ernie/scene/convert.h"
7 #include "ernie/scene/schema/geom.h"
8 #include "ernie/scene/tokens.h"
9 #include "ernie/scene/xform.h"
10 #include "ernie/util/algorithm.h"
11 #include "ernie/util/profiler.h"
12
13 #include <algorithm>
14 #include <atomic>
15 #include <stdexcept>
16 #include <string>
17
18 namespace ernie {
19 namespace {
20
21 /// @return A column layout for the attribute `name` holding `fallback` in unauthored rows.
22 8432 ColumnLayout MakeLayout(PathTable& paths, const AttributeDef& def, std::string_view name,
23 const Value& fallback) {
24 8432 const AttributeId attribute = paths.Names().Intern(name);
25 8432 return WithColumnType(def.type, [&]<typename T>(std::type_identity<T>) {
26 if constexpr (std::is_same_v<T, Tensor>) {
27
1/2
✓ Branch 1 taken 572 times.
✗ Branch 2 not taken.
572 return ColumnLayout::Make<T>(attribute);
28 } else {
29
11/26
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<double>(std::type_identity<double>) const:
✓ Branch 0 taken 1702 times.
✗ Branch 1 not taken.
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<ernie::Target>(std::type_identity<ernie::Target>) const:
✓ Branch 1 taken 481 times.
✗ Branch 2 not taken.
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::mat<4, 4, double, (glm::qualifier)0> >(std::type_identity<glm::mat<4, 4, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::qua<double, (glm::qualifier)0> >(std::type_identity<glm::qua<double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<2, double, (glm::qualifier)0> >(std::type_identity<glm::vec<2, double, (glm::qualifier)0> >) const:
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<3, double, (glm::qualifier)0> >(std::type_identity<glm::vec<3, double, (glm::qualifier)0> >) const:
✓ Branch 0 taken 279 times.
✗ Branch 1 not taken.
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<4, double, (glm::qualifier)0> >(std::type_identity<glm::vec<4, double, (glm::qualifier)0> >) const:
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<int>(std::type_identity<int>) const:
✓ Branch 0 taken 1041 times.
✗ Branch 1 not taken.
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<ernie::Target, std::allocator<ernie::Target> > >(std::type_identity<std::vector<ernie::Target, std::allocator<ernie::Target> > >) const:
✓ Branch 3 taken 279 times.
✗ Branch 4 not taken.
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<float, std::allocator<float> > >(std::type_identity<std::vector<float, std::allocator<float> > >) const:
✓ Branch 3 taken 296 times.
✗ Branch 4 not taken.
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<unsigned int, std::allocator<unsigned int> > >(std::type_identity<std::vector<unsigned int, std::allocator<unsigned int> > >) const:
✓ Branch 3 taken 446 times.
✗ Branch 4 not taken.
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<unsigned char>(std::type_identity<unsigned char>) const:
✓ Branch 0 taken 509 times.
✗ Branch 1 not taken.
auto ernie::(anonymous namespace)::MakeLayout(ernie::PathTable&, ernie::AttributeDef const&, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<unsigned int>(std::type_identity<unsigned int>) const:
✓ Branch 1 taken 2748 times.
✗ Branch 2 not taken.
14699 return ColumnLayout::Make<T>(attribute, ToElement<T>(paths, fallback).value_or(T{}));
30 }
31 });
32 }
33
34 13 std::string Describe(const Scene& scene, PrimId prim) {
35 13 const std::string_view type = scene.TypeName(prim);
36
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 13 times.
✗ Branch 7 not taken.
39 return "'" + scene.PrimPath(prim).String() + "'" +
37
7/18
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 13 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 13 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 13 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 13 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 13 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
65 (type.empty() ? std::string() : " (" + std::string(type) + ")");
38 }
39
40 233 std::uint64_t NextSceneId() {
41 233 static std::atomic<std::uint64_t> next{1};
42 233 return next++;
43 }
44
45 } // namespace
46
47 233 Scene::Scene()
48
1/2
✓ Branch 2 taken 233 times.
✗ Branch 3 not taken.
466 : id_(NextSceneId()), inherited_(store_.Tokens().Intern(tokens::kInherited)),
49
1/2
✓ Branch 1 taken 233 times.
✗ Branch 2 not taken.
233 invisible_(store_.Tokens().Intern(tokens::kInvisible)),
50
3/6
✓ Branch 2 taken 233 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 233 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 233 times.
✗ Branch 9 not taken.
466 defaultPurpose_(store_.Tokens().Intern(tokens::kDefault)) {
51
1/2
✓ Branch 1 taken 233 times.
✗ Branch 2 not taken.
233 FindOrAddBucket(nullptr, {});
52 233 }
53
54 2739 PrimId Scene::Define(const Path& path, std::string_view type) {
55
3/4
✓ Branch 0 taken 2739 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 2738 times.
2739 if (path.IsEmpty() || path.IsRoot()) {
56
2/4
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
3 throw std::invalid_argument("Cannot define the prim at '" + path.String() + "'");
57 }
58
2/2
✓ Branch 0 taken 2726 times.
✓ Branch 1 taken 12 times.
2738 const SchemaDef* schema = type.empty() ? nullptr : FindSchema(type);
59
6/6
✓ Branch 0 taken 2726 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 2723 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 2722 times.
✓ Branch 5 taken 1 time.
2738 if (!type.empty() && (schema == nullptr || schema->kind != SchemaKind::Concrete)) {
60
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
12 throw std::invalid_argument("Cannot define '" + path.String() + "' as '" +
61
2/4
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
16 std::string(type) + "', which is no prim type");
62 }
63 2734 PathTable& paths = store_.Paths();
64 2734 const PathId id = paths.Intern(path);
65
2/2
✓ Branch 0 taken 930 times.
✓ Branch 1 taken 2734 times.
3664 for (PathId above = paths.Parent(id); above != kRootPath; above = paths.Parent(above)) {
66
2/2
✓ Branch 1 taken 148 times.
✓ Branch 2 taken 782 times.
930 if (!store_.Find(above)) {
67 148 store_.Define(above, 0);
68 }
69 }
70
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 2729 times.
2734 if (const std::optional<PrimId> existing = store_.Find(id)) {
71
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
5 MoveTo(*existing, schema, Apis(*existing));
72 5 return *existing;
73 }
74
2/4
✓ Branch 1 taken 2729 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2729 times.
✗ Branch 5 not taken.
2729 return store_.Define(id, FindOrAddBucket(schema, {}));
75 }
76
77 258 bool Scene::Remove(const Path& path) {
78
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 257 times.
258 if (path.IsRoot()) {
79
1/2
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 throw std::invalid_argument("Cannot remove the root prim");
80 }
81 257 const std::optional<PathId> id = store_.Paths().Find(path);
82
3/4
✓ Branch 0 taken 257 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 253 times.
257 if (!id || !store_.Find(*id)) {
83 4 return false;
84 }
85 253 std::vector<PathId> pending{*id};
86
2/2
✓ Branch 0 taken 261 times.
✓ Branch 1 taken 253 times.
514 while (!pending.empty()) {
87 261 const PathId next = pending.back();
88 261 pending.pop_back();
89
2/4
✓ Branch 1 taken 261 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 261 times.
✗ Branch 4 not taken.
261 if (const std::optional<PrimId> prim = store_.Find(next)) {
90 261 animated_.erase(prim->slot);
91
1/2
✓ Branch 1 taken 261 times.
✗ Branch 2 not taken.
261 store_.Remove(*prim);
92 }
93
1/2
✓ Branch 2 taken 261 times.
✗ Branch 3 not taken.
269 store_.Paths().ForEachChild(next, [&](PathId child) { pending.push_back(child); });
94 }
95 253 return true;
96 253 }
97
98 3641 std::optional<PrimId> Scene::Find(const Path& path) const {
99 3641 const std::optional<PathId> id = store_.Paths().Find(path);
100
2/2
✓ Branch 0 taken 3626 times.
✓ Branch 1 taken 15 times.
3641 return id ? store_.Find(*id) : std::nullopt;
101 }
102
103 4055 Path Scene::PrimPath(PrimId prim) const {
104 4055 return store_.Paths().ToPath(store_.PrimPath(prim));
105 }
106
107 159 std::string_view Scene::TypeName(PrimId prim) const {
108 159 const SchemaDef* schema = Schema(prim);
109
2/2
✓ Branch 0 taken 134 times.
✓ Branch 1 taken 25 times.
159 return schema != nullptr ? schema->name : std::string_view();
110 }
111
112 20861 const SchemaDef* Scene::Schema(PrimId prim) const {
113 20861 return buckets_[store_.PrimBucket(prim)].schema;
114 }
115
116 57 std::vector<Path> Scene::Children(const Path& path) const {
117 57 std::vector<Path> children;
118
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 25 times.
57 const PathTable& paths = store_.Paths();
119
4/6
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 25 times.
✓ Branch 3 taken 25 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 57 times.
✗ Branch 6 not taken.
57 if (const std::optional<PathId> id = path.IsRoot() ? kRootPath : paths.Find(path)) {
120
1/2
✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
57 paths.ForEachChild(*id, [&](PathId child) {
121
2/2
✓ Branch 1 taken 59 times.
✓ Branch 2 taken 1 time.
60 if (store_.Find(child)) {
122 118 children.push_back(paths.ToPath(child));
123 }
124 60 });
125 }
126 57 return children;
127 ✗ }
128
129 98 std::vector<Path> Scene::Prims(std::string_view type) const {
130
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 89 times.
98 const SchemaDef* base = type.empty() ? nullptr : FindSchema(type);
131
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 89 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 8 times.
98 if (!type.empty() && base == nullptr) {
132
3/6
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 time.
✗ Branch 9 not taken.
3 throw std::invalid_argument("'" + std::string(type) + "' is no prim type");
133 }
134 97 std::vector<Path> prims;
135
2/2
✓ Branch 0 taken 341 times.
✓ Branch 1 taken 97 times.
438 for (BucketId bucket = 0; bucket < buckets_.size(); ++bucket) {
136
5/6
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 310 times.
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 23 times.
341 if (base == nullptr || IsA(buckets_[bucket].schema, *base)) {
137
2/2
✓ Branch 0 taken 283 times.
✓ Branch 1 taken 318 times.
601 for (const PrimId prim : store_.GetBucket(bucket).prims) {
138
1/2
✓ Branch 1 taken 283 times.
✗ Branch 2 not taken.
566 prims.push_back(PrimPath(prim));
139 }
140 }
141 }
142 97 std::ranges::sort(prims);
143 97 return prims;
144 ✗ }
145
146 4880 void Scene::Set(PrimId prim, std::string_view attribute, const Value& value) {
147 4880 ClearSamples(prim, attribute);
148 4880 Author(prim, attribute, value);
149 4857 }
150
151 22 void Scene::Set(const Path& path, std::string_view attribute, const Value& value) {
152 22 Set(Require(path), attribute, value);
153 20 }
154
155 4909 void Scene::Author(PrimId prim, std::string_view attribute, const Value& value, Value* checked) {
156 4909 const AttributeId token = store_.Tokens().Intern(attribute);
157 4909 const AttributeDef* def = Declared(store_.PrimBucket(prim), token);
158
2/2
✓ Branch 0 taken 1802 times.
✓ Branch 1 taken 3107 times.
4909 if (def != nullptr) {
159
2/2
✓ Branch 2 taken 3093 times.
✓ Branch 3 taken 14 times.
3107 Validate(*def, attribute, value, PrimPath(prim));
160 }
161 4895 const ColumnType type = def != nullptr ? def->type : TypeForUndeclared(prim, attribute, value);
162 4888 WithColumnType(type, [&]<typename T>(std::type_identity<T>) {
163
9/18
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<double>(std::type_identity<double>) const:
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1237 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<ernie::Tensor>(std::type_identity<ernie::Tensor>) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::mat<4, 4, double, (glm::qualifier)0> >(std::type_identity<glm::mat<4, 4, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::qua<double, (glm::qualifier)0> >(std::type_identity<glm::qua<double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<2, double, (glm::qualifier)0> >(std::type_identity<glm::vec<2, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<3, double, (glm::qualifier)0> >(std::type_identity<glm::vec<3, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 1671 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<4, double, (glm::qualifier)0> >(std::type_identity<glm::vec<4, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<int>(std::type_identity<int>) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<unsigned char>(std::type_identity<unsigned char>) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
4888 std::optional<T> element = ToElement<T>(store_.Paths(), value);
164
15/28
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<double>(std::type_identity<double>) const:
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1237 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<ernie::Target>(std::type_identity<ernie::Target>) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<ernie::Tensor>(std::type_identity<ernie::Tensor>) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::mat<4, 4, double, (glm::qualifier)0> >(std::type_identity<glm::mat<4, 4, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::qua<double, (glm::qualifier)0> >(std::type_identity<glm::qua<double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<2, double, (glm::qualifier)0> >(std::type_identity<glm::vec<2, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<3, double, (glm::qualifier)0> >(std::type_identity<glm::vec<3, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 1671 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<4, double, (glm::qualifier)0> >(std::type_identity<glm::vec<4, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<int>(std::type_identity<int>) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<ernie::Target, std::allocator<ernie::Target> > >(std::type_identity<std::vector<ernie::Target, std::allocator<ernie::Target> > >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<float, std::allocator<float> > >(std::type_identity<std::vector<float, std::allocator<float> > >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<unsigned int, std::allocator<unsigned int> > >(std::type_identity<std::vector<unsigned int, std::allocator<unsigned int> > >) const:
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1599 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<unsigned char>(std::type_identity<unsigned char>) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<unsigned int>(std::type_identity<unsigned int>) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
4888 if (!element) {
165
8/112
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<double>(std::type_identity<double>) const:
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<ernie::Target>(std::type_identity<ernie::Target>) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<ernie::Tensor>(std::type_identity<ernie::Tensor>) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::mat<4, 4, double, (glm::qualifier)0> >(std::type_identity<glm::mat<4, 4, double, (glm::qualifier)0> >) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::qua<double, (glm::qualifier)0> >(std::type_identity<glm::qua<double, (glm::qualifier)0> >) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<2, double, (glm::qualifier)0> >(std::type_identity<glm::vec<2, double, (glm::qualifier)0> >) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<3, double, (glm::qualifier)0> >(std::type_identity<glm::vec<3, double, (glm::qualifier)0> >) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<4, double, (glm::qualifier)0> >(std::type_identity<glm::vec<4, double, (glm::qualifier)0> >) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<int>(std::type_identity<int>) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<ernie::Target, std::allocator<ernie::Target> > >(std::type_identity<std::vector<ernie::Target, std::allocator<ernie::Target> > >) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<float, std::allocator<float> > >(std::type_identity<std::vector<float, std::allocator<float> > >) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<unsigned int, std::allocator<unsigned int> > >(std::type_identity<std::vector<unsigned int, std::allocator<unsigned int> > >) const:
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 time.
✗ Branch 9 not taken.
✓ Branch 11 taken 1 time.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<unsigned char>(std::type_identity<unsigned char>) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<unsigned int>(std::type_identity<unsigned int>) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
12 throw std::invalid_argument(Describe(*this, prim) + ": '" + std::string(attribute) +
166 "' takes another type of value");
167 }
168
15/28
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<double>(std::type_identity<double>) const:
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1221 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<ernie::Target>(std::type_identity<ernie::Target>) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<ernie::Tensor>(std::type_identity<ernie::Tensor>) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::mat<4, 4, double, (glm::qualifier)0> >(std::type_identity<glm::mat<4, 4, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::qua<double, (glm::qualifier)0> >(std::type_identity<glm::qua<double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<2, double, (glm::qualifier)0> >(std::type_identity<glm::vec<2, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<3, double, (glm::qualifier)0> >(std::type_identity<glm::vec<3, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 1671 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<glm::vec<4, double, (glm::qualifier)0> >(std::type_identity<glm::vec<4, double, (glm::qualifier)0> >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<int>(std::type_identity<int>) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<ernie::Target, std::allocator<ernie::Target> > >(std::type_identity<std::vector<ernie::Target, std::allocator<ernie::Target> > >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<float, std::allocator<float> > >(std::type_identity<std::vector<float, std::allocator<float> > >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<unsigned int, std::allocator<unsigned int> > >(std::type_identity<std::vector<unsigned int, std::allocator<unsigned int> > >) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 1599 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<unsigned char>(std::type_identity<unsigned char>) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<unsigned int>(std::type_identity<unsigned int>) const:
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 135 times.
4885 if (checked != nullptr) {
169
0/6
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<ernie::Target, std::allocator<ernie::Target> > >(std::type_identity<std::vector<ernie::Target, std::allocator<ernie::Target> > >) const:
✗ Branch 1 not taken.
✗ Branch 2 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<float, std::allocator<float> > >(std::type_identity<std::vector<float, std::allocator<float> > >) const:
✗ Branch 1 not taken.
✗ Branch 2 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<unsigned int, std::allocator<unsigned int> > >(std::type_identity<std::vector<unsigned int, std::allocator<unsigned int> > >) const:
✗ Branch 1 not taken.
✗ Branch 2 not taken.
18 *checked = ToValue(store_.Paths(), *element);
170 } else {
171
3/8
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<ernie::Tensor>(std::type_identity<ernie::Tensor>) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<ernie::Target, std::allocator<ernie::Target> > >(std::type_identity<std::vector<ernie::Target, std::allocator<ernie::Target> > >) const:
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<float, std::allocator<float> > >(std::type_identity<std::vector<float, std::allocator<float> > >) const:
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
auto ernie::Scene::Author(ernie::PrimId, std::basic_string_view<char, std::char_traits<char> >, ernie::Value const&, ernie::Value*)::{lambda<typename $T0>(std::type_identity<$T0>)#1}::operator()<std::vector<unsigned int, std::allocator<unsigned int> > >(std::type_identity<std::vector<unsigned int, std::allocator<unsigned int> > >) const:
✓ Branch 1 taken 1599 times.
✗ Branch 2 not taken.
4868 store_.Set(prim, token, std::move(*element));
172 }
173 4885 });
174 4885 }
175
176 111 void Scene::Write(PrimId prim, std::string_view attribute, Tensor array) {
177 111 ClearSamples(prim, attribute);
178 111 CheckArray(prim, attribute, array);
179
2/4
✓ Branch 2 taken 104 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 104 times.
✗ Branch 6 not taken.
104 store_.Set(prim, store_.Tokens().Intern(attribute), std::move(array));
180 104 }
181
182 118 void Scene::CheckArray(PrimId prim, std::string_view attribute, const Tensor& array) const {
183
2/2
✓ Branch 1 taken 116 times.
✓ Branch 2 taken 2 times.
118 if (const AttributeDef* def = Declaration(prim, attribute)) {
184
2/2
✓ Branch 2 taken 109 times.
✓ Branch 3 taken 7 times.
225 ValidateArray(*def, attribute, array, PrimPath(prim));
185 } else {
186 2 RequireUndeclared(prim, attribute);
187 }
188 110 }
189
190 263 const Tensor* Scene::Read(PrimId prim, std::string_view attribute) const {
191 263 return Authored<Tensor>(prim, attribute);
192 }
193
194 9479 std::optional<Value> Scene::Get(PrimId prim, std::string_view attribute) const {
195 9479 return CellValue(prim, AuthoredColumn(prim, attribute));
196 }
197
198 4566 std::optional<Value> Scene::Get(PrimId prim, AttributeId attribute) const {
199 4566 return CellValue(prim, AuthoredColumn(prim, attribute));
200 }
201
202 90 std::optional<Value> Scene::GetOrFallback(PrimId prim, std::string_view attribute) const {
203
2/2
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 60 times.
90 if (std::optional<Value> value = Get(prim, attribute)) {
204 30 return value;
205 30 }
206 60 const AttributeDef* declared = Declaration(prim, attribute);
207
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 return declared != nullptr ? std::optional<Value>(declared->fallback) : std::nullopt;
208 }
209
210 15 std::optional<Value> Scene::GetResolved(PrimId prim, std::string_view attribute) const {
211
1/2
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 const std::optional<Token> token = store_.Tokens().Find(ResolvedName(attribute));
212
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 return CellValue(prim,
213
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 token ? store_.GetBucket(store_.PrimBucket(prim)).Find(*token) : nullptr);
214 }
215
216 14060 std::optional<Value> Scene::CellValue(PrimId prim, const Column* column) const {
217
2/2
✓ Branch 0 taken 1823 times.
✓ Branch 1 taken 12237 times.
14060 if (column == nullptr) {
218 1823 return std::nullopt;
219 }
220 12237 return WithColumnType(column->values->Type(), [&]<typename T>(std::type_identity<T>) {
221 12237 const auto values = store_.Read<T>(store_.PrimBucket(prim), column->attribute);
222 12237 return ToValue(store_.Paths(), values[store_.PrimRow(prim)]);
223 });
224 }
225
226 8 std::optional<Value> Scene::Get(const Path& path, std::string_view attribute) const {
227 8 return Get(Require(path), attribute);
228 }
229
230 10 bool Scene::Clear(PrimId prim, std::string_view attribute) {
231 10 ClearSamples(prim, attribute);
232 10 const Column* column = AuthoredColumn(prim, attribute);
233
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 time.
10 if (column != nullptr) {
234 9 store_.Clear(prim, column->attribute);
235 }
236 10 return column != nullptr;
237 }
238
239 8 bool Scene::IsAuthored(PrimId prim, std::string_view attribute) const {
240 8 return AuthoredColumn(prim, attribute) != nullptr;
241 }
242
243 247 std::vector<std::string> Scene::Attributes(const Path& path) const {
244 247 const PrimId prim = Require(path);
245 247 std::vector<std::string> names;
246
3/4
✓ Branch 1 taken 247 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3359 times.
✓ Branch 4 taken 247 times.
3606 for (const Column& column : store_.GetBucket(store_.PrimBucket(prim)).columns) {
247
3/4
✓ Branch 1 taken 3359 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 485 times.
✓ Branch 4 taken 2874 times.
3359 if (column.authored.Test(store_.PrimRow(prim))) {
248
1/2
✓ Branch 2 taken 485 times.
✗ Branch 3 not taken.
485 names.emplace_back(store_.Tokens().Text(column.attribute));
249 }
250 }
251 247 return names;
252 ✗ }
253
254 62 void Scene::Update() {
255 62 ERNIE_ZONE("Update the scene");
256
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 Resolve();
257 62 {
258
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 ERNIE_ZONE("Index relationship holders");
259
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 holders_.Update(store_);
260 62 }
261 62 {
262
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 ERNIE_ZONE("Compute extents");
263
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 extents_.Update(store_);
264 62 }
265 62 }
266
267 233 glm::dmat4 Scene::LocalTransform(PrimId prim) const {
268
2/2
✓ Branch 3 taken 205 times.
✓ Branch 4 taken 28 times.
233 return IsA(Schema(prim), Xformable::Definition()) ? ComposeXformOps(*this, prim)
269 28 : glm::dmat4(1.0);
270 }
271
272 56 glm::dmat4 Scene::WorldTransform(PrimId prim) const {
273 56 glm::dmat4 world = LocalTransform(prim);
274 56 const PathTable& paths = store_.Paths();
275 56 for (PathId above = paths.Parent(store_.PrimPath(prim));
276
4/4
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 51 times.
✓ Branch 3 taken 57 times.
✓ Branch 4 taken 5 times.
113 above != kRootPath && !ResetsXformStack(*this, prim); above = paths.Parent(above)) {
277 57 prim = *store_.Find(above);
278 114 world = LocalTransform(prim) * world;
279 }
280 56 return world;
281 }
282
283 13650 std::optional<Box> Scene::Extent(PrimId prim) const {
284 13650 const SchemaDef* schema = Schema(prim);
285
2/2
✓ Branch 0 taken 13640 times.
✓ Branch 1 taken 10 times.
13650 const ExtentRule rule = schema != nullptr ? schema->Extent() : kNoExtent;
286
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 13601 times.
13650 if (rule.kind != ExtentRule::Kind::Computed) {
287 49 return std::nullopt;
288 }
289 13601 const BucketId bucket = store_.PrimBucket(prim);
290 13601 const std::uint32_t row = store_.PrimRow(prim);
291
2/2
✓ Branch 1 taken 13434 times.
✓ Branch 2 taken 167 times.
13601 if (const std::optional<Box> cached = extents_.Find(store_, bucket, row)) {
292 13434 return cached;
293 }
294 167 Box extent;
295 167 rule.compute({store_, bucket, this}, {row, row + 1}, std::span(&extent, 1));
296 167 return extent;
297 }
298
299 7 std::vector<PrimId> Scene::Holders(const Path& target) const {
300 7 const std::optional<PathId> id = store_.Paths().Find(target);
301
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 time.
7 return id ? holders_.Find(store_, *id) : std::vector<PrimId>{};
302 }
303
304 25 Box Scene::WorldBounds(const Path& path) const {
305 25 const PrimId prim = Require(path);
306 25 const PathId id = store_.PrimPath(prim);
307
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 6 times.
25 const PathId parent = store_.Paths().Parent(id);
308 25 const glm::dmat4 above =
309
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 6 times.
25 parent == kRootPath ? glm::dmat4(1.0) : WorldTransform(*store_.Find(parent));
310 25 Box bounds;
311 25 AddTo(bounds, id, above, true);
312 25 return bounds;
313 }
314
315 9 Box Scene::BoundsInParent(PrimId prim) const {
316 9 Box bounds;
317 9 AddTo(bounds, store_.PrimPath(prim), glm::dmat4(1.0), false);
318 9 return bounds;
319 }
320
321 76 void Scene::AddTo(Box& bounds, PathId path, const glm::dmat4& parent, bool visibleOnly) const {
322 76 const PrimId prim = *store_.Find(path);
323
4/4
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 9 times.
✓ Branch 3 taken 63 times.
✓ Branch 4 taken 4 times.
76 if (visibleOnly && LeavesOutOfBounds(prim)) {
324 34 return;
325 }
326 72 const glm::dmat4 local = LocalTransform(prim);
327
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 71 times.
72 const glm::dmat4 world = ResetsXformStack(*this, prim) ? local : parent * local;
328 // A prim with an extent bounds what it is made of, so its children aren't added again.
329
2/2
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 42 times.
72 if (const std::optional<Box> extent = Extent(prim)) {
330 30 bounds.Extend(extent->Transformed(world));
331 30 return;
332 }
333 42 bool loaded = false;
334 42 store_.Paths().ForEachChild(path, [&](PathId child) {
335
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 if (store_.Find(child)) {
336 42 loaded = true;
337 42 AddTo(bounds, child, world, visibleOnly);
338 }
339 42 });
340 // A model whose subtree isn't loaded is bounded by its hint.
341
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 26 times.
42 if (!loaded) {
342 16 bounds.Extend(ExtentsHint(prim, !visibleOnly).Transformed(world));
343 }
344 }
345
346 3241 PrimId Scene::Require(const Path& path) const {
347 3241 const std::optional<PrimId> prim = Find(path);
348
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3238 times.
3241 if (!prim) {
349
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
9 throw std::invalid_argument("No prim at '" + path.String() + "'");
350 }
351 3238 return *prim;
352 }
353
354 3039 BucketId Scene::FindOrAddBucket(const SchemaDef* schema, std::vector<Token> apis) {
355
2/2
✓ Branch 0 taken 2794 times.
✓ Branch 1 taken 245 times.
3039 PathTable& paths = store_.Paths();
356
2/2
✓ Branch 0 taken 2794 times.
✓ Branch 1 taken 245 times.
3039 const BucketKey key{paths.Names().Intern(schema != nullptr ? schema->name : ""),
357
1/2
✓ Branch 1 taken 3039 times.
✗ Branch 2 not taken.
3039 std::move(apis)};
358
3/4
✓ Branch 1 taken 3039 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2324 times.
✓ Branch 4 taken 715 times.
3039 if (const std::optional<BucketId> found = store_.FindBucket(key)) {
359 2324 return *found;
360 }
361
1/2
✓ Branch 1 taken 715 times.
✗ Branch 2 not taken.
715 BucketSchema entry{schema, {}};
362
1/2
✓ Branch 1 taken 715 times.
✗ Branch 2 not taken.
715 const std::vector<ApiName> allApis = AllApis(schema, key.apiSchemas);
363 // The type's fallback overrides win over its API schemas', as USD's do.
364 7411 const auto fallback = [&](const AttributeDef& def, const std::string& name) -> const Value& {
365
1/2
✓ Branch 0 taken 6696 times.
✗ Branch 1 not taken.
6696 const Value* override = schema != nullptr ? schema->FallbackOverride(name) : nullptr;
366
4/4
✓ Branch 0 taken 13728 times.
✓ Branch 1 taken 139 times.
✓ Branch 2 taken 7171 times.
✓ Branch 3 taken 6557 times.
13867 for (auto api = allApis.begin(); override == nullptr && api != allApis.end(); ++api) {
367 7171 override = api->schema->FallbackOverride(name);
368 }
369
2/2
✓ Branch 0 taken 6557 times.
✓ Branch 1 taken 139 times.
6696 return override != nullptr ? *override : def.fallback;
370 715 };
371 715 std::vector<ColumnLayout> layout;
372 1441 const auto add = [&](const SchemaDef& declaring, const ApiName& api) {
373
2/2
✓ Branch 1 taken 6696 times.
✓ Branch 2 taken 726 times.
7422 for (const AttributeDef* def : declaring.AllAttributes()) {
374
1/2
✓ Branch 1 taken 6696 times.
✗ Branch 2 not taken.
6696 const std::string name = api.Attribute(def->name);
375
3/6
✓ Branch 1 taken 6696 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6696 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 6696 times.
13392 layout.push_back(MakeLayout(paths, *def, name, fallback(*def, name)));
376
1/2
✓ Branch 1 taken 6696 times.
✗ Branch 2 not taken.
6696 entry.declared.emplace(layout.back().attribute, def);
377 6696 }
378 726 };
379
2/2
✓ Branch 0 taken 482 times.
✓ Branch 1 taken 233 times.
715 if (schema != nullptr) {
380
1/2
✓ Branch 1 taken 482 times.
✗ Branch 2 not taken.
482 add(*schema, {});
381 }
382
2/2
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 715 times.
959 for (const ApiName& api : allApis) {
383
1/2
✓ Branch 1 taken 244 times.
✗ Branch 2 not taken.
244 add(*api.schema, api);
384 }
385
4/6
✓ Branch 1 taken 715 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 715 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 434 times.
✓ Branch 7 taken 281 times.
715 if (IsA(schema, Imageable::Definition())) {
386
3/4
✓ Branch 1 taken 434 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1736 times.
✓ Branch 4 taken 434 times.
2170 for (const AttributeDef* def : InheritedAttributes()) {
387
3/6
✓ Branch 1 taken 1736 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1736 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1736 times.
3472 layout.push_back(MakeLayout(paths, *def, ResolvedName(def->name), def->fallback));
388 }
389 }
390
1/2
✓ Branch 1 taken 715 times.
✗ Branch 2 not taken.
715 const BucketId bucket = store_.AddBucket(key, layout);
391
2/4
✓ Branch 0 taken 715 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 715 times.
✗ Branch 4 not taken.
1430 buckets_.resize(std::max<std::size_t>(buckets_.size(), bucket + 1));
392 715 buckets_[bucket] = std::move(entry);
393
3/4
✓ Branch 0 taken 482 times.
✓ Branch 1 taken 233 times.
✓ Branch 3 taken 482 times.
✗ Branch 4 not taken.
715 const ExtentRule rule = schema != nullptr ? schema->Extent() : kNoExtent;
394
4/4
✓ Branch 0 taken 227 times.
✓ Branch 1 taken 488 times.
✓ Branch 2 taken 203 times.
✓ Branch 3 taken 24 times.
715 const bool cached = rule.kind == ExtentRule::Kind::Computed && !rule.readsOtherPrims;
395
1/2
✓ Branch 1 taken 715 times.
✗ Branch 2 not taken.
715 extents_.Track(bucket, cached ? rule.compute : nullptr);
396 715 return bucket;
397 3754 }
398
399 77 void Scene::MoveTo(PrimId prim, const SchemaDef* schema, std::vector<Token> apis) {
400
2/4
✓ Branch 1 taken 77 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 77 times.
✗ Branch 5 not taken.
77 store_.Move(prim, FindOrAddBucket(schema, std::move(apis)));
401 77 }
402
403 309 const std::vector<Token>& Scene::Apis(PrimId prim) const {
404 309 return store_.GetBucket(store_.PrimBucket(prim)).key.apiSchemas;
405 }
406
407 79 void Scene::ApplyApi(PrimId prim, std::string_view api) {
408 79 const ApiName parsed = ApiName::Parse(api);
409
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 72 times.
73 if (HasApi(prim, api)) {
410 1 return;
411 }
412 72 const std::vector<std::string_view>& allowed = parsed.schema->appliesTo;
413 72 const SchemaDef* type = Schema(prim);
414
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 66 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 4 times.
72 if (!allowed.empty() && std::ranges::none_of(allowed, [&](std::string_view name) {
415 6 return IsA(type, *FindSchema(name));
416 })) {
417
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
6 throw std::invalid_argument(std::string(api) + " applies only to " +
418
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
8 std::string(allowed.front()) + " prims, not " +
419
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
6 Describe(*this, prim));
420 }
421 70 std::vector<Token> apis = Apis(prim);
422
2/4
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 70 times.
✗ Branch 5 not taken.
70 apis.push_back(store_.Tokens().Intern(api));
423
2/4
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 70 times.
✗ Branch 5 not taken.
70 MoveTo(prim, Schema(prim), std::move(apis));
424 70 }
425
426 3 bool Scene::RemoveApi(PrimId prim, std::string_view api) {
427
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
3 if (!HasApi(prim, api)) {
428 return false;
429 }
430 2 std::vector<Token> apis = Apis(prim);
431
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 std::erase(apis, *store_.Tokens().Find(api));
432
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 MoveTo(prim, Schema(prim), std::move(apis));
433 2 return true;
434 2 }
435
436 122 bool Scene::HasApi(PrimId prim, std::string_view api) const {
437
1/2
✓ Branch 4 taken 122 times.
✗ Branch 5 not taken.
122 return std::ranges::any_of(AllApis(Schema(prim), Apis(prim)),
438 252 [&](const ApiName& name) { return name.ToString() == api; });
439 }
440
441 845 std::vector<ApiName> Scene::AllApis(const SchemaDef* schema, std::span<const Token> applied) const {
442 845 std::vector<ApiName> apis;
443 // Each API schema once, followed by those built into it (MeshLightAPI's LightAPI, LightAPI's
444 // light-link collections).
445 1255 const auto add = [&](const auto& self, std::string_view name) -> void {
446 410 const ApiName api = ApiName::Parse(name);
447
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 410 times.
410 if (std::ranges::find(apis, api) != apis.end()) {
448 ✗ return;
449 }
450 410 apis.push_back(api);
451
2/2
✓ Branch 1 taken 185 times.
✓ Branch 2 taken 410 times.
595 for (const std::string_view builtin : api.schema->AllBuiltinApis()) {
452
1/2
✓ Branch 1 taken 185 times.
✗ Branch 2 not taken.
185 self(self, builtin);
453 }
454 845 };
455 925 for (const std::string_view builtin :
456
5/6
✓ Branch 0 taken 612 times.
✓ Branch 1 taken 233 times.
✓ Branch 3 taken 612 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 80 times.
✓ Branch 6 taken 845 times.
925 schema != nullptr ? schema->AllBuiltinApis() : std::vector<std::string_view>{}) {
457
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 add(add, builtin);
458 845 }
459
2/2
✓ Branch 0 taken 145 times.
✓ Branch 1 taken 845 times.
990 for (const Token token : applied) {
460
1/2
✓ Branch 2 taken 145 times.
✗ Branch 3 not taken.
145 add(add, store_.Tokens().Text(token));
461 }
462 845 return apis;
463 ✗ }
464
465 102 std::vector<std::string> Scene::AppliedApis(PrimId prim) const {
466 128 return Map(Apis(prim), [&](Token token) { return std::string(store_.Tokens().Text(token)); });
467 }
468
469 67 bool Scene::LeavesOutOfBounds(PrimId prim) const {
470 67 const Token* visibility = Authored<Token>(prim, Imageable::kVisibility);
471 // Purpose is inherited, so a subtree under another purpose is left out whole.
472 67 const Token* purpose = Authored<Token>(prim, Imageable::kPurpose);
473
6/8
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 63 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 time.
67 return (visibility && *visibility == invisible_) || (purpose && *purpose != defaultPurpose_);
474 }
475
476 16 Box Scene::ExtentsHint(PrimId prim, bool allPurposes) const {
477 16 const auto* hint = Authored<std::vector<float>>(prim, GeomModelAPI::kExtentsHint);
478
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 2 times.
16 if (hint == nullptr) {
479 14 return {};
480 }
481 // Six floats per purpose, the default purpose first.
482
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
2 const std::size_t floats = allPurposes ? hint->size() : std::min<std::size_t>(6, hint->size());
483 2 Box box;
484
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 for (std::size_t i = 0; i + 6 <= floats; i += 6) {
485 3 box.Extend(AuthoredExtent(std::span(*hint).subspan(i, 6)));
486 }
487 2 return box;
488 }
489
490 9497 const Column* Scene::AuthoredColumn(PrimId prim, std::string_view attribute) const {
491 9497 const std::optional<Token> token = store_.Tokens().Find(attribute);
492
2/2
✓ Branch 0 taken 9426 times.
✓ Branch 1 taken 71 times.
9497 return token ? AuthoredColumn(prim, *token) : nullptr;
493 }
494
495 13992 const Column* Scene::AuthoredColumn(PrimId prim, AttributeId attribute) const {
496 13992 const Column* column = store_.GetBucket(store_.PrimBucket(prim)).Find(attribute);
497
4/4
✓ Branch 0 taken 13977 times.
✓ Branch 1 taken 15 times.
✓ Branch 3 taken 1743 times.
✓ Branch 4 taken 12234 times.
13992 return column != nullptr && column->authored.Test(store_.PrimRow(prim)) ? column : nullptr;
498 }
499
500 658 const AttributeDef* Scene::Declaration(PrimId prim, std::string_view attribute) const {
501 658 const std::optional<Token> token = store_.Tokens().Find(attribute);
502
2/2
✓ Branch 0 taken 622 times.
✓ Branch 1 taken 36 times.
658 return token ? Declared(store_.PrimBucket(prim), *token) : nullptr;
503 }
504
505 5699 const AttributeDef* Scene::Declared(BucketId bucket, AttributeId attribute) const {
506 5699 const auto& declared = buckets_[bucket].declared;
507 5699 const auto found = declared.find(attribute);
508
2/2
✓ Branch 0 taken 3822 times.
✓ Branch 1 taken 1877 times.
9521 return found != declared.end() ? found->second : nullptr;
509 }
510
511 168 Dirty Scene::Classify(const ChangeSet::Values& change) const {
512
2/2
✓ Branch 1 taken 94 times.
✓ Branch 2 taken 74 times.
168 if (const AttributeDef* def = Declared(change.bucket, change.attribute)) {
513 94 return def->meta.dirty;
514 }
515 74 const std::string_view name = store_.Tokens().Text(change.attribute);
516
2/2
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 53 times.
74 if (name.starts_with(tokens::kResolvedPrefix)) {
517 21 return FindInherited(name.substr(tokens::kResolvedPrefix.size()))->meta.dirty;
518 }
519
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 52 times.
53 return name.starts_with(tokens::kXformOpPrefix) ? Dirty::Transform
520
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 : name.starts_with(tokens::kPrimvarsPrefix) ? Dirty::Primvars
521 : Dirty::Params;
522 }
523
524 1804 bool Scene::TakesUndeclared(PrimId prim, std::string_view attribute) const {
525
2/2
✓ Branch 1 taken 1803 times.
✓ Branch 2 taken 1 time.
1804 if (attribute.starts_with(tokens::kResolvedPrefix)) {
526 return false;
527 }
528 1803 const SchemaDef* schema = Schema(prim);
529
6/6
✓ Branch 0 taken 1799 times.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 1791 times.
✓ Branch 5 taken 7 times.
✓ Branch 6 taken 1 time.
1811 return schema == nullptr || schema->IsOpen(attribute) ||
530
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
1811 std::ranges::any_of(AllApis(schema, Apis(prim)),
531 4 [&](const ApiName& api) { return api.schema->IsOpen(attribute); });
532 }
533
534 1804 void Scene::RequireUndeclared(PrimId prim, std::string_view attribute) const {
535
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1796 times.
1804 if (!TakesUndeclared(prim, attribute)) {
536
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
24 throw std::invalid_argument(Describe(*this, prim) + " has no attribute '" +
537
2/4
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
32 std::string(attribute) + "'");
538 }
539 1796 }
540
541 1802 ColumnType Scene::TypeForUndeclared(PrimId prim, std::string_view attribute,
542 const Value& value) const {
543 1802 RequireUndeclared(prim, attribute);
544 1795 const Token token = *store_.Tokens().Find(attribute);
545
2/2
✓ Branch 2 taken 1605 times.
✓ Branch 3 taken 190 times.
1795 if (const Column* column = store_.GetBucket(store_.PrimBucket(prim)).Find(token)) {
546 1605 return column->values->Type();
547 }
548 190 const std::optional<ColumnType> type = InferColumnType(value);
549
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 190 times.
190 if (!type) {
550
0/6
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ throw std::invalid_argument(Describe(*this, prim) + ": '" + std::string(attribute) +
551
0/4
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ "' takes no empty value");
552 }
553 190 return *type;
554 }
555
556 18 std::optional<PrimId> ExtentRows::Resolve(const Target& target) const {
557
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
18 return target.path != kNoPath ? store.Find(target.path) : std::nullopt;
558 }
559
560 10 std::optional<Box> ExtentRows::TargetExtent(const Target& target, std::uint32_t row) const {
561 10 const std::optional<PrimId> other = Resolve(target);
562
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
10 const std::optional<Box> extent = other ? scene->Extent(*other) : std::nullopt;
563
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
10 if (!extent) {
564 2 return std::nullopt;
565 }
566 8 const PrimId own = store.GetBucket(bucket).prims[row];
567 24 return extent->Transformed(glm::inverse(scene->WorldTransform(own)) *
568 16 scene->WorldTransform(*other));
569 }
570
571 } // namespace ernie
572