v2.0.0
Loading...
Searching...
No Matches
multimodalscene.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "multimodalscene.h"
18
19#include <QtGlobal>
20
21#include <algorithm>
22#include <limits>
23
24//=============================================================================================================
25// USED NAMESPACES
26//=============================================================================================================
27
28using namespace DISP3DLIB;
29
30//=============================================================================================================
31
33 : QObject(parent)
34{
35}
36
37//=============================================================================================================
38
40
41//=============================================================================================================
42
44{
45 if (layer.id.isEmpty()) {
46 qWarning("MultimodalScene::addLayer: refusing to add layer with empty id");
47 return;
48 }
49
50 const auto it = m_indexById.constFind(layer.id);
51 if (it != m_indexById.constEnd()) {
52 m_layers[*it] = std::move(layer);
53 } else {
54 m_indexById.insert(layer.id, m_layers.size());
55 m_layers.append(std::move(layer));
56 }
57 rebuildOrder();
58 emit layersChanged();
59}
60
61//=============================================================================================================
62
63bool MultimodalScene::removeLayer(const QString& id)
64{
65 const auto it = m_indexById.constFind(id);
66 if (it == m_indexById.constEnd()) {
67 return false;
68 }
69 m_layers.removeAt(*it);
70 rebuildOrder();
71 emit layersChanged();
72 return true;
73}
74
75//=============================================================================================================
76
78{
79 if (m_layers.isEmpty()) {
80 return;
81 }
82 m_layers.clear();
83 m_indexById.clear();
84 emit layersChanged();
85}
86
87//=============================================================================================================
88
89QVector<SceneLayer> MultimodalScene::layers() const
90{
91 return m_layers;
92}
93
94//=============================================================================================================
95
96const SceneLayer* MultimodalScene::findLayer(const QString& id) const
97{
98 const auto it = m_indexById.constFind(id);
99 if (it == m_indexById.constEnd()) {
100 return nullptr;
101 }
102 return &m_layers.at(*it);
103}
104
105//=============================================================================================================
106
107void MultimodalScene::setLayerVisible(const QString& id, bool visible)
108{
109 const auto it = m_indexById.constFind(id);
110 if (it == m_indexById.constEnd()) {
111 return;
112 }
113 if (m_layers[*it].visible == visible) {
114 return;
115 }
116 m_layers[*it].visible = visible;
117 emit layersChanged();
118}
119
120//=============================================================================================================
121
122void MultimodalScene::setLayerOpacity(const QString& id, float opacity)
123{
124 const auto it = m_indexById.constFind(id);
125 if (it == m_indexById.constEnd()) {
126 return;
127 }
128 opacity = qBound(0.0f, opacity, 1.0f);
129 if (qFuzzyCompare(m_layers[*it].opacity + 1.0f, opacity + 1.0f)) {
130 return;
131 }
132 m_layers[*it].opacity = opacity;
133 emit layersChanged();
134}
135
136//=============================================================================================================
137
139{
140 return m_currentTimeSample;
141}
142
143//=============================================================================================================
144
146{
147 if (sample < 0) {
148 sample = -1;
149 }
150 if (sample == m_currentTimeSample) {
151 return;
152 }
153 m_currentTimeSample = sample;
154 emit timeSampleChanged(sample);
155}
156
157//=============================================================================================================
158
160{
161 return m_timeCursor;
162}
163
164//=============================================================================================================
165
167{
168 if (qFuzzyCompare(seconds + 1.0, m_timeCursor + 1.0)) {
169 return;
170 }
171 m_timeCursor = seconds;
172 emit timeCursorChanged(seconds);
173}
174
175//=============================================================================================================
176
178{
179 return m_overlayFmin;
180}
181
182//=============================================================================================================
183
185{
186 return m_overlayFmid;
187}
188
189//=============================================================================================================
190
192{
193 return m_overlayFmax;
194}
195
196//=============================================================================================================
197
198void MultimodalScene::setOverlayThresholds(float fmin, float fmid, float fmax)
199{
200 // Clamp to monotone fmin <= fmid <= fmax.
201 if (fmid < fmin) {
202 fmid = fmin;
203 }
204 if (fmax < fmid) {
205 fmax = fmid;
206 }
207 const bool same = qFuzzyCompare(fmin + 1.0f, m_overlayFmin + 1.0f)
208 && qFuzzyCompare(fmid + 1.0f, m_overlayFmid + 1.0f)
209 && qFuzzyCompare(fmax + 1.0f, m_overlayFmax + 1.0f);
210 if (same) {
211 return;
212 }
213 m_overlayFmin = fmin;
214 m_overlayFmid = fmid;
215 m_overlayFmax = fmax;
216 emit overlayThresholdsChanged(fmin, fmid, fmax);
217}
218
219//=============================================================================================================
220
222{
223 return m_lastPick;
224}
225
226//=============================================================================================================
227
229{
230 m_lastPick = pick;
231 emit picked(pick);
232}
233
234//=============================================================================================================
235
236void MultimodalScene::worldBounds(QVector3D& bbMin, QVector3D& bbMax) const
237{
238 constexpr float fmax = std::numeric_limits<float>::max();
239 constexpr float flow = std::numeric_limits<float>::lowest();
240 bbMin = QVector3D(fmax, fmax, fmax);
241 bbMax = QVector3D(flow, flow, flow);
242
243 bool anyHit = false;
244 for (const auto& layer : m_layers) {
245 if (!layer.visible) {
246 continue;
247 }
248 const auto it = m_boundsFns.constFind(static_cast<int>(layer.kind));
249 if (it == m_boundsFns.constEnd() || !*it) {
250 continue;
251 }
252 QVector3D mn;
253 QVector3D mx;
254 if (!(*it)(layer, mn, mx)) {
255 continue;
256 }
257 bbMin.setX(std::min(bbMin.x(), mn.x()));
258 bbMin.setY(std::min(bbMin.y(), mn.y()));
259 bbMin.setZ(std::min(bbMin.z(), mn.z()));
260 bbMax.setX(std::max(bbMax.x(), mx.x()));
261 bbMax.setY(std::max(bbMax.y(), mx.y()));
262 bbMax.setZ(std::max(bbMax.z(), mx.z()));
263 anyHit = true;
264 }
265
266 if (!anyHit) {
267 bbMin = QVector3D(-1.0f, -1.0f, -1.0f);
268 bbMax = QVector3D(1.0f, 1.0f, 1.0f);
269 }
270}
271
272//=============================================================================================================
273
275{
276 m_boundsFns.insert(static_cast<int>(kind), std::move(fn));
277}
278
279//=============================================================================================================
280
281void MultimodalScene::rebuildOrder()
282{
283 // Stable sort by (kind, drawOrder); insertion order is preserved as tie-breaker.
284 std::stable_sort(m_layers.begin(), m_layers.end(),
285 [](const SceneLayer& a, const SceneLayer& b) {
286 if (a.kind != b.kind) {
287 return static_cast<int>(a.kind) < static_cast<int>(b.kind);
288 }
289 return a.drawOrder < b.drawOrder;
290 });
291 m_indexById.clear();
292 m_indexById.reserve(m_layers.size());
293 for (int i = 0; i < m_layers.size(); ++i) {
294 m_indexById.insert(m_layers.at(i).id, i);
295 }
296}
Host-agnostic controller that owns the ordered scene-layer stack (MRI slice, BEM, cortex,...
3-D brain visualisation using the Qt RHI rendering backend.
SceneLayerKind
Modality of a layer in the multimodal scene.
Opaque, type-erased handle to a layer payload.
const PickResult & lastPick() const
void timeCursorChanged(double seconds)
void setLayerOpacity(const QString &id, float opacity)
void reportPick(const PickResult &pick)
void setOverlayThresholds(float fmin, float fmid, float fmax)
void registerBoundsFn(SceneLayerKind kind, BoundsFn fn)
void setLayerVisible(const QString &id, bool visible)
std::function< bool(const SceneLayer &layer, QVector3D &bbMin, QVector3D &bbMax)> BoundsFn
void picked(const DISP3DLIB::PickResult &pick)
void setTimeCursor(double seconds)
MultimodalScene(QObject *parent=nullptr)
void worldBounds(QVector3D &bbMin, QVector3D &bbMax) const
void addLayer(SceneLayer layer)
QVector< SceneLayer > layers() const
bool removeLayer(const QString &id)
const SceneLayer * findLayer(const QString &id) const
void setCurrentTimeSample(int sample)
void timeSampleChanged(int sample)
void overlayThresholdsChanged(float fmin, float fmid, float fmax)
Uniform payload for object-id picking across the multimodal scene.
Definition pickresult.h:70