v2.0.0
Loading...
Searching...
No Matches
viewstate.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "viewstate.h"
18
20#include "view/brainrenderer.h"
21
22#include <QSettings>
23#include <cmath>
24#include <limits>
25
26namespace DISP3DLIB
27{
28
29//=============================================================================================================
30// ViewVisibilityProfile
31//=============================================================================================================
32
33bool ViewVisibilityProfile::isObjectVisible(const QString& object) const
34{
35 if (object == "lh")
36 return lh;
37 if (object == "rh")
38 return rh;
39 if (object == "bem_head")
40 return bemHead;
41 if (object == "bem_outer_skull")
42 return bemOuterSkull;
43 if (object == "bem_inner_skull")
44 return bemInnerSkull;
45 if (object == "sens_meg")
46 return sensMeg;
47 if (object == "sens_meg_grad")
48 return sensMegGrad;
49 if (object == "sens_meg_mag")
50 return sensMegMag;
51 if (object == "sens_meg_helmet")
52 return sensMegHelmet;
53 if (object == "sens_eeg")
54 return sensEeg;
55 if (object == "dig")
56 return dig;
57 if (object == "dig_cardinal")
58 return digCardinal;
59 if (object == "dig_hpi")
60 return digHpi;
61 if (object == "dig_eeg")
62 return digEeg;
63 if (object == "dig_extra")
64 return digExtra;
65 if (object == "field_meg")
66 return megFieldMap;
67 if (object == "field_eeg")
68 return eegFieldMap;
69 if (object == "contour_meg")
70 return megFieldContours;
71 if (object == "contour_eeg")
72 return eegFieldContours;
73 if (object == "dipoles")
74 return dipoles;
75 if (object == "source_space")
76 return sourceSpace;
77 if (object == "network")
78 return network;
79 if (object == "mri_slices")
80 return mriSlices;
81 return true; // unknown objects default visible
82}
83
84//=============================================================================================================
85
86void ViewVisibilityProfile::setObjectVisible(const QString& object, bool visible)
87{
88 if (object == "lh")
89 lh = visible;
90 else if (object == "rh")
91 rh = visible;
92 else if (object == "bem_head")
93 bemHead = visible;
94 else if (object == "bem_outer_skull")
95 bemOuterSkull = visible;
96 else if (object == "bem_inner_skull")
97 bemInnerSkull = visible;
98 else if (object == "sens_meg")
99 sensMeg = visible;
100 else if (object == "sens_meg_grad")
101 sensMegGrad = visible;
102 else if (object == "sens_meg_mag")
103 sensMegMag = visible;
104 else if (object == "sens_meg_helmet")
105 sensMegHelmet = visible;
106 else if (object == "sens_eeg")
107 sensEeg = visible;
108 else if (object == "dig")
109 dig = visible;
110 else if (object == "dig_cardinal")
111 digCardinal = visible;
112 else if (object == "dig_hpi")
113 digHpi = visible;
114 else if (object == "dig_eeg")
115 digEeg = visible;
116 else if (object == "dig_extra")
117 digExtra = visible;
118 else if (object == "field_meg")
119 megFieldMap = visible;
120 else if (object == "field_eeg")
121 eegFieldMap = visible;
122 else if (object == "contour_meg")
123 megFieldContours = visible;
124 else if (object == "contour_eeg")
125 eegFieldContours = visible;
126 else if (object == "dipoles")
127 dipoles = visible;
128 else if (object == "source_space")
129 sourceSpace = visible;
130 else if (object == "network")
131 network = visible;
132 else if (object == "mri_slices")
133 mriSlices = visible;
134}
135
136//=============================================================================================================
137
138void ViewVisibilityProfile::load(const QSettings& settings, const QString& prefix)
139{
140 lh = isTrue(settings.value(prefix + "lh"), lh);
141 rh = isTrue(settings.value(prefix + "rh"), rh);
142 bemHead = isTrue(settings.value(prefix + "bemHead"), bemHead);
143 bemOuterSkull = isTrue(settings.value(prefix + "bemOuterSkull"), bemOuterSkull);
144 bemInnerSkull = isTrue(settings.value(prefix + "bemInnerSkull"), bemInnerSkull);
145 sensMeg = isTrue(settings.value(prefix + "sensMeg"), sensMeg);
146 sensMegGrad = isTrue(settings.value(prefix + "sensMegGrad"), sensMegGrad);
147 sensMegMag = isTrue(settings.value(prefix + "sensMegMag"), sensMegMag);
148 sensMegHelmet = isTrue(settings.value(prefix + "sensMegHelmet"), sensMegHelmet);
149 sensEeg = isTrue(settings.value(prefix + "sensEeg"), sensEeg);
150 dig = isTrue(settings.value(prefix + "dig"), dig);
151 digCardinal = isTrue(settings.value(prefix + "digCardinal"), digCardinal);
152 digHpi = isTrue(settings.value(prefix + "digHpi"), digHpi);
153 digEeg = isTrue(settings.value(prefix + "digEeg"), digEeg);
154 digExtra = isTrue(settings.value(prefix + "digExtra"), digExtra);
155 megFieldMap = isTrue(settings.value(prefix + "megFieldMap"), megFieldMap);
156 eegFieldMap = isTrue(settings.value(prefix + "eegFieldMap"), eegFieldMap);
157 megFieldContours = isTrue(settings.value(prefix + "megFieldContours"), megFieldContours);
158 eegFieldContours = isTrue(settings.value(prefix + "eegFieldContours"), eegFieldContours);
159 dipoles = isTrue(settings.value(prefix + "dipoles"), dipoles);
160 sourceSpace = isTrue(settings.value(prefix + "sourceSpace"), sourceSpace);
161 network = isTrue(settings.value(prefix + "network"), network);
162 megFieldMapOnHead = isTrue(settings.value(prefix + "megFieldMapOnHead"), megFieldMapOnHead);
163 mriSlices = isTrue(settings.value(prefix + "mriSlices"), mriSlices);
164}
165
166//=============================================================================================================
167
168void ViewVisibilityProfile::save(QSettings& settings, const QString& prefix) const
169{
170 settings.setValue(prefix + "lh", lh);
171 settings.setValue(prefix + "rh", rh);
172 settings.setValue(prefix + "bemHead", bemHead);
173 settings.setValue(prefix + "bemOuterSkull", bemOuterSkull);
174 settings.setValue(prefix + "bemInnerSkull", bemInnerSkull);
175 settings.setValue(prefix + "sensMeg", sensMeg);
176 settings.setValue(prefix + "sensMegGrad", sensMegGrad);
177 settings.setValue(prefix + "sensMegMag", sensMegMag);
178 settings.setValue(prefix + "sensMegHelmet", sensMegHelmet);
179 settings.setValue(prefix + "sensEeg", sensEeg);
180 settings.setValue(prefix + "dig", dig);
181 settings.setValue(prefix + "digCardinal", digCardinal);
182 settings.setValue(prefix + "digHpi", digHpi);
183 settings.setValue(prefix + "digEeg", digEeg);
184 settings.setValue(prefix + "digExtra", digExtra);
185 settings.setValue(prefix + "megFieldMap", megFieldMap);
186 settings.setValue(prefix + "eegFieldMap", eegFieldMap);
187 settings.setValue(prefix + "megFieldContours", megFieldContours);
188 settings.setValue(prefix + "eegFieldContours", eegFieldContours);
189 settings.setValue(prefix + "dipoles", dipoles);
190 settings.setValue(prefix + "sourceSpace", sourceSpace);
191 settings.setValue(prefix + "network", network);
192 settings.setValue(prefix + "megFieldMapOnHead", megFieldMapOnHead);
193 settings.setValue(prefix + "mriSlices", mriSlices);
194}
195
196//=============================================================================================================
197// SubView
198//=============================================================================================================
199
200bool SubView::isBrainSurfaceKey(const QString& key)
201{
202 if (key.isEmpty())
203 return false;
204 if (key.startsWith("bem_"))
205 return false;
206 if (key.startsWith("sens_"))
207 return false;
208 if (key.startsWith("srcsp_"))
209 return false;
210 if (key.startsWith("dig_"))
211 return false;
212 return true;
213}
214
215//=============================================================================================================
216
217bool SubView::matchesSurfaceType(const QString& key) const
218{
219 return isBrainSurfaceKey(key) && key.endsWith(surfaceType);
220}
221
222//=============================================================================================================
223
224bool SubView::shouldRenderSurface(const QString& key) const
225{
226 if (key.startsWith("lh_"))
227 return visibility.lh;
228 if (key.startsWith("rh_"))
229 return visibility.rh;
230
231 if (key == "bem_head")
232 return visibility.bemHead;
233 if (key == "bem_outer_skull")
234 return visibility.bemOuterSkull;
235 if (key == "bem_inner_skull")
236 return visibility.bemInnerSkull;
237
238 if (key.startsWith("sens_contour_meg"))
239 return visibility.megFieldMap && visibility.megFieldContours;
240 if (key.startsWith("sens_contour_eeg"))
241 return visibility.eegFieldMap && visibility.eegFieldContours;
242 if (key.startsWith("sens_surface_meg"))
243 return visibility.sensMegHelmet;
244 if (key.startsWith("sens_meg_grad_"))
245 return visibility.sensMeg && visibility.sensMegGrad;
246 if (key.startsWith("sens_meg_mag_"))
247 return visibility.sensMeg && visibility.sensMegMag;
248 if (key.startsWith("sens_meg_"))
249 return visibility.sensMeg;
250 if (key.startsWith("sens_eeg_"))
251 return visibility.sensEeg;
252
253 if (key.startsWith("dig_live_"))
254 return true; // always show live tracker
255 if (key.startsWith("dig_static_"))
256 return true; // always show static markers
257 if (key.startsWith("dig_ray_"))
258 return true; // always show optical ray
259 if (key.startsWith("dig_probe_"))
260 return true; // always show catheter probe
261 if (key.startsWith("dig_cardinal"))
262 return visibility.dig && visibility.digCardinal;
263 if (key.startsWith("dig_hpi"))
264 return visibility.dig && visibility.digHpi;
265 if (key.startsWith("dig_eeg"))
266 return visibility.dig && visibility.digEeg;
267 if (key.startsWith("dig_extra"))
268 return visibility.dig && visibility.digExtra;
269 if (key.startsWith("dig_"))
270 return visibility.dig;
271
272 if (key.startsWith("srcsp_"))
273 return visibility.sourceSpace;
274
275 return true;
276}
277
278//=============================================================================================================
279
281 QMap<QString, std::shared_ptr<BrainSurface>>& surfaces) const
282{
283 for (auto it = surfaces.begin(); it != surfaces.end(); ++it) {
284 if (matchesSurfaceType(it.key())) {
285 it.value()->setVisualizationMode(overlayMode);
286 }
287 }
288}
289
290//=============================================================================================================
291// Camera presets
292//=============================================================================================================
293
295{
296 return QQuaternion::fromEulerAngles(-45.0f, -40.0f, -130.0f);
297}
298
299//=============================================================================================================
300
301QString multiViewPresetName(int preset)
302{
303 switch (preset) {
304 case 0:
305 return QStringLiteral("Top");
306 case 1:
307 return QStringLiteral("Perspective");
308 case 2:
309 return QStringLiteral("Front");
310 case 3:
311 return QStringLiteral("Left");
312 case 4:
313 return QStringLiteral("Bottom");
314 case 5:
315 return QStringLiteral("Back");
316 case 6:
317 return QStringLiteral("Right");
318 default:
319 return QStringLiteral("Top");
320 }
321}
322
323//=============================================================================================================
324
325QQuaternion multiViewPresetOffset(int preset)
326{
327 switch (preset) {
328 case 0:
329 return QQuaternion::fromAxisAndAngle(0, 0, 1, 180);
330 case 1:
332 case 2:
333 return QQuaternion::fromAxisAndAngle(0, 1, 0, 180) * QQuaternion::fromAxisAndAngle(0, 0, 1, 180) * QQuaternion::fromAxisAndAngle(1, 0, 0, 90) * QQuaternion::fromAxisAndAngle(0, 0, 1, 180);
334 case 3:
335 return QQuaternion::fromAxisAndAngle(1, 0, 0, 90) * QQuaternion::fromAxisAndAngle(0, 1, 0, -90);
336 case 4:
337 return QQuaternion::fromAxisAndAngle(1, 0, 0, 180) * QQuaternion::fromAxisAndAngle(0, 0, 1, 180);
338 case 5:
339 return QQuaternion::fromAxisAndAngle(1, 0, 0, 90);
340 case 6:
341 return QQuaternion::fromAxisAndAngle(1, 0, 0, 90) * QQuaternion::fromAxisAndAngle(0, 1, 0, 90);
342 default:
343 return QQuaternion::fromAxisAndAngle(0, 0, 1, 180);
344 }
345}
346
347//=============================================================================================================
348
350{
351 return preset == 1;
352}
353
354//=============================================================================================================
355// SubView factory
356//=============================================================================================================
357
359{
360 static constexpr ShaderMode kShaderCycle[] = {
362 Standard,
364 };
365 static constexpr int kNumShaders = 3;
366 static constexpr int kNumPresets = 7; // Top..Right
367
368 SubView sv;
369 sv.preset = index % kNumPresets;
370 sv.brainShader = kShaderCycle[index % kNumShaders];
371 sv.enabled = true;
372 return sv;
373}
374
375//=============================================================================================================
376// Enum ↔ string conversion
377//=============================================================================================================
378
379int normalizedVisualizationTarget(int target, int maxIndex)
380{
381 return std::clamp(target, -1, maxIndex);
382}
383
384//=============================================================================================================
385
386ShaderMode shaderModeFromName(const QString& name)
387{
388 if (name == "Holographic")
389 return Holographic;
390 if (name == "Anatomical")
391 return Anatomical;
392 if (name == "XRay")
393 return XRay;
394 return Standard;
395}
396
397//=============================================================================================================
398
400{
401 if (mode == Holographic)
402 return QStringLiteral("Holographic");
403 if (mode == Anatomical)
404 return QStringLiteral("Anatomical");
405 return QStringLiteral("Standard");
406}
407
408//=============================================================================================================
409
411{
412 if (name == "Annotation")
413 return ModeAnnotation;
414 if (name == "Scientific")
415 return ModeScientific;
416 if (name == "Source Estimate")
417 return ModeSourceEstimate;
418 return ModeSurface;
419}
420
421//=============================================================================================================
422
424{
425 if (mode == ModeAnnotation)
426 return QStringLiteral("Annotation");
427 if (mode == ModeScientific)
428 return QStringLiteral("Scientific");
429 if (mode == ModeSourceEstimate)
430 return QStringLiteral("Source Estimate");
431 return QStringLiteral("Surface");
432}
433
434//=============================================================================================================
435// Colormap
436//=============================================================================================================
437
438QRgb mneAnalyzeColor(double v)
439{
440 double x = 2.0 * v - 1.0;
441 x = std::clamp(x, -1.0, 1.0);
442
443 static constexpr int N = 7;
444 static const double pos[N] = {-1.0, -0.90, -0.30, 0.0, 0.30, 0.90, 1.0};
445 static const double rr[N] = {0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0};
446 static const double gg[N] = {1.0, 0.0, 0.5, 0.5, 0.5, 0.0, 1.0};
447 static const double bb[N] = {1.0, 1.0, 0.5, 0.5, 0.5, 0.0, 0.0};
448
449 int seg = 0;
450 for (int i = 0; i < N - 1; ++i) {
451 if (x >= pos[i] && x <= pos[i + 1]) {
452 seg = i;
453 break;
454 }
455 }
456 if (x > pos[N - 1])
457 seg = N - 2;
458
459 double t = (pos[seg + 1] != pos[seg])
460 ? (x - pos[seg]) / (pos[seg + 1] - pos[seg])
461 : 0.0;
462 t = std::clamp(t, 0.0, 1.0);
463
464 int r = static_cast<int>(std::round((rr[seg] + t * (rr[seg + 1] - rr[seg])) * 255.0));
465 int g = static_cast<int>(std::round((gg[seg] + t * (gg[seg + 1] - gg[seg])) * 255.0));
466 int b = static_cast<int>(std::round((bb[seg] + t * (bb[seg + 1] - bb[seg])) * 255.0));
467
468 return qRgb(std::clamp(r, 0, 255), std::clamp(g, 0, 255), std::clamp(b, 0, 255));
469}
470
471//=============================================================================================================
472// SubView serialisation
473//=============================================================================================================
474
475void SubView::load(const QSettings& settings, const QString& prefix,
476 const QQuaternion& fallbackRotation)
477{
478 surfaceType = settings.value(prefix + "surfaceType", surfaceType).toString();
479 brainShader = shaderModeFromName(settings.value(prefix + "shader", shaderModeName(brainShader)).toString());
480 bemShader = shaderModeFromName(settings.value(prefix + "bemShader", shaderModeName(bemShader)).toString());
481 overlayMode = visualizationModeFromName(settings.value(prefix + "overlay", visualizationModeName(overlayMode)).toString());
482
483 visibility.load(settings, prefix + "vis_");
484
485 zoom = settings.value(prefix + "zoom", zoom).toFloat();
486 pan = QVector2D(
487 settings.value(prefix + "panX", pan.x()).toFloat(),
488 settings.value(prefix + "panY", pan.y()).toFloat());
489 preset = std::clamp(settings.value(prefix + "preset", preset).toInt(), 0, 6);
490
491 const bool hasQuat = settings.contains(prefix + "perspRotW") && settings.contains(prefix + "perspRotX") && settings.contains(prefix + "perspRotY") && settings.contains(prefix + "perspRotZ");
492 if (hasQuat) {
493 const float w = settings.value(prefix + "perspRotW", 1.0f).toFloat();
494 const float x = settings.value(prefix + "perspRotX", 0.0f).toFloat();
495 const float y = settings.value(prefix + "perspRotY", 0.0f).toFloat();
496 const float z = settings.value(prefix + "perspRotZ", 0.0f).toFloat();
497 perspectiveRotation = QQuaternion(w, x, y, z);
498 if (perspectiveRotation.lengthSquared() <= std::numeric_limits<float>::epsilon()) {
499 perspectiveRotation = QQuaternion();
500 } else {
501 perspectiveRotation.normalize();
502 }
503 } else {
504 perspectiveRotation = fallbackRotation;
505 }
506}
507
508//=============================================================================================================
509
510void SubView::save(QSettings& settings, const QString& prefix) const
511{
512 settings.setValue(prefix + "surfaceType", surfaceType);
513 settings.setValue(prefix + "shader", shaderModeName(brainShader));
514 settings.setValue(prefix + "bemShader", shaderModeName(bemShader));
515 settings.setValue(prefix + "overlay", visualizationModeName(overlayMode));
516
517 visibility.save(settings, prefix + "vis_");
518
519 settings.setValue(prefix + "zoom", zoom);
520 settings.setValue(prefix + "panX", pan.x());
521 settings.setValue(prefix + "panY", pan.y());
522 settings.setValue(prefix + "preset", preset);
523
524 settings.setValue(prefix + "perspRotW", perspectiveRotation.scalar());
525 settings.setValue(prefix + "perspRotX", perspectiveRotation.x());
526 settings.setValue(prefix + "perspRotY", perspectiveRotation.y());
527 settings.setValue(prefix + "perspRotZ", perspectiveRotation.z());
528}
529
530} // namespace DISP3DLIB
Qt-RHI scene renderer: shader pipelines, lighting, dual render targets and per-frame draw orchestrati...
Renderable cortical / BEM mesh with interleaved vertex attributes and Qt-RHI buffer management.
Per-viewport state (camera preset, zoom, pan, visibility filter) and serialisation helpers.
3-D brain visualisation using the Qt RHI rendering backend.
ShaderMode shaderModeFromName(const QString &name)
QString shaderModeName(ShaderMode mode)
QString visualizationModeName(VisualizationMode mode)
bool multiViewPresetIsPerspective(int preset)
QString multiViewPresetName(int preset)
QRgb mneAnalyzeColor(double v)
QQuaternion multiViewPresetOffset(int preset)
QQuaternion perspectivePresetRotation()
int normalizedVisualizationTarget(int target, int maxIndex)
bool isTrue(const QVariant &value, bool fallback)
Definition viewstate.h:345
VisualizationMode visualizationModeFromName(const QString &name)
@ ModeSourceEstimate
Definition rendertypes.h:85
void save(QSettings &settings, const QString &prefix) const
void setObjectVisible(const QString &object, bool visible)
Definition viewstate.cpp:86
bool isObjectVisible(const QString &object) const
Definition viewstate.cpp:33
void load(const QSettings &settings, const QString &prefix)
Viewport subdivision holding its own camera, projection, and scissor rectangle.
Definition viewstate.h:143
QQuaternion perspectiveRotation
Definition viewstate.h:154
void applyOverlayToSurfaces(QMap< QString, std::shared_ptr< BrainSurface > > &surfaces) const
ShaderMode brainShader
Definition viewstate.h:146
void save(QSettings &settings, const QString &prefix) const
static SubView defaultForIndex(int index)
bool matchesSurfaceType(const QString &key) const
bool shouldRenderSurface(const QString &key) const
ShaderMode bemShader
Definition viewstate.h:147
ViewVisibilityProfile visibility
Definition viewstate.h:149
VisualizationMode overlayMode
Definition viewstate.h:148
void load(const QSettings &settings, const QString &prefix, const QQuaternion &fallbackRotation=QQuaternion())
static bool isBrainSurfaceKey(const QString &key)