Line |
Branch |
Exec |
Source |
1 |
|
|
/************************************************************************ |
2 |
|
|
* |
3 |
|
|
* Copyright (C) 2020-2024 IRCAD France |
4 |
|
|
* Copyright (C) 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 |
|
|
#include "modules/viz/scene3d/adaptor/voxel_picker.hpp" |
24 |
|
|
|
25 |
|
|
#include <core/com/signal.hxx> |
26 |
|
|
#include <core/com/signals.hpp> |
27 |
|
|
#include <core/com/slots.hxx> |
28 |
|
|
|
29 |
|
|
#include <data/helper/medical_image.hpp> |
30 |
|
|
|
31 |
|
|
#include <viz/scene3d/helper/camera.hpp> |
32 |
|
|
#include <viz/scene3d/utils.hpp> |
33 |
|
|
|
34 |
|
|
namespace sight::module::viz::scene3d::adaptor |
35 |
|
|
{ |
36 |
|
|
|
37 |
|
|
const core::com::slots::key_t SLICE_TYPE_SLOT = "sliceType"; |
38 |
|
|
|
39 |
|
|
static const core::com::signals::key_t PICKED_SIG = "picked"; |
40 |
|
|
|
41 |
|
|
//----------------------------------------------------------------------------- |
42 |
|
|
|
43 |
|
✗ |
voxel_picker::voxel_picker() noexcept |
44 |
|
|
{ |
45 |
|
✗ |
new_slot(SLICE_TYPE_SLOT, &voxel_picker::change_slice_type, this); |
46 |
|
|
|
47 |
|
✗ |
m_picked_sig = new_signal<core::com::signal<void(data::tools::picking_info)> >(PICKED_SIG); |
48 |
|
|
} |
49 |
|
|
|
50 |
|
|
//----------------------------------------------------------------------------- |
51 |
|
|
|
52 |
|
✗ |
void voxel_picker::configuring() |
53 |
|
|
{ |
54 |
|
✗ |
this->configure_params(); |
55 |
|
|
|
56 |
|
✗ |
const config_t config = this->get_config(); |
57 |
|
|
|
58 |
|
✗ |
static const std::string s_PRIORITY_CONFIG = CONFIG + "priority"; |
59 |
|
✗ |
static const std::string s_ORIENTATION_CONFIG = CONFIG + "orientation"; |
60 |
|
✗ |
static const std::string s_MODE_CONFIG = CONFIG + "mode"; |
61 |
|
✗ |
static const std::string s_LAYER_ORDER_DEPENDANT_CONFIG = CONFIG + "layerOrderDependant"; |
62 |
|
✗ |
static const std::string s_MOVE_ON_PICK_CONFIG = CONFIG + "moveOnPick"; |
63 |
|
|
|
64 |
|
✗ |
m_priority = config.get<int>(s_PRIORITY_CONFIG, m_priority); |
65 |
|
✗ |
m_layer_order_dependant = config.get<bool>(s_LAYER_ORDER_DEPENDANT_CONFIG, m_layer_order_dependant); |
66 |
|
|
|
67 |
|
✗ |
const std::string orientation = config.get<std::string>(s_ORIENTATION_CONFIG, "sagittal"); |
68 |
|
|
SIGHT_ASSERT( |
69 |
|
|
"Orientation mode must be 'axial', 'frontal' or 'sagittal'.", |
70 |
|
|
orientation == "axial" || orientation == "frontal" || orientation == "sagittal" |
71 |
|
✗ |
); |
72 |
|
✗ |
if(orientation == "axial") |
73 |
|
|
{ |
74 |
|
✗ |
m_orientation = orientation_mode::z_axis; |
75 |
|
|
} |
76 |
|
✗ |
else if(orientation == "frontal") |
77 |
|
|
{ |
78 |
|
✗ |
m_orientation = orientation_mode::y_axis; |
79 |
|
|
} |
80 |
|
✗ |
else if(orientation == "sagittal") |
81 |
|
|
{ |
82 |
|
✗ |
m_orientation = orientation_mode::x_axis; |
83 |
|
|
} |
84 |
|
|
|
85 |
|
✗ |
const std::string mode = config.get<std::string>(s_MODE_CONFIG, "2D"); |
86 |
|
✗ |
SIGHT_ASSERT("Mode must be '2D' or '3D'.", mode == "2D" || mode == "3D"); |
87 |
|
✗ |
m_mode_2d = mode == "2D"; |
88 |
|
|
|
89 |
|
✗ |
m_move_on_pick = config.get<bool>(s_MOVE_ON_PICK_CONFIG, m_move_on_pick); |
90 |
|
|
} |
91 |
|
|
|
92 |
|
|
//----------------------------------------------------------------------------- |
93 |
|
|
|
94 |
|
✗ |
void voxel_picker::starting() |
95 |
|
|
{ |
96 |
|
✗ |
adaptor::init(); |
97 |
|
|
|
98 |
|
✗ |
const auto interactor = std::dynamic_pointer_cast<sight::viz::scene3d::interactor::base>(this->get_sptr()); |
99 |
|
✗ |
this->layer()->add_interactor(interactor, m_priority); |
100 |
|
|
} |
101 |
|
|
|
102 |
|
|
//----------------------------------------------------------------------------- |
103 |
|
|
|
104 |
|
✗ |
service::connections_t voxel_picker::auto_connections() const |
105 |
|
|
{ |
106 |
|
✗ |
service::connections_t connections = adaptor::auto_connections(); |
107 |
|
✗ |
connections.push(IMAGE_INPUT, data::image::SLICE_TYPE_MODIFIED_SIG, SLICE_TYPE_SLOT); |
108 |
|
|
|
109 |
|
✗ |
return connections; |
110 |
|
|
} |
111 |
|
|
|
112 |
|
|
//----------------------------------------------------------------------------- |
113 |
|
|
|
114 |
|
✗ |
void voxel_picker::updating() noexcept |
115 |
|
|
{ |
116 |
|
|
} |
117 |
|
|
|
118 |
|
|
//----------------------------------------------------------------------------- |
119 |
|
|
|
120 |
|
✗ |
void voxel_picker::stopping() |
121 |
|
|
{ |
122 |
|
✗ |
const auto interactor = std::dynamic_pointer_cast<sight::viz::scene3d::interactor::base>(this->get_sptr()); |
123 |
|
✗ |
this->layer()->remove_interactor(interactor); |
124 |
|
|
|
125 |
|
✗ |
adaptor::deinit(); |
126 |
|
|
} |
127 |
|
|
|
128 |
|
|
//----------------------------------------------------------------------------- |
129 |
|
|
|
130 |
|
✗ |
void voxel_picker::button_press_event(mouse_button _button, modifier _mod, int _x, int _y) |
131 |
|
|
{ |
132 |
|
✗ |
this->pick(_button, _mod, _x, _y, true); |
133 |
|
|
} |
134 |
|
|
|
135 |
|
|
//----------------------------------------------------------------------------- |
136 |
|
|
|
137 |
|
✗ |
void voxel_picker::button_release_event(mouse_button _button, modifier _mod, int _x, int _y) |
138 |
|
|
{ |
139 |
|
✗ |
this->pick(_button, _mod, _x, _y, false); |
140 |
|
|
} |
141 |
|
|
|
142 |
|
|
//----------------------------------------------------------------------------- |
143 |
|
|
|
144 |
|
✗ |
void voxel_picker::pick(mouse_button _button, modifier _mod, int _x, int _y, bool _pressed) |
145 |
|
|
{ |
146 |
|
✗ |
if(_button == mouse_button::left) |
147 |
|
|
{ |
148 |
|
✗ |
if(auto layer = m_layer.lock()) |
149 |
|
|
{ |
150 |
|
✗ |
if(!sight::module::viz::scene3d::adaptor::voxel_picker::is_in_layer(_x, _y, layer, m_layer_order_dependant)) |
151 |
|
|
{ |
152 |
|
✗ |
return; |
153 |
|
|
} |
154 |
|
|
} |
155 |
|
|
|
156 |
|
|
// Get the scene manager. |
157 |
|
✗ |
const auto* const scene_manager = this->get_scene_manager(); |
158 |
|
|
|
159 |
|
|
// Create the ray from picking positions. |
160 |
|
✗ |
const auto* const camera = scene_manager->getCamera(sight::viz::scene3d::layer::DEFAULT_CAMERA_NAME); |
161 |
|
✗ |
const auto vp_pos = |
162 |
|
✗ |
sight::viz::scene3d::helper::camera::convert_from_window_to_viewport_space(*camera, _x, _y); |
163 |
|
✗ |
const Ogre::Ray vp_ray = camera->getCameraToViewportRay(vp_pos.x, vp_pos.y); |
164 |
|
|
|
165 |
|
|
// Get image information. |
166 |
|
✗ |
const auto image = m_image.lock(); |
167 |
|
✗ |
const auto spacing = sight::viz::scene3d::utils::get_ogre_spacing(*image); |
168 |
|
✗ |
const auto origin = sight::viz::scene3d::utils::get_ogre_origin(*image); |
169 |
|
|
|
170 |
|
✗ |
const std::pair<bool, Ogre::Vector3> result = |
171 |
|
✗ |
this->compute_ray_image_intersection(vp_ray, image.get_shared(), origin, spacing); |
172 |
|
|
|
173 |
|
✗ |
if(result.first) |
174 |
|
|
{ |
175 |
|
|
// Compute the ray/slice intersection. |
176 |
|
✗ |
const Ogre::Vector3 intersection = result.second; |
177 |
|
|
|
178 |
|
|
// Create the picking information. |
179 |
|
✗ |
data::tools::picking_info info; |
180 |
|
✗ |
info.m_world_pos[0] = static_cast<double>(intersection.x); |
181 |
|
✗ |
info.m_world_pos[1] = static_cast<double>(intersection.y); |
182 |
|
✗ |
info.m_world_pos[2] = static_cast<double>(intersection.z); |
183 |
|
|
|
184 |
|
✗ |
using picking_event_t = data::tools::picking_info::event; |
185 |
|
✗ |
switch(_button) |
186 |
|
|
{ |
187 |
|
✗ |
case mouse_button::left: |
188 |
|
✗ |
info.m_event_id = _pressed ? picking_event_t::mouse_left_down : picking_event_t::mouse_left_up; |
189 |
|
✗ |
break; |
190 |
|
|
|
191 |
|
|
case mouse_button::right: |
192 |
|
|
info.m_event_id = _pressed ? picking_event_t::mouse_right_down : picking_event_t::mouse_right_up; |
193 |
|
|
break; |
194 |
|
|
|
195 |
|
|
case mouse_button::middle: |
196 |
|
|
info.m_event_id = _pressed ? picking_event_t::mouse_middle_down : picking_event_t::mouse_middle_up; |
197 |
|
|
break; |
198 |
|
|
|
199 |
|
|
default: |
200 |
|
|
SIGHT_ERROR("Unknown mouse button"); |
201 |
|
|
break; |
202 |
|
|
} |
203 |
|
|
|
204 |
|
✗ |
if(static_cast<bool>(_mod & modifier::control)) |
205 |
|
|
{ |
206 |
|
✗ |
info.m_modifier_mask |= data::tools::picking_info::ctrl; |
207 |
|
|
|
208 |
|
✗ |
if(m_move_on_pick && info.m_event_id == data::tools::picking_info::event::mouse_left_up) |
209 |
|
|
{ |
210 |
|
|
// Emit slices positions |
211 |
|
✗ |
const int sagittal_idx = static_cast<int>((info.m_world_pos[0] - origin[0]) / spacing[0]); |
212 |
|
✗ |
const int frontal_idx = static_cast<int>((info.m_world_pos[1] - origin[1]) / spacing[1]); |
213 |
|
✗ |
const int axial_idx = static_cast<int>((info.m_world_pos[2] - origin[2]) / spacing[2]); |
214 |
|
✗ |
const auto sig = image->signal<data::image::slice_index_modified_signal_t>( |
215 |
|
|
data::image::SLICE_INDEX_MODIFIED_SIG |
216 |
|
✗ |
); |
217 |
|
✗ |
sig->async_emit(axial_idx, frontal_idx, sagittal_idx); |
218 |
|
|
} |
219 |
|
|
} |
220 |
|
|
|
221 |
|
✗ |
if(static_cast<bool>(_mod & modifier::shift)) |
222 |
|
|
{ |
223 |
|
✗ |
info.m_modifier_mask |= data::tools::picking_info::shift; |
224 |
|
|
} |
225 |
|
|
|
226 |
|
|
// Emit the picking info. |
227 |
|
✗ |
m_picked_sig->async_emit(info); |
228 |
|
|
|
229 |
|
|
// Cancel further interactors on the same layer. |
230 |
|
✗ |
this->layer()->cancel_further_interaction(); |
231 |
|
|
} |
232 |
|
|
} |
233 |
|
|
} |
234 |
|
|
|
235 |
|
|
//----------------------------------------------------------------------------- |
236 |
|
|
|
237 |
|
✗ |
void voxel_picker::change_slice_type(int _from, int _to) |
238 |
|
|
{ |
239 |
|
✗ |
const auto to_orientation = static_cast<orientation_mode>(_to); |
240 |
|
✗ |
const auto from_orientation = static_cast<orientation_mode>(_from); |
241 |
|
|
|
242 |
|
✗ |
m_orientation = m_orientation == to_orientation ? from_orientation |
243 |
|
|
: m_orientation |
244 |
|
|
== from_orientation ? to_orientation : m_orientation; |
245 |
|
|
} |
246 |
|
|
|
247 |
|
|
//----------------------------------------------------------------------------- |
248 |
|
|
|
249 |
|
✗ |
std::pair<bool, Ogre::Vector3> voxel_picker::compute_ray_image_intersection( |
250 |
|
|
const Ogre::Ray& _ray, |
251 |
|
|
const data::image::csptr _image, |
252 |
|
|
const Ogre::Vector3& _origin, |
253 |
|
|
const Ogre::Vector3& _spacing |
254 |
|
|
) |
255 |
|
|
{ |
256 |
|
✗ |
namespace imHelper = data::helper::medical_image; |
257 |
|
✗ |
const auto axial_idx = imHelper::get_slice_index(*_image, imHelper::axis_t::axial).value_or(0); |
258 |
|
✗ |
const auto frontal_idx = imHelper::get_slice_index(*_image, imHelper::axis_t::frontal).value_or(0); |
259 |
|
✗ |
const auto sagittal_idx = imHelper::get_slice_index(*_image, imHelper::axis_t::sagittal).value_or(0); |
260 |
|
|
|
261 |
|
✗ |
const auto axial_index = static_cast<Ogre::Real>(axial_idx); |
262 |
|
✗ |
const auto frontal_index = static_cast<Ogre::Real>(frontal_idx); |
263 |
|
✗ |
const auto sagittal_index = static_cast<Ogre::Real>(sagittal_idx); |
264 |
|
|
|
265 |
|
✗ |
const auto size = _image->size(); |
266 |
|
|
|
267 |
|
|
// Function to check if an intersection is inside an image. |
268 |
|
✗ |
std::function<bool(orientation_mode, Ogre::Vector3)> is_inside_image = |
269 |
|
✗ |
[&](orientation_mode _orientation, const Ogre::Vector3 _inter) -> bool |
270 |
|
|
{ |
271 |
|
✗ |
switch(_orientation) |
272 |
|
|
{ |
273 |
|
✗ |
case orientation_mode::x_axis: |
274 |
|
✗ |
return _inter.y >= _origin.y |
275 |
|
✗ |
&& _inter.z >= _origin.z |
276 |
|
✗ |
&& _inter.y <= _origin.y + _spacing.y * static_cast<Ogre::Real>(size[1]) |
277 |
|
✗ |
&& _inter.z <= _origin.z + _spacing.z * static_cast<Ogre::Real>(size[2]); |
278 |
|
|
|
279 |
|
✗ |
case orientation_mode::y_axis: |
280 |
|
✗ |
return _inter.x >= _origin.x |
281 |
|
✗ |
&& _inter.z >= _origin.z |
282 |
|
✗ |
&& _inter.x <= _origin.x + _spacing.x * static_cast<Ogre::Real>(size[0]) |
283 |
|
✗ |
&& _inter.z <= _origin.z + _spacing.z * static_cast<Ogre::Real>(size[2]); |
284 |
|
|
|
285 |
|
✗ |
case orientation_mode::z_axis: |
286 |
|
✗ |
return _inter.x >= _origin.x |
287 |
|
✗ |
&& _inter.y >= _origin.y |
288 |
|
✗ |
&& _inter.x <= _origin.x + _spacing.x * static_cast<Ogre::Real>(size[0]) |
289 |
|
✗ |
&& _inter.y <= _origin.y + _spacing.y * static_cast<Ogre::Real>(size[1]); |
290 |
|
|
|
291 |
|
✗ |
default: |
292 |
|
✗ |
SIGHT_ERROR("Unknown orientation mode"); |
293 |
|
✗ |
return false; |
294 |
|
|
} |
295 |
|
✗ |
}; |
296 |
|
|
|
297 |
|
|
// Function to cast the non depth coordinate into image spacing. |
298 |
|
✗ |
std::function<Ogre::Vector3(orientation_mode, Ogre::Vector3)> cast_to_voxel = |
299 |
|
✗ |
[&](orientation_mode _orientation, Ogre::Vector3 _inter) -> Ogre::Vector3 |
300 |
|
|
{ |
301 |
|
✗ |
switch(_orientation) |
302 |
|
|
{ |
303 |
|
✗ |
case orientation_mode::x_axis: |
304 |
|
|
{ |
305 |
|
✗ |
const int y_idx = static_cast<int>(_inter.y / _spacing.y); |
306 |
|
✗ |
const int z_idx = static_cast<int>(_inter.z / _spacing.z); |
307 |
|
✗ |
_inter.y = static_cast<float>(y_idx) * _spacing.y; |
308 |
|
✗ |
_inter.z = static_cast<float>(z_idx) * _spacing.z; |
309 |
|
✗ |
break; |
310 |
|
|
} |
311 |
|
|
|
312 |
|
✗ |
case orientation_mode::y_axis: |
313 |
|
|
{ |
314 |
|
✗ |
const int x_idx = static_cast<int>(_inter.x / _spacing.x); |
315 |
|
✗ |
const int z_idx = static_cast<int>(_inter.z / _spacing.z); |
316 |
|
✗ |
_inter.x = static_cast<float>(x_idx) * _spacing.x; |
317 |
|
✗ |
_inter.z = static_cast<float>(z_idx) * _spacing.z; |
318 |
|
✗ |
break; |
319 |
|
|
} |
320 |
|
|
|
321 |
|
✗ |
case orientation_mode::z_axis: |
322 |
|
|
{ |
323 |
|
✗ |
const int x_idx = static_cast<int>(_inter.x / _spacing.x); |
324 |
|
✗ |
const int y_idx = static_cast<int>(_inter.y / _spacing.y); |
325 |
|
✗ |
_inter.x = static_cast<float>(x_idx) * _spacing.x; |
326 |
|
✗ |
_inter.y = static_cast<float>(y_idx) * _spacing.y; |
327 |
|
✗ |
break; |
328 |
|
|
} |
329 |
|
|
|
330 |
|
✗ |
default: |
331 |
|
✗ |
SIGHT_ERROR("Unknown orientation mode"); |
332 |
|
✗ |
break; |
333 |
|
|
} |
334 |
|
|
|
335 |
|
✗ |
return _inter; |
336 |
|
✗ |
}; |
337 |
|
|
|
338 |
|
|
// If it's a 2D mode, the intersection is computed between the ray and the current image slice. |
339 |
|
✗ |
if(m_mode_2d) |
340 |
|
|
{ |
341 |
|
|
// Create the plane from image information. |
342 |
|
✗ |
Ogre::Plane plane; |
343 |
|
✗ |
switch(m_orientation) |
344 |
|
|
{ |
345 |
|
✗ |
case orientation_mode::x_axis: |
346 |
|
✗ |
plane = Ogre::Plane(Ogre::Vector3::UNIT_X, _origin.x + sagittal_index * _spacing.x); |
347 |
|
✗ |
break; |
348 |
|
|
|
349 |
|
✗ |
case orientation_mode::y_axis: |
350 |
|
✗ |
plane = Ogre::Plane(Ogre::Vector3::UNIT_Y, _origin.y + frontal_index * _spacing.y); |
351 |
|
✗ |
break; |
352 |
|
|
|
353 |
|
✗ |
case orientation_mode::z_axis: |
354 |
|
✗ |
plane = Ogre::Plane(Ogre::Vector3::UNIT_Z, _origin.z + axial_index * _spacing.z); |
355 |
|
✗ |
break; |
356 |
|
|
|
357 |
|
✗ |
default: |
358 |
|
✗ |
SIGHT_ERROR("Unknown orientation mode"); |
359 |
|
✗ |
break; |
360 |
|
|
} |
361 |
|
|
|
362 |
|
|
// Check the intersection. |
363 |
|
✗ |
const Ogre::RayTestResult result = _ray.intersects(plane); |
364 |
|
|
|
365 |
|
✗ |
if(result.first) |
366 |
|
|
{ |
367 |
|
✗ |
Ogre::Vector3 intersection = _ray.getPoint(result.second); |
368 |
|
✗ |
return std::make_pair( |
369 |
|
✗ |
is_inside_image(m_orientation, intersection), |
370 |
|
✗ |
cast_to_voxel(m_orientation, intersection) |
371 |
|
|
); |
372 |
|
|
} |
373 |
|
|
} |
374 |
|
|
// Else, the intersection is computed between each slice. The nearest one is returned. |
375 |
|
|
else |
376 |
|
|
{ |
377 |
|
✗ |
const Ogre::Plane sagittal_plane = |
378 |
|
✗ |
Ogre::Plane(Ogre::Vector3::UNIT_X, _origin.x + sagittal_index * _spacing.x); |
379 |
|
✗ |
const Ogre::Plane frontal_plane = |
380 |
|
✗ |
Ogre::Plane(Ogre::Vector3::UNIT_Y, _origin.y + frontal_index * _spacing.y); |
381 |
|
✗ |
const Ogre::Plane axial_plane = Ogre::Plane(Ogre::Vector3::UNIT_Z, _origin.z + axial_index * _spacing.z); |
382 |
|
|
|
383 |
|
✗ |
Ogre::RayTestResult sagittal_inter = _ray.intersects(sagittal_plane); |
384 |
|
✗ |
Ogre::RayTestResult frontal_inter = _ray.intersects(frontal_plane); |
385 |
|
✗ |
Ogre::RayTestResult axial_inter = _ray.intersects(axial_plane); |
386 |
|
|
|
387 |
|
✗ |
if(sagittal_inter.first) |
388 |
|
|
{ |
389 |
|
✗ |
Ogre::Vector3 intersection = _ray.getPoint(sagittal_inter.second); |
390 |
|
✗ |
sagittal_inter.first = is_inside_image(orientation_mode::x_axis, intersection); |
391 |
|
|
} |
392 |
|
|
|
393 |
|
✗ |
if(frontal_inter.first) |
394 |
|
|
{ |
395 |
|
✗ |
Ogre::Vector3 intersection = _ray.getPoint(frontal_inter.second); |
396 |
|
✗ |
frontal_inter.first = is_inside_image(orientation_mode::y_axis, intersection); |
397 |
|
|
} |
398 |
|
|
|
399 |
|
✗ |
if(axial_inter.first) |
400 |
|
|
{ |
401 |
|
✗ |
Ogre::Vector3 intersection = _ray.getPoint(axial_inter.second); |
402 |
|
✗ |
axial_inter.first = is_inside_image(orientation_mode::z_axis, intersection); |
403 |
|
|
} |
404 |
|
|
|
405 |
|
✗ |
orientation_mode orientation = orientation_mode::x_axis; |
406 |
|
✗ |
Ogre::RayTestResult result = std::make_pair(false, std::numeric_limits<Ogre::Real>::max()); |
407 |
|
✗ |
if(sagittal_inter.first) |
408 |
|
|
{ |
409 |
|
✗ |
orientation = orientation_mode::x_axis; |
410 |
|
✗ |
result.second = sagittal_inter.second; |
411 |
|
✗ |
result.first = true; |
412 |
|
|
} |
413 |
|
|
|
414 |
|
✗ |
if(frontal_inter.first && frontal_inter.second < result.second) |
415 |
|
|
{ |
416 |
|
✗ |
orientation = orientation_mode::y_axis; |
417 |
|
✗ |
result.second = frontal_inter.second; |
418 |
|
✗ |
result.first = true; |
419 |
|
|
} |
420 |
|
|
|
421 |
|
✗ |
if(axial_inter.first && axial_inter.second < result.second) |
422 |
|
|
{ |
423 |
|
✗ |
orientation = orientation_mode::z_axis; |
424 |
|
✗ |
result.second = axial_inter.second; |
425 |
|
✗ |
result.first = true; |
426 |
|
|
} |
427 |
|
|
|
428 |
|
✗ |
if(result.first) |
429 |
|
|
{ |
430 |
|
✗ |
const Ogre::Vector3 intersection = _ray.getPoint(result.second); |
431 |
|
✗ |
return std::make_pair(true, cast_to_voxel(orientation, intersection)); |
432 |
|
|
} |
433 |
|
|
} |
434 |
|
|
|
435 |
|
✗ |
return std::make_pair(false, Ogre::Vector3 {0.0, 0.0, 0.0}); |
436 |
|
|
} |
437 |
|
|
|
438 |
|
|
//----------------------------------------------------------------------------- |
439 |
|
|
|
440 |
|
|
} // namespace sight::module::viz::scene3d::adaptor. |
441 |
|
|
|