| 96 | } |
| 97 | |
| 98 | [[nodiscard]] std::optional<vis::ViewportRender> captureViewportRenderThreadSafe() { |
| 99 | auto invoke_capture = []() -> std::optional<vis::ViewportRender> { |
| 100 | return vis::capture_viewport_render(); |
| 101 | }; |
| 102 | |
| 103 | auto* const viewer = get_visualizer(); |
| 104 | if (!viewer || viewer->isOnViewerThread()) { |
| 105 | return invoke_capture(); |
| 106 | } |
| 107 | if (!viewer->acceptsPostedWork()) { |
| 108 | return std::nullopt; |
| 109 | } |
| 110 | |
| 111 | auto promise = std::make_shared<std::promise<std::optional<vis::ViewportRender>>>(); |
| 112 | auto future = promise->get_future(); |
| 113 | auto completed = std::make_shared<std::atomic_bool>(false); |
| 114 | |
| 115 | auto finish = [promise, completed](std::optional<vis::ViewportRender> result) mutable { |
| 116 | if (!completed->exchange(true)) { |
| 117 | promise->set_value(std::move(result)); |
| 118 | } |
| 119 | }; |
| 120 | |
| 121 | const bool posted = viewer->postWork(vis::Visualizer::WorkItem{ |
| 122 | .run = |
| 123 | [invoke_capture, finish]() mutable { |
| 124 | finish(invoke_capture()); |
| 125 | }, |
| 126 | .cancel = |
| 127 | [finish]() mutable { |
| 128 | finish(std::nullopt); |
| 129 | }}); |
| 130 | if (!posted) { |
| 131 | return std::nullopt; |
| 132 | } |
| 133 | |
| 134 | nb::gil_scoped_release release; |
| 135 | return future.get(); |
| 136 | } |
| 137 | |
| 138 | [[nodiscard]] core::PointCloud pointCloudFromMesh(const core::MeshData& mesh) { |
| 139 | core::Tensor colors; |
no test coverage detected