GCC Code Coverage Report


./
File: modules/viz/scene3d/adaptor/negato3d.cpp
Date: 2025-01-21 16:21:04
Lines:
212/281
75.4%
Functions:
15/22
68.2%
Branches:
166/382
43.5%

Line Branch Exec Source
1 /************************************************************************
2 *
3 * Copyright (C) 2014-2025 IRCAD France
4 * Copyright (C) 2014-2020 IHU Strasbourg
5 *
6 * This file is part of Sight.
7 *
8 * Sight is free software: you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Sight is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with Sight. If not, see <https://www.gnu.org/licenses/>.
20 *
21 ***********************************************************************/
22
23 // cspell:ignore NOLINTNEXTLINE NOLINT
24
25 #include "modules/viz/scene3d/adaptor/negato3d.hpp"
26
27 #include <core/com/signal.hxx>
28 #include <core/com/slots.hxx>
29
30 #include <data/boolean.hpp>
31 #include <data/helper/medical_image.hpp>
32 #include <data/image.hpp>
33 #include <data/tools/color.hpp>
34
35 #include <geometry/data/image.hpp>
36
37 #include <viz/scene3d/ogre.hpp>
38 #include <viz/scene3d/utils.hpp>
39
40 #include <OGRE/OgreCamera.h>
41 #include <OGRE/OgreSceneNode.h>
42 #include <OGRE/OgreVector.h>
43
44 #include <algorithm>
45
46 namespace sight::module::viz::scene3d::adaptor
47 {
48
49 //------------------------------------------------------------------------------
50
51
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 negato3d::negato3d() noexcept
52 {
53 // Auto-connected slots
54
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 new_slot(slots::UPDATE_IMAGE, [this](){lazy_update(update_flags::IMAGE);});
55
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 new_slot(slots::UPDATE_TF, [this](){lazy_update(update_flags::TF);});
56
57 // Interaction slots
58
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 new_slot(slots::SLICE_TYPE, &negato3d::change_slice_type, this);
59
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 new_slot(slots::SLICE_INDEX, &negato3d::change_slice_index, this);
60
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 new_slot(slots::UPDATE_SLICES_FROM_WORLD, &negato3d::update_slices_from_world, this);
61
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 new_slot(slots::SET_TRANSPARENCY, &negato3d::set_transparency, this);
62
63
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 new_signal<signals::picked_voxel_t>(signals::PICKED_VOXEL);
64 18 }
65
66 //------------------------------------------------------------------------------
67
68 7 void negato3d::configuring()
69 {
70 7 this->configure_params();
71
72 7 const config_t config = this->get_config();
73
74
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
7 static const std::string s_AUTORESET_CAMERA_CONFIG = CONFIG + "autoresetcamera";
75
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
7 static const std::string s_FILTERING_CONFIG = CONFIG + "filtering";
76
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
7 static const std::string s_TF_ALPHA_CONFIG = CONFIG + "tfAlpha";
77
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
7 static const std::string s_INTERACTIVE_CONFIG = CONFIG + "interactive";
78
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
7 static const std::string s_PRIORITY_CONFIG = CONFIG + "priority";
79
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
7 static const std::string s_QUERY_CONFIG = CONFIG + "queryFlags";
80
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
7 static const std::string s_BORDER_CONFIG = CONFIG + "border";
81
82
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
21 m_auto_reset_camera = config.get<bool>(s_AUTORESET_CAMERA_CONFIG, true);
83
84
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if(config.count(s_FILTERING_CONFIG) != 0U)
85 {
86 const auto filtering_value = config.get<std::string>(s_FILTERING_CONFIG);
87 sight::viz::scene3d::plane::filter_t filtering(sight::viz::scene3d::plane::filter_t::linear);
88
89 if(filtering_value == "none")
90 {
91 filtering = sight::viz::scene3d::plane::filter_t::none;
92 }
93 else if(filtering_value == "anisotropic")
94 {
95 filtering = sight::viz::scene3d::plane::filter_t::anisotropic;
96 }
97
98 m_filtering = filtering;
99 }
100
101
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
21 const std::string hexa_mask = config.get<std::string>(s_QUERY_CONFIG, "");
102
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if(!hexa_mask.empty())
103 {
104 SIGHT_ASSERT(
105 "Hexadecimal values should start with '0x'"
106 "Given value : " + hexa_mask,
107 hexa_mask.length() > 2
108 && hexa_mask.substr(0, 2) == "0x"
109 7 );
110
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 m_query_flags = static_cast<std::uint32_t>(std::stoul(hexa_mask, nullptr, 16));
111 }
112
113
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
14 m_enable_alpha = config.get<bool>(s_TF_ALPHA_CONFIG, m_enable_alpha);
114
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
14 m_interactive = config.get<bool>(s_INTERACTIVE_CONFIG, m_interactive);
115
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
14 m_priority = config.get<int>(s_PRIORITY_CONFIG, m_priority);
116
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
14 m_border = config.get<bool>(s_BORDER_CONFIG, m_border);
117
118 7 const std::string transform_id =
119
3/6
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
21 config.get<std::string>(sight::viz::scene3d::transformable::TRANSFORM_CONFIG, this->get_id() + "_transform");
120
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
14 this->set_transform_id(transform_id);
121 7 }
122
123 //------------------------------------------------------------------------------
124
125 7 void negato3d::starting()
126 {
127 7 adaptor::init();
128
129
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 this->render_service()->make_current();
130 7 {
131 // 3D source texture instantiation
132 7 const auto image = m_image.lock();
133
4/8
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 7 times.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
14 m_3d_ogre_texture = std::make_shared<sight::viz::scene3d::texture>(image.get_shared());
134
135 // TF texture initialization
136 7 const auto tf = m_tf.lock();
137
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
14 m_gpu_tf = std::make_unique<sight::viz::scene3d::transfer_function>(tf.get_shared());
138 7 }
139
140 // scene node's instantiation
141 7 Ogre::SceneNode* const root_scene_node = this->get_scene_manager()->getRootSceneNode();
142 7 Ogre::SceneNode* const transform_node = this->get_or_create_transform_node(root_scene_node);
143 7 m_origin_scene_node = transform_node->createChildSceneNode();
144 7 m_negato_scene_node = m_origin_scene_node->createChildSceneNode();
145
146 // Instantiation of the planes
147
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 7 times.
28 for(auto& plane : m_planes)
148 {
149 42 plane = std::make_shared<sight::viz::scene3d::plane>(
150 this->get_id(),
151 21 m_negato_scene_node,
152 21 this->get_scene_manager(),
153 21 m_3d_ogre_texture,
154 21 m_filtering,
155 21 m_border,
156 42 false
157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 );
158 }
159
160
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if(m_auto_reset_camera)
161 {
162
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
14 this->render_service()->reset_camera_coordinates(m_layer_id);
163 }
164
165 7 this->new_image();
166
167
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if(m_interactive)
168 {
169
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
7 auto interactor = std::dynamic_pointer_cast<sight::viz::scene3d::interactor::base>(this->get_sptr());
170
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
7 this->layer()->add_interactor(interactor, m_priority);
171
172
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
14 m_picking_cross = std::make_unique<sight::viz::scene3d::picking_cross>(
173
2/6
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
14 this->get_id(),
174 7 *this->get_scene_manager(),
175
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 *m_negato_scene_node
176 7 );
177 7 }
178
179 // Set the visibility of the 3D Negato
180 7 this->set_visible(visible());
181 7 }
182
183 //-----------------------------------------------------------------------------
184
185 7 service::connections_t negato3d::auto_connections() const
186 {
187 7 service::connections_t connections = {
188
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 {m_image, data::image::MODIFIED_SIG, slots::UPDATE_IMAGE},
189 7 {m_image, data::image::BUFFER_MODIFIED_SIG, slots::UPDATE_IMAGE},
190 7 {m_tf, data::transfer_function::POINTS_MODIFIED_SIG, slots::UPDATE_TF},
191 7 {m_tf, data::transfer_function::WINDOWING_MODIFIED_SIG, slots::UPDATE_TF},
192 7 {m_tf, data::transfer_function::MODIFIED_SIG, slots::UPDATE_TF},
193 7 {m_image, data::image::SLICE_TYPE_MODIFIED_SIG, slots::SLICE_TYPE},
194 7 {m_image, data::image::SLICE_INDEX_MODIFIED_SIG, slots::SLICE_INDEX}
195
2/4
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
56 };
196
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
14 return connections + adaptor::auto_connections();
197
7/16
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 7 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 7 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 7 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 7 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
14 }
198
199 //------------------------------------------------------------------------------
200
201 void negato3d::updating()
202 {
203 if(update_needed(update_flags::IMAGE))
204 {
205 this->new_image();
206 }
207 else if(update_needed(update_flags::TF))
208 {
209 this->update_tf();
210 }
211
212 this->update_done();
213 this->request_render();
214 }
215
216 //------------------------------------------------------------------------------
217
218 7 void negato3d::stopping()
219 {
220
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 this->render_service()->make_current();
221
222
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if(m_interactive)
223 {
224
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
7 auto interactor = std::dynamic_pointer_cast<sight::viz::scene3d::interactor::base>(this->get_sptr());
225
3/8
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
14 this->layer()->remove_interactor(interactor);
226 7 }
227
228 7 m_picked_plane.reset();
229 28 std::ranges::for_each(m_planes, [](auto& _p){_p.reset();});
230
231 7 auto* const scene_manager = this->get_scene_manager();
232 7 m_negato_scene_node->removeAndDestroyAllChildren();
233 7 scene_manager->destroySceneNode(m_negato_scene_node);
234 7 m_negato_scene_node = nullptr;
235
236 7 m_origin_scene_node->removeAndDestroyAllChildren();
237 7 scene_manager->destroySceneNode(m_origin_scene_node);
238 7 m_origin_scene_node = nullptr;
239
240
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 m_picking_cross.reset();
241
242 7 m_3d_ogre_texture.reset();
243
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 m_gpu_tf.reset();
244
245 7 adaptor::deinit();
246 7 }
247
248 //------------------------------------------------------------------------------
249
250 7 void negato3d::new_image()
251 {
252
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 this->render_service()->make_current();
253
254 7 int axial_idx = 0;
255 7 int frontal_idx = 0;
256 7 int sagittal_idx = 0;
257 7 {
258 7 const auto image = m_image.lock();
259
260
4/8
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
21 if(!data::helper::medical_image::check_image_validity(image.get_shared()))
261 {
262 return;
263 }
264
265 // Retrieves or creates the slice index fields
266
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 m_3d_ogre_texture->update();
267
268
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 const auto spacing = sight::viz::scene3d::utils::get_ogre_spacing(*image);
269
270
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
7 m_origin_scene_node->setPosition(sight::viz::scene3d::utils::get_ogre_origin(*image));
271
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
7 m_origin_scene_node->setOrientation(sight::viz::scene3d::utils::get_ogre_orientation(*image));
272
273 // Fits the planes to the new texture
274
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 7 times.
28 for(int orientation_num = 0 ; const auto& plane : m_planes)
275 {
276
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 plane->update(
277 21 static_cast<axis_t>(orientation_num++),
278 spacing,
279
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 m_enable_alpha
280 );
281
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 plane->set_query_flags(m_query_flags);
282 }
283
284 // Update Slice
285 7 namespace medical_image = data::helper::medical_image;
286
287
3/6
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
14 axial_idx = std::max(0, int(medical_image::get_slice_index(*image, axis_t::axial).value_or(0)));
288
3/6
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
14 frontal_idx = std::max(0, int(medical_image::get_slice_index(*image, axis_t::frontal).value_or(0)));
289
3/6
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
14 sagittal_idx = std::max(0, int(medical_image::get_slice_index(*image, axis_t::sagittal).value_or(0)));
290 7 }
291
292 7 this->change_slice_index(axial_idx, frontal_idx, sagittal_idx);
293
294 // Update transfer function in Gpu programs
295 7 this->update_tf();
296
297 7 this->apply_visibility();
298
299 7 this->request_render();
300 }
301
302 //------------------------------------------------------------------------------
303
304 15 void negato3d::change_slice_type(int /*unused*/, int /*unused*/)
305 {
306
1/2
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 this->render_service()->make_current();
307
308 15 this->update_tf();
309
310 15 this->request_render();
311 15 }
312
313 //------------------------------------------------------------------------------
314
315 23 void negato3d::change_slice_index(int _axial_index, int _frontal_index, int _sagittal_index)
316 {
317 23 const auto image = m_image.lock();
318
319
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 auto img_size = image->size();
320
321 // Sometimes, the image can contain only one slice,
322 // it results into a division by 0 when the range is transformed between [0-1].
323 // So we increase the image size to 2 to divide by 1.
324
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 img_size[0] = img_size[0] == 1 ? 2 : img_size[0];
325
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 img_size[1] = img_size[1] == 1 ? 2 : img_size[1];
326
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 img_size[2] = img_size[2] == 1 ? 2 : img_size[2];
327
328 23 const std::array<float, 3> slice_indices = {
329 23 static_cast<float>(_sagittal_index) / (static_cast<float>(img_size[0] - 1)),
330 23 static_cast<float>(_frontal_index) / (static_cast<float>(img_size[1] - 1)),
331 23 static_cast<float>(_axial_index) / (static_cast<float>(img_size[2] - 1))
332 23 };
333
334
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 23 times.
92 for(std::uint8_t i = 0 ; i < 3 ; ++i)
335 {
336
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 m_planes[i]->change_slice(slice_indices);
337 }
338
339
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 this->request_render();
340 23 }
341
342 //-----------------------------------------------------------------------------
343
344 22 void negato3d::update_tf()
345 {
346
1/2
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
22 this->render_service()->make_current();
347 22 m_gpu_tf->update();
348
349 // Sends the TF texture to the negato-related passes
350 88 std::ranges::for_each(m_planes, [this](auto& _p){_p->set_tf_data(*m_gpu_tf.get());});
351
352 22 this->request_render();
353 22 }
354
355 //-----------------------------------------------------------------------------
356
357 void negato3d::set_transparency(double _transparency)
358 {
359 SIGHT_ASSERT("Service not started", this->started());
360
361 const float opacity = 1.F - static_cast<float>(_transparency);
362 std::ranges::for_each(m_planes, [opacity](auto& _p){_p->set_entity_opacity(opacity);});
363
364 this->request_render();
365 }
366
367 //------------------------------------------------------------------------------
368
369 21 void negato3d::set_visible(bool _visible)
370 {
371 84 std::ranges::for_each(m_planes, [_visible](auto& _p){_p->set_visible(_visible);});
372
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 if(m_auto_reset_camera)
373 {
374
1/2
✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
42 this->render_service()->reset_camera_coordinates(m_layer_id);
375 }
376
377 21 this->request_render();
378 21 }
379
380 //------------------------------------------------------------------------------
381
382 20 void negato3d::set_planes_query_flags(std::uint32_t _flags)
383 {
384 60 std::ranges::for_each(m_planes, [_flags](auto& _p){_p->set_query_flags(_flags);});
385 }
386
387 //------------------------------------------------------------------------------
388
389 10 void negato3d::mouse_move_event(mouse_button _button, modifier /*_mods*/, int _x, int _y, int /*_dx*/, int /*_dy*/)
390 {
391
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 7 times.
10 if(m_picked_plane)
392 {
393
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(_button == mouse_button::middle)
394 {
395 this->move_slices(_x, _y);
396 }
397
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 else if(_button == mouse_button::right)
398 {
399 const auto dx = static_cast<double>(_x - m_initial_pos[0]);
400 const auto dy = static_cast<double>(m_initial_pos[1] - _y);
401
402 this->update_windowing(dx, dy);
403 }
404
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 else if(_button == mouse_button::left)
405 {
406 3 this->pick_intensity(_x, _y);
407 }
408
409
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
6 this->layer()->cancel_further_interaction();
410 }
411 10 }
412
413 //------------------------------------------------------------------------------
414
415 20 void negato3d::button_press_event(mouse_button _button, modifier /*_mods*/, int _x, int _y)
416 {
417 20 m_picked_plane.reset();
418
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 m_picking_cross->set_visible(false);
419
420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if(_button == mouse_button::middle)
421 {
422 this->move_slices(_x, _y);
423 }
424
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 else if(_button == mouse_button::right)
425 {
426 if(this->get_picked_slices(_x, _y) != std::nullopt)
427 {
428 const auto tf = m_tf.const_lock();
429
430 m_initial_level = tf->level();
431 m_initial_window = tf->window();
432
433 m_initial_pos = {_x, _y};
434 }
435 }
436
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 else if(_button == mouse_button::left)
437 {
438 20 this->pick_intensity(_x, _y);
439 }
440
441
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 2 times.
20 if(m_picked_plane)
442 {
443
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
36 this->layer()->cancel_further_interaction();
444 }
445 20 }
446
447 //------------------------------------------------------------------------------
448
449 20 void negato3d::button_release_event(mouse_button /*_button*/, modifier /*_mods*/, int /*_x*/, int /*_y*/)
450 {
451
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 2 times.
20 if(m_picked_plane)
452 {
453 18 m_picked_plane->set_render_queuer_group_and_priority(sight::viz::scene3d::rq::SURFACE_ID, 0);
454 18 m_picked_plane.reset();
455 }
456
457 20 m_picking_cross->set_visible(false);
458
3/8
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 20 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
40 this->signal<signals::picked_voxel_t>(signals::PICKED_VOXEL)->async_emit("");
459 20 this->set_planes_query_flags(m_query_flags); // Make all planes pickable again.
460 20 }
461
462 //------------------------------------------------------------------------------
463
464 void negato3d::move_slices(int _x, int _y)
465 {
466 const auto pick_res = this->get_picked_slices(
467 _x,
468 _y
469 );
470
471 if(pick_res.has_value())
472 {
473 const auto image = m_image.lock();
474
475 auto picked_pt = pick_res.value();
476
477 std::ranges::for_each(
478 m_planes,
479 [this](auto& _p)
480 {
481 if(_p != m_picked_plane)
482 {
483 _p->set_query_flags(0x0);
484 }
485 });
486
487 const auto picked_voxel = geometry::data::world_to_image(*image, picked_pt, true, true);
488
489 image->async_emit(
490 data::image::SLICE_INDEX_MODIFIED_SIG,
491 int(picked_voxel[2]),
492 int(picked_voxel[1]),
493 int(picked_voxel[0])
494 );
495 }
496 }
497
498 //------------------------------------------------------------------------------
499
500 void negato3d::update_slices_from_world(double _x, double _y, double _z)
501 {
502 const auto image = m_image.lock();
503
504 Ogre::Vector3 point = {static_cast<float>(_x), static_cast<float>(_y), static_cast<float>(_z)};
505 Ogre::Vector3i slice_idx;
506 try
507 {
508 slice_idx = sight::viz::scene3d::utils::world_to_slices(*image, point);
509 }
510 catch(core::exception& e)
511 {
512 SIGHT_WARN("Cannot update slice index: " << e.what());
513 return;
514 }
515
516 image->async_emit(
517 data::image::SLICE_INDEX_MODIFIED_SIG,
518 int(slice_idx[2]),
519 int(slice_idx[1]),
520 int(slice_idx[0])
521 );
522 }
523
524 //------------------------------------------------------------------------------
525
526 23 void negato3d::pick_intensity(int _x, int _y)
527 {
528 23 auto sig = this->signal<signals::picked_voxel_t>(signals::PICKED_VOXEL);
529
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
✗ Branch 4 not taken.
23 if(sig->num_connections() > 0)
530 {
531
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 const auto picked_pos = this->get_picked_slices(_x, _y);
532
533
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 2 times.
23 if(picked_pos.has_value())
534 {
535 21 const auto image = m_image.lock();
536
537
4/8
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 21 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 21 times.
63 if(!data::helper::medical_image::check_image_validity(image.get_shared()))
538 {
539 return;
540 }
541
542
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 const auto cross_lines = m_picked_plane->compute_cross(*picked_pos, *image);
543
544
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 m_picking_cross->update(cross_lines[0], cross_lines[1], cross_lines[2], cross_lines[3]);
545
546
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 const auto picking_text = sight::viz::scene3d::utils::pick_image(*image, *picked_pos);
547
2/4
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
21 sig->async_emit(picking_text);
548
549
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 this->request_render();
550
551 // Render the picked plane before the widget.
552
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 m_picked_plane->set_render_queuer_group_and_priority(sight::viz::scene3d::rq::NEGATO_WIDGET_ID, 0);
553 21 }
554 }
555 23 }
556
557 //------------------------------------------------------------------------------
558
559 23 std::optional<Ogre::Vector3> negato3d::get_picked_slices(int _x, int _y)
560 {
561 23 Ogre::SceneManager* sm = this->get_scene_manager();
562 23 const auto result = sight::viz::scene3d::utils::pick_object(_x, _y, m_query_flags, *sm);
563
564
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 2 times.
23 if(result.has_value())
565 {
566
2/2
✓ Branch 1 taken 42 times.
✓ Branch 2 taken 21 times.
63 const auto is_picked = [&result](const auto& _p){return _p->get_movable_object() == result->first;};
567 21 const auto it = std::ranges::find_if(m_planes, is_picked); // NOLINT(readability-qualified-auto,llvm-qualified-auto)
568
569
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 if(it != m_planes.cend())
570 {
571 21 m_picked_plane = *it;
572 21 return result->second;
573 }
574 }
575
576 2 return std::nullopt;
577 }
578
579 //------------------------------------------------------------------------------
580
581 void negato3d::update_windowing(double _dw, double _dl)
582 {
583 const double new_window = m_initial_window + _dw;
584 const double new_level = m_initial_level - _dl;
585
586 {
587 const auto tf = m_tf.lock();
588
589 tf->set_window(new_window);
590 tf->set_level(new_level);
591 const auto sig = tf->signal<data::transfer_function::windowing_modified_signal_t>(
592 data::transfer_function::WINDOWING_MODIFIED_SIG
593 );
594 {
595 sig->async_emit(new_window, new_level);
596 }
597 }
598 }
599
600 } // namespace sight::module::viz::scene3d::adaptor.
601