chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
373 / 677
55.1%
Branches:
104 / 539
19.3%
Functions:
12 / 65
18.5%

python/src/render.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include <cstdint>
3 #include <optional>
4 #include <string>
5 #include <string_view>
6 #include <utility>
7 #include <vector>
8
9 #include <nanobind/stl/map.h>
10 #include <nanobind/stl/optional.h>
11 #include <nanobind/stl/string.h>
12 #include <nanobind/stl/string_view.h>
13 #include <nanobind/stl/vector.h>
14
15 #include "bindings.h"
16 #include "values.h"
17
18 namespace nb = nanobind;
19
20 namespace chrono::batch_render::python {
21
22 namespace {
23
24 2 void RegisterOutputs(nb::module_& module) {
25 2 nb::class_<IdLabel>(module, "IdLabel")
26 2 .def_ro("path", &IdLabel::path)
27 2 .def_ro("labels", &IdLabel::labels)
28 2 .def("__eq__", [](const IdLabel& a,
29
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ const IdLabel& b) { return a.path == b.path && a.labels == b.labels; })
30 2 .def("__repr__", [](const IdLabel& label) {
31 ✗ return nb::str("IdLabel(path={!r}, labels={!r})")
32
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ .format(label.path, nb::cast(label.labels));
33 });
34
35 2 nb::class_<RenderVarOutput>(module, "RenderVarOutput")
36 2 .def_ro("render_var_path", &RenderVarOutput::renderVarPath)
37 2 .def_ro("source_name", &RenderVarOutput::sourceName)
38 2 .def_ro("tensor", &RenderVarOutput::tensor)
39 2 .def_prop_ro("channels",
40 2 [](const RenderVarOutput& output) {
41 ✗ nb::dict channels;
42
0/3
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ for (const auto& [name, tensor] : output.channels) {
43
0/3
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 8 not taken.
✗ channels[nb::str(name.data(), name.size())] = nb::cast(tensor);
44 }
45 ✗ return channels;
46 ✗ })
47 2 .def_prop_ro("id_map",
48 ✗ [](const RenderVarOutput& output) -> nb::object {
49
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!output.idMap) {
50 ✗ return nb::none();
51 }
52 ✗ const bool single = output.sourceName == "SemanticSegmentation" ||
53
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ output.sourceName == "SemanticIdMap" ||
54
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ output.sourceName == "NonStableInstanceSegmentation";
55 ✗ nb::dict entries;
56
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ for (const auto& [id, label] : *output.idMap) {
57
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (single) {
58
0/2
✗ Branch 1 not taken.
✗ Branch 5 not taken.
✗ entries[nb::int_(id[0])] = label;
59 } else {
60
0/2
✗ Branch 1 not taken.
✗ Branch 5 not taken.
✗ entries[nb::make_tuple(id[0], id[1], id[2], id[3])] = label;
61 }
62 }
63 ✗ return entries;
64 ✗ })
65 2 .def("numpy",
66
1/1
✓ Branch 2 taken 174 times.
348 [](const RenderVarOutput& output) { return nb::cast(output.tensor).attr("numpy")(); });
67
68 2 nb::class_<FrameOutput>(module, "FrameOutput")
69 2 .def_ro("start_time", &FrameOutput::startTime)
70 2 .def_ro("end_time", &FrameOutput::endTime)
71 2 .def_ro("render_vars", &FrameOutput::renderVars)
72 2 .def_prop_ro("overlay",
73 2 [](const FrameOutput& frame) -> std::optional<device::Tensor> {
74
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (frame.overlay.Empty()) {
75 ✗ return std::nullopt;
76 }
77 ✗ return frame.overlay;
78 })
79 4 .def("render_var", &FrameOutput::RenderVar, nb::arg("path"))
80 4 .def("source", &FrameOutput::Source, nb::arg("source_name"));
81
82 2 module.def(
83 "decode_id_map",
84 2 [](const device::Tensor& map) {
85 ✗ nb::dict entries;
86
0/4
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ for (const auto& [id, text] : DecodeIdMap(map)) {
87
0/2
✗ Branch 1 not taken.
✗ Branch 5 not taken.
✗ entries[nb::make_tuple(id[0], id[1], id[2], id[3])] = text;
88 ✗ }
89 ✗ return entries;
90 ✗ },
91 2 nb::arg("map"));
92
93 2 nb::class_<ProductOutput>(module, "ProductOutput")
94 2 .def_ro("name", &ProductOutput::name)
95 2 .def_ro("frames", &ProductOutput::frames);
96
97 2 nb::class_<RenderOutput>(module, "RenderOutput")
98 2 .def_ro("simulation_start_time", &RenderOutput::simulationStartTime)
99 2 .def_ro("simulation_end_time", &RenderOutput::simulationEndTime)
100 4 .def_prop_ro("ready", &RenderOutput::Ready)
101 2 .def_prop_ro(
102 ✗ "done", [](const RenderOutput& output) { return output.Done(); },
103 nb::keep_alive<0, 1>())
104 2 .def(
105 "wait",
106 141 [](const RenderOutput& output, std::optional<uintptr_t> stream) {
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 139 times.
139 if (stream) {
108 ✗ output.Done().Wait(ToStream(stream));
109 } else {
110 139 output.Wait();
111 }
112 139 },
113 4 nb::arg("stream") = nb::none(), nb::call_guard<nb::gil_scoped_release>())
114 2 .def("__getitem__", &RenderOutput::operator[])
115 2 .def("__contains__", &RenderOutput::Contains)
116 2 .def("__len__", &RenderOutput::Size)
117 2 .def("products", [](const RenderOutput& output) {
118 ✗ std::vector<std::string> names;
119
0/3
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ for (const auto& [name, product] : output) {
120
0/1
✗ Branch 1 not taken.
✗ names.push_back(name);
121 }
122 ✗ return names;
123 ✗ });
124 2 }
125
126 2 void RegisterRenderer(nb::module_& module) {
127
1/1
✓ Branch 2 taken 2 times.
2 nb::class_<Overlays>(module, "Overlays")
128
1/2
✗ Branch 1 not taken.
✓ Branch 4 taken 2 times.
4 .def(nb::new_([](Ref<device::Device> device) { return Overlays::Create(*device); }),
129
1/1
✓ Branch 1 taken 2 times.
4 nb::arg("device"))
130 4 .def("set", &Overlays::Set, nb::arg("name"), nb::arg("overlay"),
131 2 nb::call_guard<nb::gil_scoped_release>())
132 2 .def("remove", &Overlays::Remove, nb::arg("name"), nb::call_guard<nb::gil_scoped_release>())
133 2 .def("names", &Overlays::Names)
134 2 .def(
135 "pick",
136 ✗ [](Overlays& overlays, std::string_view product, nb::handle pixel, float radius,
137 uint32_t layer) {
138 ✗ Float2 at;
139 ✗ Assign(at, pixel);
140 ✗ return overlays.Pick(product, at, radius, layer);
141 },
142 4 nb::arg("product"), nb::arg("pixel"), nb::kw_only(), nb::arg("radius") = 6.0f,
143
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
6 nb::arg("layer") = 0);
144
145
1/1
✓ Branch 2 taken 2 times.
2 nb::class_<Renderer>(module, "Renderer")
146 2 .def(
147 "__init__",
148 13 [](Renderer* renderer, Ref<Scene> scene, uint64_t maxPixelsPerLaunch,
149 std::optional<Ref<Overlays>> overlays, std::string_view streaming) {
150
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
13 if (streaming != "blocking" && streaming != "progressive") {
151
1/1
✓ Branch 2 taken 1 time.
1 throw nb::value_error("streaming is \"blocking\" or \"progressive\"");
152 }
153 12 nb::gil_scoped_release release;
154
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 new (renderer)
155 Renderer(std::move(scene),
156 {.maxPixelsPerLaunch = maxPixelsPerLaunch,
157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 .overlays = overlays ? *overlays : Ref<Overlays>(),
158
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 11 times.
12 .streaming = streaming == "blocking" ? Streaming::Blocking
159
2/5
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
24 : Streaming::Progressive});
160
4/5
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 11 times.
✓ Branch 6 taken 12 times.
25 },
161 2 nb::arg("scene"), nb::kw_only(),
162
2/5
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
6 nb::arg("max_pixels_per_launch") = RendererDesc{}.maxPixelsPerLaunch,
163
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
6 nb::arg("overlays") = nb::none(), nb::arg("streaming") = "blocking")
164 2 .def(
165 "step",
166 ✗ [](Renderer& renderer, const std::string& product, std::optional<uintptr_t> stream) {
167 ✗ return renderer.Step(product, ToStream(stream));
168 },
169 4 nb::arg("product"), nb::kw_only(), nb::arg("stream") = nb::none(),
170 nb::call_guard<nb::gil_scoped_release>())
171 2 .def(
172 "step",
173 143 [](Renderer& renderer, const std::vector<std::string>& products,
174 std::optional<uintptr_t> stream) {
175 143 return renderer.Step(products, ToStream(stream));
176 },
177 4 nb::arg("products"), nb::kw_only(), nb::arg("stream") = nb::none(),
178 nb::call_guard<nb::gil_scoped_release>())
179 4 .def_prop_ro("overlays", &Renderer::GetOverlays);
180 2 }
181
182 struct OverlaySources {
183 std::optional<Ref<device::Device>> device;
184 std::optional<uintptr_t> stream;
185 std::shared_ptr<std::vector<std::shared_ptr<void>>> held =
186 std::make_shared<std::vector<std::shared_ptr<void>>>();
187
188 ✗ device::Tensor Take(nb::handle value, const char* label) {
189
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (value.is_none()) {
190 ✗ return {};
191 }
192
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if (nb::isinstance<device::Tensor>(value)) {
193 ✗ return nb::cast<device::Tensor>(value);
194 }
195
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!device) {
196 ✗ throw nb::value_error(
197 "Overlay arrays that aren't chrono_batch_render tensors need a device to read "
198
0/1
✗ Branch 2 not taken.
✗ "them on; pass device=, such as the scene's");
199 }
200 ✗ const TakenTensor taken = TakeTensor(value, stream);
201
0/3
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ held->push_back(Hold(taken.managed));
202
0/1
✗ Branch 2 not taken.
✗ return (*device)->Import(taken.managed->dl_tensor, taken.producer, label);
203 }
204 };
205
206 ✗ Float3 Point(nb::handle value) {
207 ✗ Float3 point;
208 ✗ Assign(point, value);
209 ✗ return point;
210 }
211
212 ✗ Quatd Orientation(nb::handle value) {
213 ✗ Quatd orientation;
214
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if (!value.is_none()) {
215 ✗ Assign(orientation, value);
216 }
217 ✗ return orientation;
218 }
219
220 ✗ OverlayStyle Style(std::optional<OverlayStyle> style) {
221
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ return style.value_or(OverlayStyle{});
222 }
223
224 2 void RegisterOverlays(nb::module_& module) {
225 4 nb::enum_<OverlayDepth>(module, "OverlayDepth")
226 2 .value("TEST", OverlayDepth::Test)
227 2 .value("XRAY", OverlayDepth::XRay)
228 2 .value("ALWAYS", OverlayDepth::Always);
229
230 4 nb::enum_<MarkerShape>(module, "MarkerShape")
231 2 .value("CIRCLE", MarkerShape::Circle)
232 2 .value("RING", MarkerShape::Ring)
233 2 .value("SQUARE", MarkerShape::Square)
234 2 .value("DIAMOND", MarkerShape::Diamond)
235 2 .value("CROSS", MarkerShape::Cross)
236 2 .value("PLUS", MarkerShape::Plus);
237
238 4 nb::enum_<OverlayUnits>(module, "OverlayUnits")
239 2 .value("PIXELS", OverlayUnits::Pixels)
240 2 .value("METERS", OverlayUnits::Meters);
241
242 4 nb::enum_<OverlayPrimShape>(module, "OverlayPrimShape")
243 2 .value("FRAMES", OverlayPrimShape::Frames)
244 2 .value("BOUNDS", OverlayPrimShape::Bounds)
245 2 .value("FRUSTUMS", OverlayPrimShape::Frustums)
246 2 .value("LIGHTS", OverlayPrimShape::Lights)
247 2 .value("CAMERA_IMAGES", OverlayPrimShape::CameraImages);
248
249 4 nb::class_<OverlayStyle>(module, "OverlayStyle")
250 2 .def(
251 "__init__",
252 ✗ [](OverlayStyle* style, nb::handle color, float width, float size, MarkerShape marker,
253 OverlayDepth depth, std::optional<uint32_t> world, std::optional<uint32_t> anchor,
254 nb::handle background, nb::handle halo, nb::handle align, float dash, float gap,
255 OverlayUnits units, bool screenSized, bool facing, bool pickable, uint32_t id,
256 nb::handle screen) {
257 ✗ new (style) OverlayStyle{};
258
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!screen.is_none()) {
259 ✗ Float2 at;
260 ✗ Assign(at, screen);
261
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ style->screen = at;
262 }
263
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!color.is_none()) {
264 ✗ Assign(style->color, color);
265 }
266
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!background.is_none()) {
267 ✗ Assign(style->background, background);
268 }
269
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!halo.is_none()) {
270 ✗ Assign(style->halo, halo);
271 }
272
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!align.is_none()) {
273 ✗ Assign(style->align, align);
274 }
275 ✗ style->dash = dash;
276 ✗ style->gap = gap;
277 ✗ style->units = units;
278 ✗ style->screenSized = screenSized;
279 ✗ style->facing = facing;
280 ✗ style->pickable = pickable;
281 ✗ style->id = id;
282 ✗ style->width = width;
283 ✗ style->size = size;
284 ✗ style->marker = marker;
285 ✗ style->depth = depth;
286
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ style->world = world.value_or(kGlobalWorld);
287
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ style->anchor = anchor.value_or(kNoPrim);
288 ✗ },
289 4 nb::kw_only(), nb::arg("color") = nb::none(), nb::arg("width") = OverlayStyle{}.width,
290
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
6 nb::arg("size") = OverlayStyle{}.size, nb::arg("marker") = MarkerShape::Circle,
291
2/2
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
6 nb::arg("depth") = OverlayDepth::Test, nb::arg("world") = nb::none(),
292 4 nb::arg("anchor") = nb::none(), nb::arg("background") = nb::none(),
293 4 nb::arg("halo") = nb::none(), nb::arg("align") = nb::none(), nb::arg("dash") = 0.0f,
294
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
6 nb::arg("gap") = 0.0f, nb::arg("units") = OverlayUnits::Pixels,
295
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
8 nb::arg("screen_sized") = false, nb::arg("facing") = false, nb::arg("pickable") = true,
296
2/2
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
6 nb::arg("id") = 0, nb::arg("screen") = nb::none())
297 2 .def_prop_ro("color",
298 ✗ [](const OverlayStyle& style) {
299 ✗ return nb::make_tuple(style.color.x, style.color.y, style.color.z,
300 ✗ style.color.w);
301 })
302 2 .def_rw("width", &OverlayStyle::width)
303 2 .def_rw("size", &OverlayStyle::size)
304 2 .def_rw("marker", &OverlayStyle::marker)
305 2 .def_rw("depth", &OverlayStyle::depth)
306 2 .def_rw("world", &OverlayStyle::world)
307 2 .def_rw("anchor", &OverlayStyle::anchor)
308 2 .def_rw("dash", &OverlayStyle::dash)
309 2 .def_rw("gap", &OverlayStyle::gap)
310 2 .def_rw("units", &OverlayStyle::units)
311 2 .def_rw("screen_sized", &OverlayStyle::screenSized)
312 2 .def_rw("facing", &OverlayStyle::facing)
313 2 .def_rw("pickable", &OverlayStyle::pickable)
314 2 .def_rw("id", &OverlayStyle::id)
315 2 .def_prop_ro("background",
316 ✗ [](const OverlayStyle& style) {
317 ✗ return nb::make_tuple(style.background.x, style.background.y,
318 ✗ style.background.z, style.background.w);
319 })
320 2 .def_prop_ro("halo",
321 ✗ [](const OverlayStyle& style) {
322 ✗ return nb::make_tuple(style.halo.x, style.halo.y, style.halo.z,
323 ✗ style.halo.w);
324 })
325 2 .def_prop_ro(
326 "align",
327 ✗ [](const OverlayStyle& style) { return nb::make_tuple(style.align.x, style.align.y); })
328 2 .def_prop_ro("screen", [](const OverlayStyle& style) -> nb::object {
329
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!style.screen) {
330 ✗ return nb::none();
331 }
332 ✗ return nb::make_tuple(style.screen->x, style.screen->y);
333 });
334
335 4 nb::class_<OverlayHit>(module, "OverlayHit")
336 2 .def_ro("overlay", &OverlayHit::overlay)
337 2 .def_ro("id", &OverlayHit::id)
338 2 .def_ro("batch", &OverlayHit::batch)
339 2 .def_ro("item", &OverlayHit::item)
340 2 .def_ro("piece", &OverlayHit::piece)
341 2 .def_ro("world", &OverlayHit::world)
342 2 .def_ro("prim", &OverlayHit::prim)
343 2 .def_ro("distance", &OverlayHit::distance)
344 2 .def_ro("depth", &OverlayHit::depth)
345 2 .def_prop_ro("position", [](const OverlayHit& hit) {
346 ✗ return nb::make_tuple(hit.position.x, hit.position.y, hit.position.z);
347 });
348
349 4 nb::class_<OverlayRay>(module, "OverlayRay")
350 2 .def_prop_ro("origin",
351 ✗ [](const OverlayRay& ray) {
352 ✗ return nb::make_tuple(ray.origin.x, ray.origin.y, ray.origin.z);
353 })
354 2 .def_prop_ro("direction", [](const OverlayRay& ray) {
355 ✗ return nb::make_tuple(ray.direction.x, ray.direction.y, ray.direction.z);
356 });
357
358 2 nb::class_<OverlayPick>(module, "OverlayPick")
359 2 .def_prop_ro("ready", &OverlayPick::Ready)
360 2 .def("wait", &OverlayPick::Wait, nb::call_guard<nb::gil_scoped_release>())
361 2 .def_prop_ro("hit", &OverlayPick::Hit, nb::call_guard<nb::gil_scoped_release>())
362 4 .def_prop_ro("ray", &OverlayPick::Ray, nb::call_guard<nb::gil_scoped_release>());
363
364 4 nb::class_<ScreenPlacement>(module, "ScreenPlacement")
365 2 .def(
366 "__init__",
367 ✗ [](ScreenPlacement* placement, nb::handle at, nb::handle offset, nb::handle align) {
368
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ new (placement) ScreenPlacement{};
369
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!at.is_none()) {
370 ✗ Assign(placement->at, at);
371 }
372
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!offset.is_none()) {
373 ✗ Assign(placement->offset, offset);
374 }
375
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!align.is_none()) {
376 ✗ Float2 value;
377 ✗ Assign(value, align);
378
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ placement->align = value;
379 }
380 ✗ },
381 4 nb::arg("at") = nb::none(), nb::kw_only(), nb::arg("offset") = nb::none(),
382 4 nb::arg("align") = nb::none())
383 2 .def_prop_ro("alignment", [](const ScreenPlacement& placement) {
384 ✗ const Float2 align = placement.Alignment();
385 ✗ return nb::make_tuple(align.x, align.y);
386 });
387
388 2 nb::class_<OverlayNumberFormat>(module, "OverlayNumberFormat")
389 2 .def(
390 "__init__",
391 ✗ [](OverlayNumberFormat* format, std::string prefix, std::string suffix,
392 uint32_t decimals) {
393 ✗ new (format) OverlayNumberFormat{std::move(prefix), std::move(suffix), decimals};
394 },
395
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
8 nb::kw_only(), nb::arg("prefix") = "", nb::arg("suffix") = "",
396
2/2
✓ Branch 3 taken 2 times.
✓ Branch 6 taken 2 times.
6 nb::arg("decimals") = OverlayNumberFormat{}.decimals)
397 2 .def_rw("prefix", &OverlayNumberFormat::prefix)
398 2 .def_rw("suffix", &OverlayNumberFormat::suffix)
399 2 .def_rw("decimals", &OverlayNumberFormat::decimals);
400
401 2 nb::
402 2 class_<Overlay>(module, "Overlay")
403 2 .def(nb::init<>())
404 2 .def(
405 "line",
406 ✗ [](Overlay& overlay, nb::handle a, nb::handle b,
407 std::optional<OverlayStyle> style) {
408 ✗ Float3 from;
409 ✗ Float3 to;
410 ✗ Assign(from, a);
411 ✗ Assign(to, b);
412 ✗ overlay.Line(from, to, Style(style));
413 ✗ },
414 4 nb::arg("a"), nb::arg("b"), nb::arg("style") = nb::none())
415 2 .def(
416 "lines",
417 ✗ [](Overlay& overlay, nb::handle endpoints, std::optional<OverlayStyle> style,
418 nb::handle colors) {
419 ✗ std::vector<Float3> points;
420 ✗ std::vector<Float4> tints;
421
0/1
✗ Branch 1 not taken.
✗ Assign(points, endpoints);
422
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!colors.is_none()) {
423
0/1
✗ Branch 1 not taken.
✗ Assign(tints, colors);
424 }
425
0/1
✗ Branch 3 not taken.
✗ overlay.Lines(points, Style(style), tints);
426 ✗ },
427 4 nb::arg("endpoints"), nb::arg("style") = nb::none(), nb::arg("colors") = nb::none())
428 2 .def(
429 "polyline",
430 ✗ [](Overlay& overlay, nb::handle points, bool closed,
431 std::optional<OverlayStyle> style) {
432 ✗ std::vector<Float3> values;
433
0/1
✗ Branch 1 not taken.
✗ Assign(values, points);
434
0/1
✗ Branch 2 not taken.
✗ overlay.Polyline(values, closed, Style(style));
435 ✗ },
436
1/1
✓ Branch 2 taken 2 times.
6 nb::arg("points"), nb::arg("closed") = false, nb::arg("style") = nb::none())
437 2 .def(
438 "marker",
439 ✗ [](Overlay& overlay, nb::handle point, std::optional<OverlayStyle> style) {
440 ✗ Float3 value;
441 ✗ Assign(value, point);
442 ✗ overlay.Marker(value, Style(style));
443 ✗ },
444 4 nb::arg("point"), nb::arg("style") = nb::none())
445 2 .def(
446 "markers",
447 ✗ [](Overlay& overlay, nb::handle points, std::optional<OverlayStyle> style,
448 nb::handle colors) {
449 ✗ std::vector<Float3> values;
450 ✗ std::vector<Float4> tints;
451
0/1
✗ Branch 1 not taken.
✗ Assign(values, points);
452
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!colors.is_none()) {
453
0/1
✗ Branch 1 not taken.
✗ Assign(tints, colors);
454 }
455
0/1
✗ Branch 3 not taken.
✗ overlay.Markers(values, Style(style), tints);
456 ✗ },
457 4 nb::arg("points"), nb::arg("style") = nb::none(), nb::arg("colors") = nb::none())
458 2 .def(
459 "add_lines",
460 ✗ [](Overlay& overlay, nb::handle points, nb::handle ends, nb::handle colors,
461 nb::handle widths, nb::handle worlds, nb::handle itemCount,
462 std::optional<OverlayStyle> style, std::optional<Ref<device::Device>> device,
463 std::optional<uintptr_t> stream) {
464
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
465 ✗ OverlayLines lines{.points = sources.Take(points, "overlay points"),
466 .ends = sources.Take(ends, "overlay ends"),
467 .colors = sources.Take(colors, "overlay colors"),
468 .widths = sources.Take(widths, "overlay widths"),
469 .worlds = sources.Take(worlds, "overlay worlds"),
470 .itemCount = sources.Take(itemCount, "overlay item counts"),
471
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
472
0/6
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ .owner = sources.held};
473
0/1
✗ Branch 2 not taken.
✗ overlay.Add(std::move(lines));
474 ✗ },
475 4 nb::arg("points"), nb::arg("ends"), nb::kw_only(), nb::arg("colors") = nb::none(),
476 4 nb::arg("widths") = nb::none(), nb::arg("worlds") = nb::none(),
477 4 nb::arg("item_count") = nb::none(), nb::arg("style") = nb::none(),
478 4 nb::arg("device") = nb::none(), nb::arg("stream") = nb::none())
479 2 .def(
480 "add_markers",
481 ✗ [](Overlay& overlay, nb::handle points, nb::handle colors, nb::handle sizes,
482 nb::handle worlds, nb::handle itemCount, std::optional<OverlayStyle> style,
483 std::optional<Ref<device::Device>> device, std::optional<uintptr_t> stream) {
484
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
485 ✗ OverlayMarkers markers{.points = sources.Take(points, "overlay points"),
486 .colors = sources.Take(colors, "overlay colors"),
487 .sizes = sources.Take(sizes, "overlay sizes"),
488 .worlds = sources.Take(worlds, "overlay worlds"),
489 .itemCount =
490 sources.Take(itemCount, "overlay item counts"),
491
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
492
0/5
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ .owner = sources.held};
493
0/1
✗ Branch 2 not taken.
✗ overlay.Add(std::move(markers));
494 ✗ },
495 4 nb::arg("points"), nb::kw_only(), nb::arg("colors") = nb::none(),
496 4 nb::arg("sizes") = nb::none(), nb::arg("worlds") = nb::none(),
497 4 nb::arg("item_count") = nb::none(), nb::arg("style") = nb::none(),
498 4 nb::arg("device") = nb::none(), nb::arg("stream") = nb::none())
499
500 2 .def(
501 "box",
502 ✗ [](Overlay& overlay, nb::handle center, nb::handle halfExtents,
503 std::optional<OverlayStyle> style, nb::handle orientation) {
504 ✗ overlay.Box(Point(center), Point(halfExtents), Style(style),
505 Orientation(orientation));
506 ✗ },
507 4 nb::arg("center"), nb::arg("half_extents"), nb::arg("style") = nb::none(),
508 4 nb::arg("orientation") = nb::none())
509 2 .def(
510 "sphere",
511 ✗ [](Overlay& overlay, nb::handle center, float radius,
512 std::optional<OverlayStyle> style) {
513 ✗ overlay.Sphere(Point(center), radius, Style(style));
514 ✗ },
515 4 nb::arg("center"), nb::arg("radius"), nb::arg("style") = nb::none())
516 2 .def(
517 "circle",
518 ✗ [](Overlay& overlay, nb::handle center, nb::handle normal, float radius,
519 std::optional<OverlayStyle> style) {
520 ✗ overlay.Circle(Point(center), Point(normal), radius, Style(style));
521 ✗ },
522 2 nb::arg("center"), nb::arg("normal"), nb::arg("radius"),
523 4 nb::arg("style") = nb::none())
524 2 .def(
525 "frame",
526 ✗ [](Overlay& overlay, nb::handle origin, nb::handle orientation, float length,
527 std::optional<OverlayStyle> style) {
528 ✗ overlay.Frame(Point(origin), Orientation(orientation), length, Style(style));
529 ✗ },
530 4 nb::arg("origin"), nb::arg("orientation") = nb::none(), nb::arg("length") = 1.0f,
531
1/1
✓ Branch 2 taken 2 times.
4 nb::arg("style") = nb::none())
532 2 .def(
533 "arrow",
534 ✗ [](Overlay& overlay, nb::handle from, nb::handle to,
535 std::optional<OverlayStyle> style, float headLength) {
536 ✗ overlay.Arrow(Point(from), Point(to), Style(style), headLength);
537 ✗ },
538 4 nb::arg("start"), nb::arg("end"), nb::arg("style") = nb::none(),
539
1/1
✓ Branch 1 taken 2 times.
6 nb::arg("head_length") = 0.0f)
540 2 .def(
541 "capsule",
542 ✗ [](Overlay& overlay, nb::handle center, float radius, float halfHeight,
543 nb::handle orientation, std::optional<OverlayStyle> style) {
544 ✗ overlay.Capsule(Point(center), Orientation(orientation), radius, halfHeight,
545 ✗ Style(style));
546 ✗ },
547 2 nb::arg("center"), nb::arg("radius"), nb::arg("half_height"),
548 4 nb::arg("orientation") = nb::none(), nb::arg("style") = nb::none())
549 2 .def(
550 "add_boxes",
551 ✗ [](Overlay& overlay, nb::handle points, nb::handle halfExtents,
552 nb::handle orientations, nb::handle colors, nb::handle widths, nb::handle worlds,
553 nb::handle itemCount, std::optional<OverlayStyle> style,
554 std::optional<Ref<device::Device>> device, std::optional<uintptr_t> stream) {
555
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
556
0/20
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ overlay.Add(OverlayBoxes{
557 .points = sources.Take(points, "overlay points"),
558 .halfExtents = sources.Take(halfExtents, "overlay half extents"),
559 .orientations = sources.Take(orientations, "overlay orientations"),
560 .colors = sources.Take(colors, "overlay colors"),
561 .widths = sources.Take(widths, "overlay widths"),
562 .worlds = sources.Take(worlds, "overlay worlds"),
563 .itemCount = sources.Take(itemCount, "overlay item counts"),
564
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
565 .owner = sources.held});
566 ✗ },
567 2 nb::arg("points"), nb::arg("half_extents"), nb::kw_only(),
568 4 nb::arg("orientations") = nb::none(), nb::arg("colors") = nb::none(),
569 4 nb::arg("widths") = nb::none(), nb::arg("worlds") = nb::none(),
570 4 nb::arg("item_count") = nb::none(), nb::arg("style") = nb::none(),
571 4 nb::arg("device") = nb::none(), nb::arg("stream") = nb::none())
572 2 .def(
573 "add_spheres",
574 ✗ [](Overlay& overlay, nb::handle points, nb::handle radii, nb::handle colors,
575 nb::handle widths, nb::handle worlds, nb::handle itemCount,
576 std::optional<OverlayStyle> style, std::optional<Ref<device::Device>> device,
577 std::optional<uintptr_t> stream) {
578
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
579 ✗ overlay.Add(
580
0/17
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ OverlaySpheres{.points = sources.Take(points, "overlay points"),
581 .radii = sources.Take(radii, "overlay radii"),
582 .colors = sources.Take(colors, "overlay colors"),
583 .widths = sources.Take(widths, "overlay widths"),
584 .worlds = sources.Take(worlds, "overlay worlds"),
585 .itemCount = sources.Take(itemCount, "overlay item counts"),
586
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
587 .owner = sources.held});
588 ✗ },
589 4 nb::arg("points"), nb::arg("radii"), nb::kw_only(), nb::arg("colors") = nb::none(),
590 4 nb::arg("widths") = nb::none(), nb::arg("worlds") = nb::none(),
591 4 nb::arg("item_count") = nb::none(), nb::arg("style") = nb::none(),
592 4 nb::arg("device") = nb::none(), nb::arg("stream") = nb::none())
593 2 .def(
594 "add_circles",
595 ✗ [](Overlay& overlay, nb::handle points, nb::handle normals, nb::handle radii,
596 nb::handle colors, nb::handle widths, nb::handle worlds, nb::handle itemCount,
597 std::optional<OverlayStyle> style, std::optional<Ref<device::Device>> device,
598 std::optional<uintptr_t> stream) {
599
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
600 ✗ overlay.Add(
601
0/20
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ OverlayCircles{.points = sources.Take(points, "overlay points"),
602 .normals = sources.Take(normals, "overlay normals"),
603 .radii = sources.Take(radii, "overlay radii"),
604 .colors = sources.Take(colors, "overlay colors"),
605 .widths = sources.Take(widths, "overlay widths"),
606 .worlds = sources.Take(worlds, "overlay worlds"),
607 .itemCount = sources.Take(itemCount, "overlay item counts"),
608
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
609 .owner = sources.held});
610 ✗ },
611 2 nb::arg("points"), nb::arg("normals"), nb::arg("radii"), nb::kw_only(),
612 4 nb::arg("colors") = nb::none(), nb::arg("widths") = nb::none(),
613 4 nb::arg("worlds") = nb::none(), nb::arg("item_count") = nb::none(),
614 4 nb::arg("style") = nb::none(), nb::arg("device") = nb::none(),
615 4 nb::arg("stream") = nb::none())
616 2 .def(
617 "add_frames",
618 ✗ [](Overlay& overlay, nb::handle points, nb::handle orientations, nb::handle lengths,
619 float length, nb::handle widths, nb::handle worlds, nb::handle itemCount,
620 std::optional<OverlayStyle> style, std::optional<Ref<device::Device>> device,
621 std::optional<uintptr_t> stream) {
622
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
623
0/17
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ overlay.Add(OverlayFrames{
624 .points = sources.Take(points, "overlay points"),
625 .orientations = sources.Take(orientations, "overlay orientations"),
626 .lengths = sources.Take(lengths, "overlay lengths"),
627 .widths = sources.Take(widths, "overlay widths"),
628 .worlds = sources.Take(worlds, "overlay worlds"),
629 .itemCount = sources.Take(itemCount, "overlay item counts"),
630 .length = length,
631
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
632 .owner = sources.held});
633 ✗ },
634 4 nb::arg("points"), nb::kw_only(), nb::arg("orientations") = nb::none(),
635 4 nb::arg("lengths") = nb::none(), nb::arg("length") = 1.0f,
636
1/1
✓ Branch 4 taken 2 times.
4 nb::arg("widths") = nb::none(), nb::arg("worlds") = nb::none(),
637 4 nb::arg("item_count") = nb::none(), nb::arg("style") = nb::none(),
638 4 nb::arg("device") = nb::none(), nb::arg("stream") = nb::none())
639 2 .def(
640 "add_arrows",
641 ✗ [](Overlay& overlay, nb::handle points, nb::handle ends, nb::handle vectors,
642 float vectorScale, nb::handle headLengths, float headLength, nb::handle colors,
643 nb::handle widths, nb::handle worlds, nb::handle itemCount,
644 std::optional<OverlayStyle> style, std::optional<Ref<device::Device>> device,
645 std::optional<uintptr_t> stream) {
646
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
647
0/23
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 25 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ overlay.Add(OverlayArrows{
648 .points = sources.Take(points, "overlay points"),
649 .ends = sources.Take(ends, "overlay ends"),
650 .vectors = sources.Take(vectors, "overlay vectors"),
651 .vectorScale = vectorScale,
652 .headLengths = sources.Take(headLengths, "overlay head lengths"),
653 .colors = sources.Take(colors, "overlay colors"),
654 .widths = sources.Take(widths, "overlay widths"),
655 .worlds = sources.Take(worlds, "overlay worlds"),
656 .itemCount = sources.Take(itemCount, "overlay item counts"),
657 .headLength = headLength,
658
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
659 .owner = sources.held});
660 ✗ },
661 4 nb::arg("points"), nb::arg("ends") = nb::none(), nb::kw_only(),
662 4 nb::arg("vectors") = nb::none(), nb::arg("vector_scale") = 1.0f,
663
1/1
✓ Branch 3 taken 2 times.
4 nb::arg("head_lengths") = nb::none(), nb::arg("head_length") = 0.0f,
664
1/1
✓ Branch 4 taken 2 times.
4 nb::arg("colors") = nb::none(), nb::arg("widths") = nb::none(),
665 4 nb::arg("worlds") = nb::none(), nb::arg("item_count") = nb::none(),
666 4 nb::arg("style") = nb::none(), nb::arg("device") = nb::none(),
667 4 nb::arg("stream") = nb::none())
668 2 .def(
669 "add_capsules",
670 ✗ [](Overlay& overlay, nb::handle points, nb::handle radii, nb::handle halfHeights,
671 nb::handle orientations, nb::handle colors, nb::handle widths, nb::handle worlds,
672 nb::handle itemCount, std::optional<OverlayStyle> style,
673 std::optional<Ref<device::Device>> device, std::optional<uintptr_t> stream) {
674
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
675
0/23
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 25 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ overlay.Add(OverlayCapsules{
676 .points = sources.Take(points, "overlay points"),
677 .orientations = sources.Take(orientations, "overlay orientations"),
678 .radii = sources.Take(radii, "overlay radii"),
679 .halfHeights = sources.Take(halfHeights, "overlay half heights"),
680 .colors = sources.Take(colors, "overlay colors"),
681 .widths = sources.Take(widths, "overlay widths"),
682 .worlds = sources.Take(worlds, "overlay worlds"),
683 .itemCount = sources.Take(itemCount, "overlay item counts"),
684
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
685 .owner = sources.held});
686 ✗ },
687 2 nb::arg("points"), nb::arg("radii"), nb::arg("half_heights"), nb::kw_only(),
688 4 nb::arg("orientations") = nb::none(), nb::arg("colors") = nb::none(),
689 4 nb::arg("widths") = nb::none(), nb::arg("worlds") = nb::none(),
690 4 nb::arg("item_count") = nb::none(), nb::arg("style") = nb::none(),
691 4 nb::arg("device") = nb::none(), nb::arg("stream") = nb::none())
692 2 .def(
693 "add_trails",
694 ✗ [](Overlay& overlay, nb::handle points, nb::handle heads, float fade,
695 nb::handle colors, nb::handle widths, nb::handle worlds, nb::handle itemCount,
696 std::optional<OverlayStyle> style, std::optional<Ref<device::Device>> device,
697 std::optional<uintptr_t> stream) {
698
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
699 ✗ overlay.Add(
700
0/17
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ OverlayTrails{.points = sources.Take(points, "overlay points"),
701 .heads = sources.Take(heads, "overlay heads"),
702 .colors = sources.Take(colors, "overlay colors"),
703 .widths = sources.Take(widths, "overlay widths"),
704 .worlds = sources.Take(worlds, "overlay worlds"),
705 .itemCount = sources.Take(itemCount, "overlay item counts"),
706 .fade = fade,
707
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
708 .owner = sources.held});
709 ✗ },
710 4 nb::arg("points"), nb::kw_only(), nb::arg("heads") = nb::none(),
711
1/1
✓ Branch 2 taken 2 times.
6 nb::arg("fade") = 0.0f, nb::arg("colors") = nb::none(),
712 4 nb::arg("widths") = nb::none(), nb::arg("worlds") = nb::none(),
713 4 nb::arg("item_count") = nb::none(), nb::arg("style") = nb::none(),
714 4 nb::arg("device") = nb::none(), nb::arg("stream") = nb::none())
715 2 .def(
716 "label",
717 ✗ [](Overlay& overlay, nb::handle point, std::string_view text,
718 std::optional<OverlayStyle> style) {
719 ✗ overlay.Label(Point(point), text, Style(style));
720 ✗ },
721 4 nb::arg("point"), nb::arg("text"), nb::arg("style") = nb::none())
722 2 .def(
723 "text",
724 ✗ [](Overlay& overlay, const ScreenPlacement& placement, std::string_view text,
725 std::optional<OverlayStyle> style) {
726 ✗ overlay.Text(placement, text, Style(style));
727 ✗ },
728 4 nb::arg("placement"), nb::arg("text"), nb::arg("style") = nb::none())
729 2 .def(
730 "add_texts",
731 ✗ [](Overlay& overlay, nb::handle chars, nb::handle points, nb::handle colors,
732 nb::handle sizes, nb::handle worlds, nb::handle itemCount,
733 std::optional<ScreenPlacement> placement, std::optional<OverlayStyle> style,
734 std::optional<Ref<device::Device>> device, std::optional<uintptr_t> stream) {
735
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
736 ✗ overlay.Add(
737
0/17
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ OverlayTexts{.points = sources.Take(points, "overlay points"),
738 .chars = sources.Take(chars, "overlay chars"),
739 .colors = sources.Take(colors, "overlay colors"),
740 .sizes = sources.Take(sizes, "overlay sizes"),
741 .worlds = sources.Take(worlds, "overlay worlds"),
742 .itemCount = sources.Take(itemCount, "overlay item counts"),
743 .placement = placement,
744
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
745 .owner = sources.held});
746 ✗ },
747 4 nb::arg("chars"), nb::kw_only(), nb::arg("points") = nb::none(),
748 4 nb::arg("colors") = nb::none(), nb::arg("sizes") = nb::none(),
749 4 nb::arg("worlds") = nb::none(), nb::arg("item_count") = nb::none(),
750 4 nb::arg("placement") = nb::none(), nb::arg("style") = nb::none(),
751 4 nb::arg("device") = nb::none(), nb::arg("stream") = nb::none())
752 2 .def(
753 "add_numbers",
754 ✗ [](Overlay& overlay, nb::handle values, nb::handle points,
755 std::optional<OverlayNumberFormat> format, nb::handle colors, nb::handle sizes,
756 nb::handle worlds, nb::handle itemCount,
757 std::optional<ScreenPlacement> placement, std::optional<OverlayStyle> style,
758 std::optional<Ref<device::Device>> device, std::optional<uintptr_t> stream) {
759
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
760 ✗ overlay.Add(
761
0/17
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ OverlayNumbers{.points = sources.Take(points, "overlay points"),
762 .values = sources.Take(values, "overlay values"),
763
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ .format = format.value_or(OverlayNumberFormat{}),
764 .colors = sources.Take(colors, "overlay colors"),
765 .sizes = sources.Take(sizes, "overlay sizes"),
766 .worlds = sources.Take(worlds, "overlay worlds"),
767 .itemCount = sources.Take(itemCount, "overlay item counts"),
768 .placement = placement,
769
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
770 .owner = sources.held});
771 ✗ },
772 4 nb::arg("values"), nb::kw_only(), nb::arg("points") = nb::none(),
773 4 nb::arg("format") = nb::none(), nb::arg("colors") = nb::none(),
774 4 nb::arg("sizes") = nb::none(), nb::arg("worlds") = nb::none(),
775 4 nb::arg("item_count") = nb::none(), nb::arg("placement") = nb::none(),
776 4 nb::arg("style") = nb::none(), nb::arg("device") = nb::none(),
777 4 nb::arg("stream") = nb::none())
778 2 .def(
779 "triangle",
780 ✗ [](Overlay& overlay, nb::handle a, nb::handle b, nb::handle c,
781 std::optional<OverlayStyle> style) {
782 ✗ overlay.Triangle(Point(a), Point(b), Point(c), Style(style));
783 ✗ },
784 4 nb::arg("a"), nb::arg("b"), nb::arg("c"), nb::arg("style") = nb::none())
785 2 .def(
786 "polygon",
787 ✗ [](Overlay& overlay, nb::handle points, std::optional<OverlayStyle> style) {
788 ✗ std::vector<Float3> values;
789
0/1
✗ Branch 1 not taken.
✗ Assign(values, points);
790
0/1
✗ Branch 2 not taken.
✗ overlay.Polygon(values, Style(style));
791 ✗ },
792 4 nb::arg("points"), nb::arg("style") = nb::none())
793 2 .def(
794 "curve",
795 ✗ [](Overlay& overlay, nb::handle controls, std::optional<OverlayStyle> style,
796 uint32_t tessellation) {
797 ✗ std::vector<Float3> values;
798
0/1
✗ Branch 1 not taken.
✗ Assign(values, controls);
799
0/1
✗ Branch 2 not taken.
✗ overlay.Curve(values, Style(style), tessellation);
800 ✗ },
801
1/1
✓ Branch 1 taken 2 times.
6 nb::arg("controls"), nb::arg("style") = nb::none(), nb::arg("tessellation") = 16)
802 2 .def(
803 "arc",
804 ✗ [](Overlay& overlay, nb::handle center, nb::handle normal, float radius,
805 float begin, float end, std::optional<OverlayStyle> style, bool filled) {
806 ✗ overlay.Arc(Point(center), Point(normal), radius, begin, end, Style(style),
807 filled);
808 ✗ },
809 2 nb::arg("center"), nb::arg("normal"), nb::arg("radius"), nb::arg("begin"),
810 4 nb::arg("end"), nb::arg("style") = nb::none(), nb::kw_only(),
811
1/1
✓ Branch 1 taken 2 times.
6 nb::arg("filled") = false)
812 2 .def(
813 "rectangle",
814 ✗ [](Overlay& overlay, nb::handle center, nb::handle halfRight, nb::handle halfUp,
815 std::optional<OverlayStyle> style, bool filled) {
816 ✗ overlay.Rectangle(Point(center), Point(halfRight), Point(halfUp), Style(style),
817 filled);
818 ✗ },
819 2 nb::arg("center"), nb::arg("half_right"), nb::arg("half_up"),
820
1/1
✓ Branch 1 taken 2 times.
6 nb::arg("style") = nb::none(), nb::kw_only(), nb::arg("filled") = false)
821 2 .def(
822 "mesh",
823 ✗ [](Overlay& overlay, nb::handle points, std::vector<uint32_t> faceCounts,
824 std::vector<uint32_t> indices, std::optional<OverlayStyle> style,
825 bool wireframe) {
826 ✗ std::vector<Float3> values;
827
0/1
✗ Branch 1 not taken.
✗ Assign(values, points);
828
0/1
✗ Branch 4 not taken.
✗ overlay.Mesh(values, faceCounts, indices, Style(style), wireframe);
829 ✗ },
830 2 nb::arg("points"), nb::arg("face_counts"), nb::arg("indices"),
831
1/1
✓ Branch 1 taken 2 times.
6 nb::arg("style") = nb::none(), nb::kw_only(), nb::arg("wireframe") = false)
832 2 .def(
833 "image",
834 ✗ [](Overlay& overlay, nb::handle topLeft, nb::handle right, nb::handle down,
835 nb::handle pixels, std::optional<OverlayStyle> style,
836 std::optional<Ref<device::Device>> device, std::optional<uintptr_t> stream) {
837
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
838
0/4
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ overlay.Image(Point(topLeft), Point(right), Point(down),
839
0/1
✗ Branch 1 not taken.
✗ sources.Take(pixels, "overlay image"), Style(style));
840 ✗ },
841 2 nb::arg("top_left"), nb::arg("right"), nb::arg("down"), nb::arg("pixels"),
842 4 nb::kw_only(), nb::arg("style") = nb::none(), nb::arg("device") = nb::none(),
843 4 nb::arg("stream") = nb::none())
844 2 .def(
845 "prims",
846 ✗ [](Overlay& overlay, std::vector<uint32_t> prims, OverlayPrimShape shape,
847 std::optional<OverlayStyle> style, float length, std::vector<uint32_t> groups,
848 nb::handle colors) {
849 ✗ std::vector<Float4> tints;
850
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!colors.is_none()) {
851
0/1
✗ Branch 1 not taken.
✗ Assign(tints, colors);
852 }
853
0/1
✗ Branch 4 not taken.
✗ overlay.Prims(prims, shape, Style(style), length, groups, tints);
854 ✗ },
855 4 nb::arg("prims"), nb::arg("shape"), nb::kw_only(), nb::arg("style") = nb::none(),
856
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
8 nb::arg("length") = 1.0f, nb::arg("groups") = std::vector<uint32_t>(),
857 4 nb::arg("colors") = nb::none())
858 2 .def(
859 "outline",
860 ✗ [](Overlay& overlay, std::vector<uint32_t> prims,
861 ✗ std::optional<OverlayStyle> style) { overlay.Outline(prims, Style(style)); },
862 4 nb::arg("prims"), nb::arg("style") = nb::none())
863 2 .def(
864 "add_triangles",
865 ✗ [](Overlay& overlay, nb::handle points, nb::handle colors, nb::handle worlds,
866 nb::handle itemCount, std::optional<OverlayStyle> style,
867 std::optional<Ref<device::Device>> device, std::optional<uintptr_t> stream) {
868
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
869
0/11
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ overlay.Add(OverlayTriangles{.points = sources.Take(points, "overlay points"),
870 .colors = sources.Take(colors, "overlay colors"),
871 .worlds = sources.Take(worlds, "overlay worlds"),
872 .itemCount =
873 sources.Take(itemCount, "overlay item counts"),
874
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
875 .owner = sources.held});
876 ✗ },
877 4 nb::arg("points"), nb::kw_only(), nb::arg("colors") = nb::none(),
878 4 nb::arg("worlds") = nb::none(), nb::arg("item_count") = nb::none(),
879 4 nb::arg("style") = nb::none(), nb::arg("device") = nb::none(),
880 4 nb::arg("stream") = nb::none())
881 2 .def(
882 "add_images",
883 ✗ [](Overlay& overlay, nb::handle points, nb::handle rights, nb::handle downs,
884 nb::handle pixels, nb::handle colors, nb::handle worlds, nb::handle itemCount,
885 std::optional<OverlayStyle> style, std::optional<Ref<device::Device>> device,
886 std::optional<uintptr_t> stream) {
887
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
888 ✗ overlay.Add(
889
0/20
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ OverlayImages{.points = sources.Take(points, "overlay points"),
890 .rights = sources.Take(rights, "overlay rights"),
891 .downs = sources.Take(downs, "overlay downs"),
892 .pixels = sources.Take(pixels, "overlay images"),
893 .colors = sources.Take(colors, "overlay colors"),
894 .worlds = sources.Take(worlds, "overlay worlds"),
895 .itemCount = sources.Take(itemCount, "overlay item counts"),
896
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
897 .owner = sources.held});
898 ✗ },
899 2 nb::arg("points"), nb::arg("rights"), nb::arg("downs"), nb::arg("pixels"),
900 4 nb::kw_only(), nb::arg("colors") = nb::none(), nb::arg("worlds") = nb::none(),
901 4 nb::arg("item_count") = nb::none(), nb::arg("style") = nb::none(),
902 4 nb::arg("device") = nb::none(), nb::arg("stream") = nb::none())
903 2 .def(
904 "add_prims",
905 ✗ [](Overlay& overlay, nb::handle prims, OverlayPrimShape shape, nb::handle groups,
906 uint32_t groupCount, float length, nb::handle pixels, nb::handle colors,
907 nb::handle widths, std::optional<OverlayStyle> style,
908 std::optional<Ref<device::Device>> device, std::optional<uintptr_t> stream) {
909
0/1
✗ Branch 2 not taken.
✗ OverlaySources sources{device, stream};
910
0/14
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ overlay.Add(OverlayPrims{.prims = sources.Take(prims, "overlay prims"),
911 .groups = sources.Take(groups, "overlay groups"),
912 .groupCount = groupCount,
913 .shape = shape,
914 .length = length,
915 .pixels = sources.Take(pixels, "overlay images"),
916 .colors = sources.Take(colors, "overlay colors"),
917 .widths = sources.Take(widths, "overlay widths"),
918
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ .style = Style(style),
919 .owner = sources.held});
920 ✗ },
921 4 nb::arg("prims"), nb::arg("shape"), nb::kw_only(), nb::arg("groups") = nb::none(),
922
1/1
✓ Branch 1 taken 2 times.
6 nb::arg("group_count") = 0, nb::arg("length") = 1.0f,
923
1/1
✓ Branch 4 taken 2 times.
4 nb::arg("pixels") = nb::none(), nb::arg("colors") = nb::none(),
924 4 nb::arg("widths") = nb::none(), nb::arg("style") = nb::none(),
925 4 nb::arg("device") = nb::none(), nb::arg("stream") = nb::none())
926 4 .def_prop_ro("empty", &Overlay::Empty);
927 2 }
928
929 2 void RegisterRayCaster(nb::module_& module) {
930 2 nb::enum_<RayCastMode>(module, "RayCastMode")
931 2 .value("CLOSEST", RayCastMode::Closest)
932 2 .value("ANY", RayCastMode::Any);
933
934 2 nb::enum_<RayOutput>(module, "RayOutput", nb::is_flag())
935 2 .value("DISTANCE", RayOutput::Distance)
936 2 .value("PRIM", RayOutput::Prim)
937 2 .value("NORMAL", RayOutput::Normal)
938 2 .value("POSITION", RayOutput::Position);
939
940 2 nb::class_<RayHits>(module, "RayHits")
941 4 .def(nb::init<>())
942 2 .def_ro("hit", &RayHits::hit)
943 2 .def_ro("distance", &RayHits::distance)
944 2 .def_ro("prim", &RayHits::prim)
945 2 .def_ro("normal", &RayHits::normal)
946 2 .def_ro("position", &RayHits::position)
947 2 .def_ro("done", &RayHits::done);
948
949 2 nb::class_<RayCaster>(module, "RayCaster")
950
1/1
✓ Branch 1 taken 2 times.
2 .def(
951 "__init__",
952 8 [](RayCaster* caster, Ref<Scene> scene) {
953 8 nb::gil_scoped_release release;
954
2/3
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 new (caster) RayCaster(std::move(scene));
955 8 },
956 2 nb::arg("scene"))
957 2 .def(
958 "cast",
959 9 [](RayCaster& caster, nb::handle origins, nb::handle directions, nb::handle tMins,
960 nb::handle tMaxes, nb::handle worlds, float minDistance, float maxDistance,
961 std::optional<uint32_t> world, RayCastMode mode, RayOutput outputs,
962 std::string label, std::optional<RayHits> kept, std::optional<uintptr_t> stream) {
963 9 device::Device& device = caster.GetScene().GetDevice();
964 9 HeldTensors held;
965 54 const auto input = [&](nb::handle value, const char* what) {
966
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 19 times.
45 if (value.is_none()) {
967 26 return device::Tensor();
968 }
969
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
19 if (nb::isinstance<device::Tensor>(value)) {
970 ✗ return nb::cast<device::Tensor>(value);
971 }
972
1/1
✓ Branch 2 taken 19 times.
38 return held.Import(device, value, stream, what);
973 9 };
974
1/1
✓ Branch 1 taken 9 times.
9 Rays rays;
975
1/1
✓ Branch 1 taken 9 times.
9 rays.origins = input(origins, "ray origins");
976
1/1
✓ Branch 1 taken 9 times.
9 rays.directions = input(directions, "ray directions");
977
1/1
✓ Branch 1 taken 9 times.
9 rays.tMins = input(tMins, "ray minimum distances");
978
1/1
✓ Branch 1 taken 9 times.
9 rays.tMaxes = input(tMaxes, "ray maximum distances");
979
1/1
✓ Branch 1 taken 9 times.
9 rays.worlds = input(worlds, "ray worlds");
980 9 rays.minDistance = minDistance;
981 9 rays.maxDistance = maxDistance;
982
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 rays.world = world.value_or(kGlobalWorld);
983
7/18
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✓ Branch 4 taken 9 times.
✓ Branch 7 taken 9 times.
✓ Branch 10 taken 9 times.
✓ Branch 13 taken 9 times.
✓ Branch 16 taken 9 times.
✓ Branch 19 taken 9 times.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
9 RayHits hits = kept ? std::move(*kept) : RayHits{};
984 9 nb::gil_scoped_release release;
985
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✓ Branch 3 taken 8 times.
✓ Branch 6 taken 8 times.
17 held.Retire(caster.GetScene(),
986
1/1
✓ Branch 1 taken 8 times.
19 caster.Cast(rays, hits,
987 {.mode = mode, .outputs = outputs, .label = label},
988
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 stream ? ToStream(stream) : Stream::Host()));
989 8 return hits;
990
2/3
✓ Branch 1 taken 9 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
19 },
991 4 nb::arg("origins"), nb::arg("directions"), nb::kw_only(), nb::arg("t_min") = nb::none(),
992 4 nb::arg("t_max") = nb::none(), nb::arg("worlds") = nb::none(),
993
5/13
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
✓ Branch 13 taken 2 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
6 nb::arg("min_distance") = Rays{}.minDistance,
994
7/15
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
✓ Branch 13 taken 2 times.
✓ Branch 16 taken 2 times.
✓ Branch 19 taken 2 times.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
6 nb::arg("max_distance") = Rays{}.maxDistance, nb::arg("world") = nb::none(),
995
1/1
✓ Branch 1 taken 2 times.
4 nb::arg("mode") = RayCastMode::Closest,
996
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
8 nb::arg("outputs").sig("RayOutput.Distance | RayOutput.Prim") = RayCastDesc{}.outputs,
997
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
8 nb::arg("label") = RayCastDesc{}.label, nb::arg("hits") = nb::none(),
998
1/1
✓ Branch 1 taken 2 times.
4 nb::arg("stream") = nb::none());
999 2 }
1000
1001 }
1002
1003 2 void BindRender(nb::module_& module) {
1004 2 RegisterOutputs(module);
1005 2 RegisterOverlays(module);
1006 2 RegisterRenderer(module);
1007 2 module.def(
1008 "composite_overlay",
1009 2 [](const device::Tensor& color, const device::Tensor& overlay) {
1010
0/1
✗ Branch 1 not taken.
✗ return CompositeOverlay(color, overlay);
1011 },
1012 2 nb::arg("color"), nb::arg("overlay"), nb::call_guard<nb::gil_scoped_release>());
1013 2 module.def(
1014 "render_overlay_text",
1015 2 [](device::Device& device, std::string_view text, float lineHeight,
1016 std::optional<Float4> color) {
1017
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ return RenderOverlayText(device, text, lineHeight, color.value_or(Float4{1, 1, 1, 1}));
1018 },
1019 2 nb::arg("device"), nb::arg("text"), nb::arg("line_height"), nb::arg("color") = nb::none());
1020 2 RegisterRayCaster(module);
1021 2 }
1022
1023 }
1024