47#include <QResizeEvent>
51#include <QStandardItem>
52#include <QCoreApplication>
53#include <QElapsedTimer>
79 setMinimumSize(800, 600);
81 setAutoRenderTarget(
false);
83#if defined(WASMBUILD) || defined(__EMSCRIPTEN__)
85#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
87#elif defined(Q_OS_WIN)
88 setApi(Api::Direct3D11);
93 setMouseTracking(
true);
99 m_fpsLabel =
new QLabel(
this);
100 m_fpsLabel->setStyleSheet(
"color: white; font-weight: bold; font-family: monospace; font-size: 13px; background: transparent; padding: 5px;");
101 m_fpsLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
102 m_fpsLabel->setAlignment(Qt::AlignRight | Qt::AlignTop);
103 m_fpsLabel->setText(
"FPS: --.-\nVertices: 0");
104 m_fpsLabel->adjustSize();
105 m_fpsLabel->move(width() - m_fpsLabel->width() - 10, 10);
108 m_singleViewInfoLabel =
new QLabel(
this);
109 m_singleViewInfoLabel->setStyleSheet(
"color: white; font-family: monospace; font-size: 10px; background: rgba(0,0,0,110); border-radius: 3px; padding: 2px 4px;");
110 m_singleViewInfoLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
111 m_singleViewInfoLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
112 m_singleViewInfoLabel->setText(
"");
113 m_singleViewInfoLabel->adjustSize();
114 m_singleViewInfoLabel->hide();
118 m_regionLabel =
new QLabel(
this);
119 m_regionLabel->setStyleSheet(
"color: white; font-weight: bold; font-family: sans-serif; font-size: 16px; background: transparent; padding: 5px;");
120 m_regionLabel->setText(
"");
121 m_regionLabel->move(10, 10);
122 m_regionLabel->resize(300, 30);
123 m_regionLabel->hide();
126 m_subViews.resize(kDefaultViewportCount);
127 m_viewportNameLabels.resize(kDefaultViewportCount,
nullptr);
128 m_viewportInfoLabels.resize(kDefaultViewportCount,
nullptr);
129 for (
int i = 0; i < kDefaultViewportCount; ++i) {
132 m_viewportNameLabels[i] =
new QLabel(
this);
133 m_viewportNameLabels[i]->setStyleSheet(
"color: white; font-weight: bold; font-family: sans-serif; font-size: 12px; background: transparent; padding: 2px 4px;");
134 m_viewportNameLabels[i]->setAttribute(Qt::WA_TransparentForMouseEvents);
136 m_viewportNameLabels[i]->adjustSize();
137 m_viewportNameLabels[i]->hide();
139 m_viewportInfoLabels[i] =
new QLabel(
this);
140 m_viewportInfoLabels[i]->setStyleSheet(
"color: white; font-family: monospace; font-size: 10px; background: rgba(0,0,0,110); border-radius: 3px; padding: 2px 4px;");
141 m_viewportInfoLabels[i]->setAttribute(Qt::WA_TransparentForMouseEvents);
142 m_viewportInfoLabels[i]->setAlignment(Qt::AlignLeft | Qt::AlignTop);
143 m_viewportInfoLabels[i]->setText(
"");
144 m_viewportInfoLabels[i]->adjustSize();
145 m_viewportInfoLabels[i]->hide();
148 m_verticalSeparator =
new QFrame(
this);
149 m_verticalSeparator->setFrameShape(QFrame::NoFrame);
150 m_verticalSeparator->setAttribute(Qt::WA_TransparentForMouseEvents);
151 m_verticalSeparator->hide();
153 m_horizontalSeparator =
new QFrame(
this);
154 m_horizontalSeparator->setFrameShape(QFrame::NoFrame);
155 m_horizontalSeparator->setAttribute(Qt::WA_TransparentForMouseEvents);
156 m_horizontalSeparator->hide();
158 QColor sepColor = palette().color(QPalette::Midlight);
159 if (sepColor.alpha() == 255) {
160 sepColor.setAlpha(180);
162 const QString sepStyle = QString(
"background-color: rgba(%1,%2,%3,%4);")
164 .arg(sepColor.green())
165 .arg(sepColor.blue())
166 .arg(sepColor.alpha());
167 m_verticalSeparator->setStyleSheet(sepStyle);
168 m_horizontalSeparator->setStyleSheet(sepStyle);
170 loadMultiViewSettings();
171 updateViewportSeparators();
172 updateOverlayLayout();
176 QColor(200, 255, 255, 160));
180 this, &BrainView::onSourceEstimateLoaded);
188 this, &BrainView::onRealtimeColorsAvailable);
192 this, &BrainView::onSensorStreamColorsAvailable);
196 m_videoOverlay = std::make_unique<DISP3DLIB::VideoOverlay>();
203 saveMultiViewSettings();
222 m_cameraRotation = rotation;
223 saveMultiViewSettings();
224 m_sceneDirty =
true; update();
230 if (!m_model)
return;
232 for (
int i = first; i <= last; ++i) {
233 QModelIndex index = m_model->index(i, 0, parent);
234 QStandardItem* item = m_model->itemFromIndex(index);
241 auto brainSurf = std::make_shared<BrainSurface>();
247 if (absItem->parent()) {
248 QString parentText = absItem->parent()->text();
249 if (parentText ==
"lh") brainSurf->setHemi(0);
250 else if (parentText ==
"rh") brainSurf->setHemi(1);
254 brainSurf->setVisible(surfItem->
isVisible());
259 m_itemSurfaceMap[item] = brainSurf;
263 if (absItem->parent()) {
264 key = absItem->parent()->text() +
"_" + surfItem->text();
266 key = surfItem->text();
268 m_surfaces[key] = brainSurf;
276 if (!m_activeSurface) {
277 m_activeSurface = brainSurf;
278 m_activeSurfaceType = surfItem->text();
286 auto brainSurf = std::make_shared<BrainSurface>();
289 brainSurf->fromBemSurface(bemSurfData, bemItem->
color());
291 brainSurf->setVisible(bemItem->
isVisible());
294 QString surfName = bemItem->text().toLower();
295 if (surfName.contains(
"head") || surfName.contains(
"skin") || surfName.contains(
"scalp")) {
297 }
else if (surfName.contains(
"outer") && surfName.contains(
"skull")) {
299 }
else if (surfName.contains(
"inner") && surfName.contains(
"skull")) {
301 }
else if (surfName.contains(
"skull")) {
303 }
else if (surfName.contains(
"brain")) {
307 m_itemSurfaceMap[item] = brainSurf;
310 m_surfaces[
"bem_" + bemItem->text()] = brainSurf;
317 std::shared_ptr<BrainSurface> brainSurf;
319 QString parentText =
"";
320 if (sensItem->parent()) parentText = sensItem->parent()->text();
322 if (parentText.contains(
"MEG/Grad") && sensItem->
hasOrientation()) {
325 }
else if (parentText.contains(
"MEG/Mag") && sensItem->
hasOrientation()) {
335 m_itemSurfaceMap[item] = brainSurf;
339 if (!m_headToMriTrans.isEmpty()) {
341 if (m_applySensorTrans) {
344 brainSurf->applyTransform(m);
350 QString key = keyPrefix + sensItem->text() +
"_" + QString::number((quintptr)sensItem);
351 m_surfaces[key] = brainSurf;
359 auto dipObject = std::make_shared<DipoleObject>();
360 dipObject->load(dipItem->
ecdSet());
361 dipObject->setVisible(dipItem->
isVisible());
363 m_itemDipoleMap[item] = dipObject;
369 const QVector<QVector3D>& positions = srcItem->
positions();
370 if (positions.isEmpty())
continue;
374 brainSurf->setVisible(srcItem->
isVisible());
375 m_itemSurfaceMap[item] = brainSurf;
377 QString key =
"srcsp_" + srcItem->text();
378 m_surfaces[key] = brainSurf;
384 const QVector<QVector3D>& positions = digItem->
positions();
385 if (positions.isEmpty())
continue;
389 brainSurf->setVisible(digItem->
isVisible());
392 if (!m_headToMriTrans.isEmpty()) {
394 if (m_applySensorTrans) {
397 brainSurf->applyTransform(m);
400 m_itemSurfaceMap[item] = brainSurf;
407 static int s_digKeyCounter = 0;
414 default: catName = digItem->text().toLower().replace(
' ',
'_');
break;
416 QString key = QStringLiteral(
"dig_%1_%2").arg(catName).arg(s_digKeyCounter++);
417 m_surfaces[key] = brainSurf;
422 if (m_model->hasChildren(index)) {
426 updateInflatedSurfaceTransforms();
428 m_vertexCountDirty =
true;
429 m_sceneDirty =
true; update();
435 for (
int i = topLeft.row(); i <= bottomRight.row(); ++i) {
436 QModelIndex index = m_model->index(i, 0, topLeft.parent());
437 QStandardItem* item = m_model->itemFromIndex(index);
439 if (m_itemSurfaceMap.contains(item)) {
440 auto surf = m_itemSurfaceMap[item];
446 m_vertexCountDirty =
true;
461 m_sceneDirty =
true; update();
468 subViewForTarget(m_visualizationEditTarget).surfaceType = type;
470 m_activeSurfaceType = type;
473 QString key =
"lh_" + type;
474 if (m_surfaces.contains(key)) m_activeSurface = m_surfaces[key];
477 if (m_surfaces.contains(key)) m_activeSurface = m_surfaces[key];
480 updateInflatedSurfaceTransforms();
481 saveMultiViewSettings();
484 m_vertexCountDirty =
true;
485 m_sceneDirty =
true; update();
488void BrainView::updateSceneBounds()
490 QVector3D min(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
491 QVector3D max(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
492 bool hasContent =
false;
495 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
496 if (it.value()->isVisible()) {
497 QVector3D sMin, sMax;
498 it.value()->boundingBox(sMin, sMax);
500 min.setX(std::min(min.x(), sMin.x()));
501 min.setY(std::min(min.y(), sMin.y()));
502 min.setZ(std::min(min.z(), sMin.z()));
504 max.setX(std::max(max.x(), sMax.x()));
505 max.setY(std::max(max.y(), sMax.y()));
506 max.setZ(std::max(max.z(), sMax.z()));
512 for (
auto it = m_itemDipoleMap.begin(); it != m_itemDipoleMap.end(); ++it) {
513 if (it.value()->isVisible()) {
522 m_sceneCenter = (min + max) * 0.5f;
524 QVector3D diag = max - min;
525 m_sceneSize = std::max(diag.x(), std::max(diag.y(), diag.z()));
528 if (m_sceneSize < 0.01f) m_sceneSize = 0.3f;
532 m_sceneCenter = QVector3D(0,0,0);
542 subViewForTarget(m_visualizationEditTarget).brainShader = mode;
544 m_brainShaderMode = mode;
545 saveMultiViewSettings();
546 m_sceneDirty =
true; update();
554 const int prev = m_visualizationEditTarget;
557 const SubView &sv = subViewForTarget(m_visualizationEditTarget);
564 const bool remapMegSurface = (m_fieldMapper.megFieldMapOnHead() != visibility.
megFieldMapOnHead);
566 m_dipolesVisible = visibility.
dipoles;
567 m_networkVisible = visibility.
network;
575 if (m_fieldMapper.isLoaded()) {
576 if (remapMegSurface) {
577 m_fieldMapper.buildMapping(m_surfaces, m_headToMriTrans, m_applySensorTrans);
579 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
583 updateViewportLabelHighlight();
585 saveMultiViewSettings();
587 if (prev != m_visualizationEditTarget) {
596 return m_visualizationEditTarget;
603 return subViewForTarget(target).surfaceType;
638 return subViewForTarget(target).visibility;
643SubView& BrainView::subViewForTarget(
int target)
646 return (normalized < 0) ? m_singleView : m_subViews[normalized];
651const SubView& BrainView::subViewForTarget(
int target)
const
654 return (normalized < 0) ? m_singleView : m_subViews[normalized];
666 return visibilityProfileForTarget(target).isObjectVisible(
object);
673 return visibilityProfileForTarget(target).megFieldMapOnHead;
678void BrainView::updateInflatedSurfaceTransforms()
680 const bool needsInflated = (m_singleView.
surfaceType ==
"inflated")
681 || std::any_of(m_subViews.cbegin(), m_subViews.cend(),
682 [](
const SubView &sv) { return sv.surfaceType ==
"inflated"; });
684 const QString lhKey =
"lh_inflated";
685 const QString rhKey =
"rh_inflated";
687 if (!m_surfaces.contains(lhKey) || !m_surfaces.contains(rhKey)) {
691 auto lhSurf = m_surfaces[lhKey];
692 auto rhSurf = m_surfaces[rhKey];
695 lhSurf->applyTransform(identity);
696 rhSurf->applyTransform(identity);
698 if (!needsInflated) {
702 const float lhMaxX = lhSurf->maxX();
703 const float rhMinX = rhSurf->minX();
705 const float gap = 0.005f;
706 const float lhOffset = -gap / 2.0f - lhMaxX;
707 const float rhOffset = gap / 2.0f - rhMinX;
709 lhSurf->translateX(lhOffset);
710 rhSurf->translateX(rhOffset);
717 subViewForTarget(m_visualizationEditTarget).bemShader = mode;
719 m_bemShaderMode = mode;
720 saveMultiViewSettings();
721 m_sceneDirty =
true; update();
728 m_singleView.bemShader = m_singleView.brainShader;
729 for (
int i = 0; i < m_subViews.size(); ++i) {
730 m_subViews[i].bemShader = m_subViews[i].brainShader;
733 m_bemShaderMode = subViewForTarget(m_visualizationEditTarget).bemShader;
735 saveMultiViewSettings();
736 m_sceneDirty =
true; update();
742 if (
object.isEmpty())
return;
744 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
745 profile.setObjectVisible(
object, visible);
750 if (type == QLatin1String(
"MEG")) {
751 profile.sensMegGrad = visible;
752 profile.sensMegMag = visible;
753 }
else if (type == QLatin1String(
"EEG")) {
755 }
else if (type == QLatin1String(
"Digitizer")) {
756 profile.digCardinal = visible;
757 profile.digHpi = visible;
758 profile.digEeg = visible;
759 profile.digExtra = visible;
762 saveMultiViewSettings();
763 m_sceneDirty =
true; update();
768 if (m_applySensorTrans != enabled) {
769 m_applySensorTrans = enabled;
770 refreshSensorTransforms();
771 m_sceneDirty =
true; update();
779 m_megHelmetOverridePath = path;
784 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
785 profile.dipoles = visible;
786 m_dipolesVisible = visible;
787 saveMultiViewSettings();
788 m_sceneDirty =
true; update();
796 SubView &sv = subViewForTarget(m_visualizationEditTarget);
799 m_currentVisMode = mode;
805 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
806 const QString &key = it.key();
807 if (key.startsWith(
"lh_") || key.startsWith(
"rh_")) {
808 it.value()->setVisualizationMode(mode);
812 saveMultiViewSettings();
813 m_sceneDirty =
true; update();
820 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
822 profile.lh = visible;
823 }
else if (hemiIdx == 1) {
824 profile.rh = visible;
826 saveMultiViewSettings();
827 m_sceneDirty =
true; update();
834 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
835 profile.setObjectVisible(
"bem_" + name, visible);
836 saveMultiViewSettings();
837 m_sceneDirty =
true; update();
842 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
843 if (it.key().startsWith(
"bem_")) {
844 it.value()->setUseDefaultColor(enabled);
847 m_sceneDirty =
true; update();
854 m_lightingEnabled = enabled;
855 m_sceneDirty =
true; update();
862 QImage img = grabFramebuffer();
863 QString fileName = QString(
"snapshot_refactor_%1.png").arg(m_snapshotCounter++, 4, 10, QChar(
'0'));
871 const QString &surfaceType)
876 setAttribute(Qt::WA_DontShowOnScreen,
true);
877 resize(width, height);
881 m_singleView.surfaceType = surfaceType;
890 while (timer.elapsed() < 5000) {
891 QCoreApplication::processEvents(QEventLoop::AllEvents, 16);
892 if (m_renderer && !m_surfaces.isEmpty())
898 setAttribute(Qt::WA_DontShowOnScreen,
false);
907 for (
int i = 0; i < 5; ++i)
908 QCoreApplication::processEvents(QEventLoop::AllEvents, 16);
912 QImage img = grabFramebuffer();
915 setAttribute(Qt::WA_DontShowOnScreen,
false);
920 return img.save(path,
"PNG");
928 m_isDraggingSplitter =
false;
931 saveMultiViewSettings();
932 updateViewportSeparators();
933 updateOverlayLayout();
934 m_sceneDirty =
true; update();
942 saveMultiViewSettings();
943 updateViewportSeparators();
944 updateOverlayLayout();
945 m_sceneDirty =
true; update();
952 count = std::clamp(count, 1,
static_cast<int>(m_subViews.size()));
957 m_isDraggingSplitter =
false;
964 if (m_visualizationEditTarget < 0)
969 for (
int i = 0; i < m_subViews.size(); ++i)
970 m_subViews[i].enabled = (i < count);
972 saveMultiViewSettings();
973 updateViewportSeparators();
974 updateOverlayLayout();
975 m_sceneDirty =
true; update();
983 m_layout.resetSplits();
984 m_multiSplitX = m_layout.splitX();
985 m_multiSplitY = m_layout.splitY();
986 saveMultiViewSettings();
987 updateViewportSeparators();
988 updateOverlayLayout();
989 m_sceneDirty =
true; update();
994 if (index < 0 || index >= m_subViews.size()) {
998 return m_subViews[index].enabled;
1003int BrainView::enabledViewportCount()
const
1010 for (
int i = 0; i < m_subViews.size(); ++i) {
1011 if (m_subViews[i].enabled) {
1016 return numEnabled > 0 ? numEnabled : 1;
1021QVector<int> BrainView::enabledViewportIndices()
const
1025 for (
int i = 0; i < m_subViews.size(); ++i) {
1026 if (m_subViews[i].enabled)
1039int BrainView::viewportIndexAt(
const QPoint& pos)
const
1045 const auto enabledViewports = enabledViewportIndices();
1046 return m_layout.viewportIndexAt(pos, enabledViewports, size());
1051QRect BrainView::multiViewSlotRect(
int slot,
int numEnabled,
const QSize& outputSize)
const
1053 return m_layout.slotRect(slot, numEnabled, outputSize);
1058SplitterHit BrainView::hitTestSplitter(
const QPoint& pos,
int numEnabled,
const QSize& outputSize)
const
1060 if (m_viewMode !=
MultiView || numEnabled <= 1) {
1063 return m_layout.hitTestSplitter(pos, numEnabled, outputSize);
1068void BrainView::updateSplitterCursor(
const QPoint& pos)
1070 const SplitterHit hit = hitTestSplitter(pos, enabledViewportCount(), size());
1072 if (shape == Qt::ArrowCursor) {
1081void BrainView::updateViewportSeparators()
1083 if (!m_verticalSeparator || !m_horizontalSeparator) {
1087 m_verticalSeparator->hide();
1088 m_horizontalSeparator->hide();
1090 const int numEnabled = enabledViewportCount();
1091 if (m_viewMode !=
MultiView || numEnabled <= 1) {
1096 m_layout.separatorGeometries(numEnabled, size(), vRect, hRect);
1098 if (!vRect.isEmpty()) {
1099 m_verticalSeparator->setGeometry(vRect);
1100 m_verticalSeparator->show();
1101 m_verticalSeparator->raise();
1103 if (!hRect.isEmpty()) {
1104 m_horizontalSeparator->setGeometry(hRect);
1105 m_horizontalSeparator->show();
1106 m_horizontalSeparator->raise();
1109 updateOverlayLayout();
1114void BrainView::updateOverlayLayout()
1116 const auto enabledViewports = enabledViewportIndices();
1119 m_fpsLabel->setVisible(m_infoPanelVisible);
1120 m_fpsLabel->adjustSize();
1121 const int perfBottomMargin = 2;
1124 m_fpsLabel->move(width() - m_fpsLabel->width() - 10,
1125 height() - m_fpsLabel->height() - perfBottomMargin);
1127 m_fpsLabel->move(width() - m_fpsLabel->width() - 10,
1128 height() - m_fpsLabel->height() - perfBottomMargin);
1131 m_fpsLabel->raise();
1134 if (m_singleViewInfoLabel) {
1135 const bool showSingleInfo = (m_viewMode ==
SingleView) && m_infoPanelVisible;
1136 m_singleViewInfoLabel->setVisible(showSingleInfo);
1137 if (showSingleInfo) {
1138 m_singleViewInfoLabel->adjustSize();
1139 m_singleViewInfoLabel->move(width() - m_singleViewInfoLabel->width() - 8, 8);
1140 m_singleViewInfoLabel->raise();
1144 if (m_regionLabel) {
1145 const int regionY = (m_viewMode ==
MultiView) ? 38 : 10;
1146 m_regionLabel->move(10, regionY);
1147 if (!m_regionLabel->text().isEmpty()) {
1148 m_regionLabel->raise();
1152 for (
int i = 0; i < m_viewportNameLabels.size(); ++i) {
1153 if (m_viewportNameLabels[i]) {
1154 m_viewportNameLabels[i]->hide();
1156 if (m_viewportInfoLabels[i]) {
1157 m_viewportInfoLabels[i]->hide();
1165 const int numEnabled = enabledViewports.size();
1166 const QSize overlaySize = size();
1167 for (
int slot = 0; slot < numEnabled; ++slot) {
1168 const int vp = enabledViewports[slot];
1169 QLabel* label = m_viewportNameLabels[vp];
1170 QLabel* infoLabel = m_viewportInfoLabels[vp];
1175 const int preset = std::clamp(m_subViews[vp].preset, 0, 6);
1178 const QRect pane = multiViewSlotRect(slot, numEnabled, overlaySize);
1179 label->adjustSize();
1180 label->move(pane.x() + 8, pane.y() + 8);
1181 label->setVisible(
true);
1185 infoLabel->adjustSize();
1186 infoLabel->move(pane.x() + pane.width() - infoLabel->width() - 8,
1188 infoLabel->setVisible(m_infoPanelVisible);
1193 updateViewportLabelHighlight();
1198void BrainView::updateViewportLabelHighlight()
1200 static const QString normalStyle =
1201 QStringLiteral(
"color: white; font-weight: bold; font-family: sans-serif; "
1202 "font-size: 12px; background: transparent; padding: 2px 4px;");
1203 static const QString selectedStyle =
1204 QStringLiteral(
"color: #FFD54F; font-weight: bold; font-family: sans-serif; "
1205 "font-size: 13px; background: rgba(255,213,79,40); "
1206 "border: 1px solid #FFD54F; border-radius: 3px; padding: 2px 6px;");
1208 for (
int i = 0; i < m_viewportNameLabels.size(); ++i) {
1209 if (!m_viewportNameLabels[i])
continue;
1210 const bool selected = (m_viewMode ==
MultiView && m_visualizationEditTarget == i);
1211 m_viewportNameLabels[i]->setStyleSheet(selected ? selectedStyle : normalStyle);
1212 m_viewportNameLabels[i]->adjustSize();
1218void BrainView::logPerspectiveRotation(
const QString& context)
const
1225void BrainView::loadMultiViewSettings()
1228 settings.beginGroup(QStringLiteral(
"BrainView"));
1230 m_multiSplitX = settings.value(
"multiSplitX", 0.5f).toFloat();
1231 m_multiSplitY = settings.value(
"multiSplitY", 0.5f).toFloat();
1233 const int savedViewMode = settings.value(
"viewMode",
static_cast<int>(
SingleView)).toInt();
1235 m_viewCount = std::clamp(settings.value(
"viewCount", 1).toInt(), 1,
static_cast<int>(m_subViews.size()));
1237 if (m_viewCount > 1) m_viewMode =
MultiView;
1240 const bool hasCameraQuat = settings.contains(
"cameraRotW")
1241 && settings.contains(
"cameraRotX")
1242 && settings.contains(
"cameraRotY")
1243 && settings.contains(
"cameraRotZ");
1244 if (hasCameraQuat) {
1245 const float w = settings.value(
"cameraRotW", 1.0f).toFloat();
1246 const float x = settings.value(
"cameraRotX", 0.0f).toFloat();
1247 const float y = settings.value(
"cameraRotY", 0.0f).toFloat();
1248 const float z = settings.value(
"cameraRotZ", 0.0f).toFloat();
1249 m_cameraRotation = QQuaternion(w, x, y, z);
1250 if (m_cameraRotation.lengthSquared() <= std::numeric_limits<float>::epsilon()) {
1251 m_cameraRotation = QQuaternion();
1253 m_cameraRotation.normalize();
1258 for (
int i = 0; i < m_subViews.size(); ++i) {
1260 m_subViews[i].enabled = (i < m_viewCount);
1264 m_singleView.load(settings,
"single_", m_cameraRotation);
1265 for (
int i = 0; i < m_subViews.size(); ++i)
1266 m_subViews[i].load(settings, QStringLiteral(
"multi%1_").arg(i), m_cameraRotation);
1268 const int maxIdx =
static_cast<int>(m_subViews.size()) - 1;
1270 settings.value(
"visualizationEditTarget", -1).toInt(), maxIdx);
1272 m_infoPanelVisible = settings.value(
"infoPanelVisible",
true).toBool();
1274 settings.endGroup();
1276 m_multiSplitX = std::clamp(m_multiSplitX, 0.15f, 0.85f);
1277 m_multiSplitY = std::clamp(m_multiSplitY, 0.15f, 0.85f);
1278 m_layout.setSplitX(m_multiSplitX);
1279 m_layout.setSplitY(m_multiSplitY);
1288 QTimer::singleShot(0,
this, [
this]() {
1296void BrainView::saveMultiViewSettings()
const
1299 settings.beginGroup(QStringLiteral(
"BrainView"));
1300 settings.setValue(
"multiSplitX", m_multiSplitX);
1301 settings.setValue(
"multiSplitY", m_multiSplitY);
1302 settings.setValue(
"viewMode",
static_cast<int>(m_viewMode));
1303 settings.setValue(
"viewCount", m_viewCount);
1304 settings.setValue(
"cameraRotW", m_cameraRotation.scalar());
1305 settings.setValue(
"cameraRotX", m_cameraRotation.x());
1306 settings.setValue(
"cameraRotY", m_cameraRotation.y());
1307 settings.setValue(
"cameraRotZ", m_cameraRotation.z());
1308 for (
int i = 0; i < m_subViews.size(); ++i)
1309 settings.setValue(QStringLiteral(
"viewportEnabled%1").arg(i), m_subViews[i].enabled);
1310 settings.setValue(
"visualizationEditTarget", m_visualizationEditTarget);
1311 settings.setValue(
"infoPanelVisible", m_infoPanelVisible);
1314 m_singleView.save(settings,
"single_");
1315 for (
int i = 0; i < m_subViews.size(); ++i)
1316 m_subViews[i].save(settings, QStringLiteral(
"multi%1_").arg(i));
1318 settings.endGroup();
1325 if (index >= 0 && index < m_subViews.size()) {
1326 m_subViews[index].enabled = enabled;
1327 saveMultiViewSettings();
1328 updateViewportSeparators();
1329 updateOverlayLayout();
1330 m_sceneDirty =
true; update();
1338 if (index < 0 || index >=
static_cast<int>(m_subViews.size()))
1340 preset = std::clamp(preset, 0, 6);
1341 if (m_subViews[index].preset == preset)
1343 m_subViews[index].preset = preset;
1344 saveMultiViewSettings();
1345 updateOverlayLayout();
1346 m_sceneDirty =
true; update();
1353 m_cameraRotation = QQuaternion();
1355 saveMultiViewSettings();
1356 m_sceneDirty =
true; update();
1363 if (index < 0 || index >=
static_cast<int>(m_subViews.size())) {
1367 m_subViews[index].zoom = 0.0f;
1368 m_subViews[index].pan = QVector2D();
1369 m_subViews[index].perspectiveRotation = QQuaternion();
1370 saveMultiViewSettings();
1371 m_sceneDirty =
true; update();
1379 for (
int i = 0; i < m_subViews.size(); ++i) {
1380 const bool wasEnabled = m_subViews[i].enabled;
1382 m_subViews[i].enabled = wasEnabled;
1384 m_cameraRotation = QQuaternion();
1386 saveMultiViewSettings();
1387 updateOverlayLayout();
1388 m_sceneDirty =
true; update();
1395 if (index < 0 || index >=
static_cast<int>(m_subViews.size()))
1397 return std::clamp(m_subViews[index].preset, 0, 6);
1404 m_infoPanelVisible = visible;
1405 saveMultiViewSettings();
1406 updateOverlayLayout();
1413 QRhiWidget::resizeEvent(event);
1414 updateViewportSeparators();
1415 updateOverlayLayout();
1424 m_renderer = std::make_unique<BrainRenderer>();
1429 m_renderer->ensureRenderTargets(rhi(), colorTexture(), colorTexture()->pixelSize());
1437 bool hasSurfaces = !m_surfaces.isEmpty();
1438 bool hasDipoles = !m_itemDipoleMap.isEmpty() || m_dipoles;
1441 if (!hasSurfaces && !hasDipoles) {
1444 m_renderer = std::make_unique<BrainRenderer>();
1446 m_renderer->ensureRenderTargets(rhi(), colorTexture(), colorTexture()->pixelSize());
1447 m_renderer->initialize(rhi(), m_renderer->rtClear()->renderPassDescriptor(), sampleCount());
1448 m_renderer->beginFrame(cb);
1449 m_renderer->endPass(cb);
1454 if (!m_activeSurface && !m_surfaces.isEmpty()) {
1455 m_activeSurface = m_surfaces.begin().value();
1460 if (m_fpsTimer.elapsed() >= 500) {
1461 float fps = m_frameCount / (m_fpsTimer.elapsed() / 1000.0f);
1464 if (m_vertexCountDirty) {
1465 auto countVerticesForSubView = [
this](
const SubView &sv) -> qint64 {
1467 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
1468 const QString &key = it.key();
1469 auto surface = it.value();
1470 if (!surface)
continue;
1475 if (!surface->isVisible())
continue;
1477 total += surface->vertexCount();
1484 for (
int vp : enabledViewportIndices()) {
1485 vCount += countVerticesForSubView(m_subViews[vp]);
1488 vCount = countVerticesForSubView(m_singleView);
1490 m_cachedVertexCount = vCount;
1491 m_vertexCountDirty =
false;
1494 m_fpsLabel->setText(QString(
"FPS: %1\nVertices: %2").arg(fps, 0,
'f', 1).arg(m_cachedVertexCount));
1495 updateOverlayLayout();
1496 m_fpsLabel->raise();
1498 m_fpsTimer.restart();
1502 m_renderer->ensureRenderTargets(rhi(), colorTexture(), colorTexture()->pixelSize());
1503 m_renderer->initialize(rhi(), m_renderer->rtClear()->renderPassDescriptor(), sampleCount());
1506 QSize outputSize = m_renderer->rtClear()->pixelSize();
1509 const auto enabledViewports = enabledViewportIndices();
1510 int numEnabled = enabledViewports.size();
1530 QRhiResourceUpdateBatch *preUpload = rhi()->nextResourceUpdateBatch();
1531 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1532#ifdef __EMSCRIPTEN__
1539 it.value()->updateBuffers(rhi(), preUpload);
1541#ifndef __EMSCRIPTEN__
1542 if (m_debugPointerSurface) {
1543 m_debugPointerSurface->updateBuffers(rhi(), preUpload);
1545 for (
auto it = m_itemDipoleMap.begin(); it != m_itemDipoleMap.end(); ++it) {
1546 it.value()->updateBuffers(rhi(), preUpload);
1549 m_dipoles->updateBuffers(rhi(), preUpload);
1552 if (m_videoOverlay && m_videoOverlay->isEnabled()) {
1553 m_renderer->prepareVideoOverlay(rhi(), preUpload, m_videoOverlay.get());
1557 for (
int i = 0; i < kMaxSliceSlots; ++i) {
1558 if (m_slices[i] && m_sliceVisible[i]) {
1559 m_renderer->prepareSlice(rhi(), preUpload, m_slices[i], i);
1561 m_renderer->prepareSlice(rhi(), preUpload,
nullptr, i);
1565#ifdef __EMSCRIPTEN__
1572 const SubView &sv = (m_viewMode ==
MultiView) ? m_subViews[0] : m_singleView;
1574 QVector<BrainSurface*> allSurfaces;
1577 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1581 allSurfaces.append(it.value().get());
1585 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1586 if (!it.key().startsWith(
"srcsp_"))
continue;
1588 if (!it.value()->isVisible())
continue;
1589 allSurfaces.append(it.value().get());
1593 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1594 if (!it.key().startsWith(
"dig_"))
continue;
1596 if (!it.value()->isVisible())
continue;
1597 allSurfaces.append(it.value().get());
1601 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1602 bool isSensor = it.key().startsWith(
"sens_");
1603 bool isBem = it.key().startsWith(
"bem_");
1604 if (!isSensor && !isBem)
continue;
1606 if (!it.value()->isVisible())
continue;
1607 allSurfaces.append(it.value().get());
1610 m_renderer->prepareMergedSurfaces(rhi(), preUpload, allSurfaces, QStringLiteral(
"default"));
1615 cb->resourceUpdate(preUpload);
1619 m_renderer->beginFrame(cb);
1621 for (
int slot = 0; slot < numEnabled; ++slot) {
1622 int vp = (m_viewMode ==
MultiView) ? enabledViewports[slot] : 0;
1623 const SubView &sv = (m_viewMode ==
MultiView) ? m_subViews[vp] : m_singleView;
1624 const int preset = (m_viewMode ==
MultiView) ? std::clamp(sv.
preset, 0, 6) : 1;
1626 const QRect paneRect = (m_viewMode ==
MultiView)
1627 ? multiViewSlotRect(slot, numEnabled, outputSize)
1628 : QRect(0, 0, outputSize.width(), outputSize.height());
1630 QRect renderRect = paneRect;
1631 if (m_viewMode ==
MultiView && numEnabled > 1) {
1632 constexpr int separatorPx = 2;
1634 if (numEnabled == 2) {
1636 renderRect.setWidth(std::max(1, renderRect.width() - separatorPx));
1638 }
else if (numEnabled == 3) {
1642 renderRect.setHeight(std::max(1, renderRect.height() - separatorPx));
1643 }
else if (slot == 1) {
1645 renderRect.setWidth(std::max(1, renderRect.width() - separatorPx));
1649 const int col = slot % 2;
1650 const int row = slot / 2;
1652 const bool hasRightNeighbor = (col == 0)
1653 && (slot + 1 < numEnabled)
1654 && ((slot / 2) == ((slot + 1) / 2));
1655 const bool hasBottomNeighbor = (row == 0)
1656 && (slot + 2 < numEnabled);
1658 if (hasRightNeighbor) {
1659 renderRect.setWidth(std::max(1, renderRect.width() - separatorPx));
1661 if (hasBottomNeighbor) {
1662 renderRect.setHeight(std::max(1, renderRect.height() - separatorPx));
1667 const int viewX = renderRect.x();
1668 const int viewY = outputSize.height() - (renderRect.y() + renderRect.height());
1669 const int viewW = std::max(1, renderRect.width());
1670 const int viewH = std::max(1, renderRect.height());
1672 QRhiViewport viewport(viewX, viewY, viewW, viewH);
1673 QRhiScissor scissor(viewX, viewY, viewW, viewH);
1674 const float aspectRatio = float(viewW) / float(viewH);
1677 cb->setViewport(viewport);
1678 cb->setScissor(scissor);
1681 const QVector3D effectiveCenter = m_cameraFocusOverride ? m_cameraFocusCenter : m_sceneCenter;
1682 const float effectiveSize = m_cameraFocusOverride ? m_cameraFocusSize : m_sceneSize;
1683 m_camera.setSceneCenter(effectiveCenter);
1684 m_camera.setSceneSize(effectiveSize);
1685 m_camera.setRotation(m_cameraRotation);
1686 m_camera.setZoom(m_zoom);
1688 ? m_camera.computeMultiView(sv, aspectRatio)
1689 : m_camera.computeSingleView(aspectRatio);
1692 sceneData.
mvp = rhi()->clipSpaceCorrMatrix();
1721#ifndef __EMSCRIPTEN__
1722 QStringList drawnKeys;
1724 const QString drawnInfo = QStringLiteral(
"merged");
1727 if (m_viewMode ==
MultiView && m_viewportInfoLabels[vp]) {
1728 m_viewportInfoLabels[vp]->setText(
1729 QString(
"Shader: %1\nSurface: %2\nOverlay: %3")
1731 }
else if (m_viewMode ==
SingleView && m_singleViewInfoLabel) {
1732 m_singleViewInfoLabel->setText(
1733 QString(
"Shader: %1\nSurface: %2\nOverlay: %3")
1737 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1741#ifdef __EMSCRIPTEN__
1746#ifndef __EMSCRIPTEN__
1747 drawnKeys << it.key();
1749 m_renderer->renderSurface(cb, rhi(), sceneData, it.value().get(), currentShader);
1752#ifndef __EMSCRIPTEN__
1755 const QString drawnInfo = drawnKeys.isEmpty() ? QStringLiteral(
"none") : drawnKeys.join(QStringLiteral(
", "));
1756 if (m_viewMode ==
MultiView && m_viewportInfoLabels[vp]) {
1757 m_viewportInfoLabels[vp]->setText(m_viewportInfoLabels[vp]->text()
1758 + QStringLiteral(
"\nDrawn: ") + drawnInfo);
1759 }
else if (m_viewMode ==
SingleView && m_singleViewInfoLabel) {
1760 m_singleViewInfoLabel->setText(m_singleViewInfoLabel->text()
1761 + QStringLiteral(
"\nDrawn: ") + drawnInfo);
1766#ifdef __EMSCRIPTEN__
1777 m_renderer->drawMergedSurfaces(cb, rhi(), sceneData, currentShader, QStringLiteral(
"default"));
1790 const QString &megFieldKey = m_fieldMapper.megSurfaceKey();
1791 const QString &eegFieldKey = m_fieldMapper.eegSurfaceKey();
1801 QVector<DrawItem> opaqueDraws;
1802 QVector<DrawItem> transparentDraws;
1804 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1805 const QString &key = it.key();
1811 }
else if (key.startsWith(
"srcsp_") || key.startsWith(
"dig_")) {
1814 if (key.startsWith(QLatin1String(
"dig_live_t_"))
1815 || key.startsWith(QLatin1String(
"dig_ray_"))
1816 || key.startsWith(QLatin1String(
"dig_probe_"))) {
1817 QVector3D bmin, bmax;
1819 QVector3D ctr = (bmin + bmax) * 0.5f;
1820 float dist = (sceneData.
cameraPos - ctr).lengthSquared();
1825 opaqueDraws.append({surf, currentShader,
1830 bool isSensor = key.startsWith(
"sens_");
1831 bool isBem = key.startsWith(
"bem_");
1832 if (!isSensor && !isBem)
continue;
1836 QVector3D bmin, bmax;
1838 QVector3D center = (bmin + bmax) * 0.5f;
1839 float dist = (sceneData.
cameraPos - center).lengthSquared();
1843 if (key == megFieldKey && !megFieldVisible)
1845 else if (key == eegFieldKey && !eegFieldVisible)
1848 transparentDraws.append({surf, mode, itemOverlay, dist, -1});
1853 std::sort(transparentDraws.begin(), transparentDraws.end(),
1854 [](
const DrawItem &a,
const DrawItem &b) { return a.distSq > b.distSq; });
1857 QRhiResourceUpdateBatch *surfBatch = rhi()->nextResourceUpdateBatch();
1860 for (
auto &item : opaqueDraws) {
1862 item.uniformOffset = m_renderer->prepareSurfaceDraw(surfBatch, batchData, item.surface);
1864 for (
auto &item : transparentDraws) {
1866 item.uniformOffset = m_renderer->prepareSurfaceDraw(surfBatch, batchData, item.surface);
1870 int sliceOffsets[kMaxSliceSlots] = {-1, -1, -1};
1872 for (
int i = 0; i < kMaxSliceSlots; ++i) {
1873 if (m_slices[i] && m_sliceVisible[i]) {
1874 sliceOffsets[i] = m_renderer->prepareSliceDraw(surfBatch, sceneData, i);
1879 cb->resourceUpdate(surfBatch);
1882 cb->setViewport(viewport);
1883 cb->setScissor(scissor);
1886 for (
const auto &item : opaqueDraws)
1887 m_renderer->issueSurfaceDraw(cb, item.surface, item.mode, item.uniformOffset);
1894 for (
int i = 0; i < kMaxSliceSlots; ++i) {
1895 if (sliceOffsets[i] >= 0) {
1896 m_renderer->issueSliceDraw(cb, i, sliceOffsets[i]);
1901 const bool hasVideoOverlay = m_videoOverlay
1902 && m_videoOverlay->isEnabled()
1903 && m_videoOverlay->hasFrame();
1904 if (hasVideoOverlay) {
1906 if (m_surfaces.contains(QStringLiteral(
"bem_head"))) {
1907 videoOverlayTargetSurface = m_surfaces[QStringLiteral(
"bem_head")].get();
1909 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1911 videoOverlayTargetSurface = it.value().get();
1918 bool videoOverlayDrawn =
false;
1919 auto drawVideoOverlay = [&]() {
1920 if (videoOverlayDrawn)
return;
1921 if (videoOverlayTargetSurface) {
1922 m_renderer->renderVideoOverlayOnSurface(cb, rhi(), sceneData,
1923 m_videoOverlay.get(), videoOverlayTargetSurface);
1924 }
else if (hasVideoOverlay) {
1925 m_renderer->renderVideoOverlay(cb, rhi(), sceneData, m_videoOverlay.get());
1927 videoOverlayDrawn =
true;
1930 for (
const auto &item : transparentDraws) {
1931 m_renderer->issueSurfaceDraw(cb, item.surface, item.mode, item.uniformOffset);
1932 if (item.surface == videoOverlayTargetSurface) {
1939 for(
auto it = m_itemDipoleMap.begin(); it != m_itemDipoleMap.end(); ++it) {
1941 m_renderer->renderDipoles(cb, rhi(), sceneData, it.value().get());
1946 m_renderer->renderDipoles(cb, rhi(), sceneData, m_dipoles.get());
1951 m_renderer->renderNetwork(cb, rhi(), sceneData, m_network.get());
1955 if (m_hasIntersection && m_debugPointerSurface) {
1959 QMatrix4x4 translation;
1960 translation.translate(m_lastIntersectionPoint);
1971 m_renderer->endPass(cb);
1981 if (e->button() == Qt::LeftButton) {
1982 m_perspectiveRotatedSincePress =
false;
1985 if (e->button() == Qt::LeftButton && m_viewMode ==
MultiView) {
1986 const int clickedVp = viewportIndexAt(e->pos());
1987 if (clickedVp >= 0 && m_viewportNameLabels[clickedVp] && m_viewportNameLabels[clickedVp]->isVisible()) {
1988 if (m_viewportNameLabels[clickedVp]->geometry().contains(e->pos())) {
1989 if (clickedVp != m_visualizationEditTarget) {
1992 showViewportPresetMenu(clickedVp, mapToGlobal(e->pos()));
1993 m_lastMousePos = e->pos();
1998 const int numEnabled = enabledViewportCount();
1999 const SplitterHit hit = hitTestSplitter(e->pos(), numEnabled, size());
2001 m_isDraggingSplitter =
true;
2002 m_activeSplitter = hit;
2003 m_lastMousePos = e->pos();
2004 updateSplitterCursor(e->pos());
2009 const int clickedVpForSelection = viewportIndexAt(e->pos());
2010 if (clickedVpForSelection >= 0 && clickedVpForSelection != m_visualizationEditTarget) {
2015 m_lastMousePos = e->pos();
2022 if (m_isDraggingSplitter && (event->buttons() & Qt::LeftButton)) {
2023 m_layout.dragSplitter(event->pos(), m_activeSplitter, size());
2024 m_multiSplitX = m_layout.splitX();
2025 m_multiSplitY = m_layout.splitY();
2027 m_lastMousePos =
event->pos();
2028 updateViewportSeparators();
2029 m_sceneDirty =
true; update();
2033 if (event->buttons() & Qt::LeftButton) {
2035 const int activeVp = viewportIndexAt(event->pos());
2036 const int activePreset = (activeVp >= 0 && activeVp < m_subViews.size())
2037 ? std::clamp(m_subViews[activeVp].preset, 0, 6)
2042 const QPoint diff =
event->pos() - m_lastMousePos;
2044 m_lastMousePos =
event->pos();
2045 m_sceneDirty =
true; update();
2051 QPoint diff =
event->pos() - m_lastMousePos;
2054 m_perspectiveRotatedSincePress =
true;
2055 m_lastMousePos =
event->pos();
2056 m_sceneDirty =
true; update();
2060 m_lastMousePos =
event->pos();
2065 QPoint diff =
event->pos() - m_lastMousePos;
2068 m_lastMousePos =
event->pos();
2069 m_sceneDirty =
true; update();
2072 updateSplitterCursor(event->pos());
2084 if (event->button() == Qt::LeftButton && m_isDraggingSplitter) {
2085 m_isDraggingSplitter =
false;
2087 saveMultiViewSettings();
2088 updateSplitterCursor(event->pos());
2092 if (event->button() == Qt::LeftButton && m_viewMode ==
MultiView && m_perspectiveRotatedSincePress) {
2093 m_perspectiveRotatedSincePress =
false;
2094 saveMultiViewSettings();
2098 if (event->button() == Qt::LeftButton && m_viewMode ==
MultiView && !m_perspectiveRotatedSincePress) {
2099 saveMultiViewSettings();
2103 if (event->button() == Qt::LeftButton && !m_isDraggingSplitter && !m_perspectiveRotatedSincePress) {
2105 if (m_hasIntersection) {
2111 updateSplitterCursor(event->pos());
2121 if (event->button() == Qt::LeftButton) {
2123 if (m_hasIntersection) {
2128 QRhiWidget::mouseDoubleClickEvent(event);
2135 const float delta =
event->angleDelta().y() / 120.0f;
2138 const int vp = viewportIndexAt(event->position().toPoint());
2139 if (vp >= 0 && vp < m_subViews.size()) {
2140 m_subViews[vp].zoom += delta;
2141 saveMultiViewSettings();
2146 m_sceneDirty =
true; update();
2153 if (event->key() == Qt::Key_S) {
2155 }
else if (event->key() == Qt::Key_R) {
2156 m_cameraRotation = QQuaternion();
2157 logPerspectiveRotation(
"reset-initial");
2158 saveMultiViewSettings();
2159 m_sceneDirty =
true; update();
2167 return m_sourceManager.load(lhPath, rhPath, m_surfaces, m_activeSurfaceType);
2172void BrainView::onSourceEstimateLoaded(
int numTimePoints)
2183 m_sourceManager.setTimePoint(index, m_surfaces, m_singleView, m_subViews);
2184 m_sceneDirty =
true; update();
2191 m_sourceManager.setColormap(name);
2199 m_sourceManager.setThresholds(min, mid, max);
2208 m_sourceManager.startStreaming(m_surfaces, m_singleView, m_subViews);
2215 m_sourceManager.stopStreaming();
2222 return m_sourceManager.isStreaming();
2229 m_sourceManager.pushData(data);
2236 m_sourceManager.setInterval(msec);
2243 m_sourceManager.setLooping(enabled);
2248void BrainView::onRealtimeColorsAvailable(
const QVector<uint32_t> &colorsLh,
2249 const QVector<uint32_t> &colorsRh)
2252 QSet<QString> activeTypes;
2254 for (
int i = 0; i < m_subViews.size(); ++i) {
2255 activeTypes.insert(m_subViews[i].surfaceType);
2258 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
2262 for (
const QString &type : activeTypes) {
2263 if (it.key().endsWith(type)) {
2264 int hemi = it.value()->hemi();
2265 const QVector<uint32_t> &colors = (hemi == 0) ? colorsLh : colorsRh;
2266 if (!colors.isEmpty()) {
2267 it.value()->applySourceEstimateColors(colors);
2274 m_sceneDirty =
true; update();
2282 if (evoked.isEmpty())
return false;
2286 const int previousTimePoint = m_fieldMapper.timePoint();
2287 const bool canReuse = m_fieldMapper.hasMappingFor(evoked);
2289 m_fieldMapper.setEvoked(evoked);
2293 if (!m_fieldMapper.buildMapping(m_surfaces, m_headToMriTrans, m_applySensorTrans)) {
2299 m_fieldMapper.computeNormRange();
2303 const int numTimes =
static_cast<int>(m_fieldMapper.evoked().times.size());
2304 const int tp = qBound(0, previousTimePoint, numTimes - 1);
2322 if (!m_fieldMapper.isLoaded() || m_fieldMapper.evoked().isEmpty()) {
2326 int maxIdx =
static_cast<int>(m_fieldMapper.evoked().times.size()) - 1;
2331 m_fieldMapper.setTimePoint(qBound(0, index, maxIdx));
2332 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
2334 m_sceneDirty =
true; update();
2341 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
2342 if (type ==
"MEG") {
2343 profile.megFieldMap = visible;
2344 }
else if (type ==
"EEG") {
2345 profile.eegFieldMap = visible;
2350 saveMultiViewSettings();
2351 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
2352 m_sceneDirty =
true; update();
2359 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
2360 if (type ==
"MEG") {
2361 profile.megFieldContours = visible;
2362 }
else if (type ==
"EEG") {
2363 profile.eegFieldContours = visible;
2368 saveMultiViewSettings();
2369 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
2370 m_sceneDirty =
true; update();
2377 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
2378 if (profile.megFieldMapOnHead == useHead && m_fieldMapper.megFieldMapOnHead() == useHead) {
2382 profile.megFieldMapOnHead = useHead;
2383 m_fieldMapper.setMegFieldMapOnHead(useHead);
2384 saveMultiViewSettings();
2385 if (m_fieldMapper.isLoaded()) {
2386 m_fieldMapper.buildMapping(m_surfaces, m_headToMriTrans, m_applySensorTrans);
2387 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
2388 m_sceneDirty =
true; update();
2396 if (m_fieldMapper.colormap() == name) {
2399 m_fieldMapper.setColormap(name);
2400 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
2401 m_sceneDirty =
true; update();
2408 return m_sourceManager.tstep();
2415 return m_sourceManager.tmin();
2422 return m_sourceManager.numTimePoints();
2429 if (!m_fieldMapper.isLoaded() || m_fieldMapper.evoked().nave == -1 || m_fieldMapper.evoked().times.size() == 0) {
2434 float bestDist = std::abs(m_fieldMapper.evoked().times(0) - timeSec);
2435 for (
int i = 1; i < m_fieldMapper.evoked().times.size(); ++i) {
2436 float dist = std::abs(m_fieldMapper.evoked().times(i) - timeSec);
2437 if (dist < bestDist) {
2449 return m_sourceManager.closestIndex(timeSec);
2456 if (!m_fieldMapper.isLoaded() || m_fieldMapper.evoked().nave == -1 || m_fieldMapper.evoked().times.size() == 0) {
2459 tmin = m_fieldMapper.evoked().times(0);
2460 tmax = m_fieldMapper.evoked().times(m_fieldMapper.evoked().times.size() - 1);
2470 m_sensorStreamManager.startStreaming(modality, m_fieldMapper, m_surfaces);
2477 m_sensorStreamManager.stopStreaming();
2484 return m_sensorStreamManager.isStreaming();
2491 m_sensorStreamManager.pushData(data);
2498 m_sensorStreamManager.setInterval(msec);
2505 m_sensorStreamManager.setLooping(enabled);
2512 m_sensorStreamManager.setAverages(numAvr);
2519 m_sensorStreamManager.setColormap(name);
2524void BrainView::onSensorStreamColorsAvailable(
const QString &surfaceKey,
2525 const QVector<uint32_t> &colors)
2527 if (surfaceKey.isEmpty() || !m_surfaces.contains(surfaceKey)) {
2531 auto surface = m_surfaces[surfaceKey];
2532 if (surface && !colors.isEmpty()) {
2533 surface->applySourceEstimateColors(colors);
2536 m_sceneDirty =
true; update();
2543 if (!r.hasInfo && !r.hasDigitizer)
return false;
2546 m_devHeadTrans = r.devHeadTrans;
2547 m_hasDevHead = r.hasDevHead;
2549 if (!r.megGradItems.isEmpty()) m_model->addSensors(
"MEG/Grad", r.megGradItems);
2550 if (!r.megMagItems.isEmpty()) m_model->addSensors(
"MEG/Mag", r.megMagItems);
2551 if (!r.eegItems.isEmpty()) m_model->addSensors(
"EEG", r.eegItems);
2553 if (r.helmetSurface) {
2554 m_surfaces[
"sens_surface_meg"] = r.helmetSurface;
2556 qWarning() <<
"BrainView::loadSensors: NO helmet surface returned from DataLoader!";
2559 if (!r.digitizerPoints.isEmpty())
2560 m_model->addDigitizerData(r.digitizerPoints);
2563 m_cardinalDigPoints.clear();
2564 for (
const auto &dp : r.digitizerPoints) {
2566 m_cardinalDigPoints.append(dp);
2576 QMap<int, QVector3D> result;
2577 if (m_cardinalDigPoints.isEmpty())
2581 const bool haveTrans = (m_headToMriTrans.from != 0 || m_headToMriTrans.to != 0);
2582 QMatrix4x4 headToMri;
2583 headToMri.setToIdentity();
2587 for (
const auto &dp : m_cardinalDigPoints) {
2588 const QVector3D posHead(dp.r[0], dp.r[1], dp.r[2]);
2589 result[dp.ident] = haveTrans ? headToMri.map(posHead) : posHead;
2598 auto it = m_surfaces.find(QStringLiteral(
"bem_head"));
2599 if (it == m_surfaces.end() || !it.value())
2603 QVector3D bmin, bmax;
2607 pos = QVector3D((bmin.x() + bmax.x()) * 0.5f,
2608 (bmin.y() + bmax.y()) * 0.5f,
2618 qWarning() <<
"BrainView::loadMegHelmetSurface: DataLoader returned nullptr!";
2622 m_surfaces[
"sens_surface_meg"] = surface;
2623 refreshSensorTransforms();
2624 updateSceneBounds();
2625 m_sceneDirty =
true; update();
2634 if (ecdSet.size() == 0)
return false;
2635 m_model->addDipoles(ecdSet);
2643 if (network.
getNodes().isEmpty())
return false;
2645 m_network = std::make_unique<NetworkObject>();
2646 m_network->load(network);
2647 m_network->setVisible(
true);
2650 m_model->addNetwork(network, name);
2652 m_sceneDirty =
true; update();
2660 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
2661 profile.network = visible;
2662 m_networkVisible = visible;
2663 if (m_network) m_network->setVisible(visible);
2664 saveMultiViewSettings();
2665 m_sceneDirty =
true; update();
2673 m_network->setThreshold(threshold);
2674 m_sceneDirty =
true; update();
2683 m_network->setColormap(name);
2684 m_sceneDirty =
true; update();
2693 if (srcSpace.isEmpty())
return false;
2694 m_model->addSourceSpace(srcSpace);
2702 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
2703 profile.sourceSpace = visible;
2704 saveMultiViewSettings();
2705 m_sceneDirty =
true; update();
2716 m_headToMriTrans = trans;
2717 refreshSensorTransforms();
2723void BrainView::refreshSensorTransforms()
2726 if (m_applySensorTrans && !m_headToMriTrans.
isEmpty()) {
2731 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
2732 if ((it.key().startsWith(
"sens_") || it.key().startsWith(
"dig_")) && it.value()) {
2733 it.value()->applyTransform(qmat);
2738 if (m_fieldMapper.isLoaded()) {
2739 m_fieldMapper.buildMapping(m_surfaces, m_headToMriTrans, m_applySensorTrans);
2740 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
2749 const QSize outputSize = size();
2751 const auto enabledViewports = enabledViewportIndices();
2753 const int numEnabled = enabledViewports.size();
2755 QRect activePane(0, 0, outputSize.width(), outputSize.height());
2757 bool hasValidPane =
true;
2758 if (m_viewMode ==
MultiView && numEnabled > 1) {
2759 bool foundSlot =
false;
2760 for (
int slot = 0; slot < numEnabled; ++slot) {
2761 const QRect pane = multiViewSlotRect(slot, numEnabled, outputSize);
2762 if (pane.contains(pos)) {
2770 hasValidPane = foundSlot;
2773 const int vp = (m_viewMode ==
MultiView) ? enabledViewports[activeSlot] : 0;
2774 const SubView &sv = (m_viewMode ==
MultiView) ? m_subViews[vp] : m_singleView;
2776 const QVector3D effectiveCenter2 = m_cameraFocusOverride ? m_cameraFocusCenter : m_sceneCenter;
2777 const float effectiveSize2 = m_cameraFocusOverride ? m_cameraFocusSize : m_sceneSize;
2778 m_camera.setSceneCenter(effectiveCenter2);
2779 m_camera.setSceneSize(effectiveSize2);
2780 m_camera.setRotation(m_cameraRotation);
2781 m_camera.setZoom(m_zoom);
2782 const float aspect = float(std::max(1, activePane.width())) / float(std::max(1, activePane.height()));
2784 ? m_camera.computeMultiView(sv, aspect)
2785 : m_camera.computeSingleView(aspect);
2789 QVector3D rayOrigin, rayDir;
2796 pickResult =
RayPicker::pick(rayOrigin, rayDir, sv, m_surfaces, m_itemSurfaceMap, m_itemDipoleMap);
2798 m_hasIntersection = pickResult.
hit;
2799 if (pickResult.
hit) {
2800 m_lastIntersectionPoint = pickResult.
hitPoint;
2803 QStandardItem *hitItem = pickResult.
item;
2808 const QString &hitKey = pickResult.
surfaceKey;
2809 int currentRegionId = pickResult.
regionId;
2811 if (displayLabel != m_hoveredRegion) {
2812 m_hoveredRegion = displayLabel;
2814 if (m_regionLabel) {
2815 if (m_hoveredRegion.isEmpty()) {
2816 m_regionLabel->hide();
2818 m_regionLabel->setText(m_hoveredRegion);
2819 m_regionLabel->show();
2824 QString hoveredSurfaceKey;
2825 if (hitKey.startsWith(
"sens_surface_meg")) {
2826 hoveredSurfaceKey = hitKey;
2829 if (hitItem != m_hoveredItem || hitIndex != m_hoveredIndex || hoveredSurfaceKey != m_hoveredSurfaceKey) {
2831 if (m_hoveredItem) {
2832 if (m_itemSurfaceMap.contains(m_hoveredItem)) {
2833 m_itemSurfaceMap[m_hoveredItem]->setSelected(
false);
2834 m_itemSurfaceMap[m_hoveredItem]->setSelectedRegion(-1);
2835 m_itemSurfaceMap[m_hoveredItem]->setSelectedVertexRange(-1, 0);
2836 }
else if (m_itemDipoleMap.contains(m_hoveredItem)) {
2837 m_itemDipoleMap[m_hoveredItem]->setSelected(m_hoveredIndex,
false);
2840 if (!m_hoveredSurfaceKey.isEmpty() && m_surfaces.contains(m_hoveredSurfaceKey)) {
2841 m_surfaces[m_hoveredSurfaceKey]->setSelected(
false);
2842 m_surfaces[m_hoveredSurfaceKey]->setSelectedRegion(-1);
2843 m_surfaces[m_hoveredSurfaceKey]->setSelectedVertexRange(-1, 0);
2846 m_hoveredItem = hitItem;
2847 m_hoveredIndex = hitIndex;
2848 m_hoveredSurfaceKey = hoveredSurfaceKey;
2850 if (m_hoveredItem) {
2852 if (m_itemSurfaceMap.contains(m_hoveredItem)) {
2855 bool isDigitizer = absHitSel &&
2858 if (isDigitizer && m_hoveredIndex >= 0) {
2860 int sphereIdx = m_hoveredIndex / vertsPerSphere;
2861 m_itemSurfaceMap[m_hoveredItem]->setSelectedVertexRange(
2862 sphereIdx * vertsPerSphere, vertsPerSphere);
2863 }
else if (currentRegionId != -1) {
2864 m_itemSurfaceMap[m_hoveredItem]->setSelectedRegion(currentRegionId);
2868 m_itemSurfaceMap[m_hoveredItem]->setSelected(
true);
2870 m_itemSurfaceMap[m_hoveredItem]->setSelected(
true);
2871 m_itemSurfaceMap[m_hoveredItem]->setSelectedRegion(-1);
2873 }
else if (m_itemDipoleMap.contains(m_hoveredItem)) {
2874 m_itemDipoleMap[m_hoveredItem]->setSelected(m_hoveredIndex,
true);
2876 }
else if (!m_hoveredSurfaceKey.isEmpty() && m_surfaces.contains(m_hoveredSurfaceKey)) {
2877 m_surfaces[m_hoveredSurfaceKey]->setSelected(
true);
2878 m_surfaces[m_hoveredSurfaceKey]->setSelectedRegion(-1);
2880 }
else if (m_hoveredItem && m_itemSurfaceMap.contains(m_hoveredItem)) {
2882 bool isDigitizer = absHitUpd &&
2885 if (isDigitizer && m_hoveredIndex >= 0) {
2887 int sphereIdx = m_hoveredIndex / vertsPerSphere;
2888 m_itemSurfaceMap[m_hoveredItem]->setSelectedVertexRange(
2889 sphereIdx * vertsPerSphere, vertsPerSphere);
2890 }
else if (currentRegionId != -1) {
2891 m_itemSurfaceMap[m_hoveredItem]->setSelectedRegion(currentRegionId);
2892 m_itemSurfaceMap[m_hoveredItem]->setSelected(
true);
2894 m_itemSurfaceMap[m_hoveredItem]->setSelectedRegion(-1);
2895 m_itemSurfaceMap[m_hoveredItem]->setSelected(
true);
2897 }
else if (!m_hoveredSurfaceKey.isEmpty() && m_surfaces.contains(m_hoveredSurfaceKey)) {
2898 m_surfaces[m_hoveredSurfaceKey]->setSelected(
true);
2900 m_sceneDirty =
true; update();
2905void BrainView::showViewportPresetMenu(
int viewport,
const QPoint &globalPos)
2907 if (viewport < 0 || viewport >= m_subViews.size()) {
2912 QAction *topAction = menu.addAction(
"Top");
2913 QAction *perspectiveAction = menu.addAction(
"Perspective");
2914 QAction *frontAction = menu.addAction(
"Front");
2915 QAction *leftAction = menu.addAction(
"Left");
2916 menu.addSeparator();
2917 QAction *bottomAction = menu.addAction(
"Bottom");
2918 QAction *backAction = menu.addAction(
"Back");
2919 QAction *rightAction = menu.addAction(
"Right");
2921 const int currentPreset = std::clamp(m_subViews[viewport].preset, 0, 6);
2922 topAction->setCheckable(
true);
2923 perspectiveAction->setCheckable(
true);
2924 frontAction->setCheckable(
true);
2925 leftAction->setCheckable(
true);
2926 bottomAction->setCheckable(
true);
2927 backAction->setCheckable(
true);
2928 rightAction->setCheckable(
true);
2930 topAction->setChecked(currentPreset == 0);
2931 perspectiveAction->setChecked(currentPreset == 1);
2932 frontAction->setChecked(currentPreset == 2);
2933 leftAction->setChecked(currentPreset == 3);
2934 bottomAction->setChecked(currentPreset == 4);
2935 backAction->setChecked(currentPreset == 5);
2936 rightAction->setChecked(currentPreset == 6);
2938 QAction *selected = menu.exec(globalPos);
2943 int newPreset = currentPreset;
2944 if (selected == topAction) {
2946 }
else if (selected == perspectiveAction) {
2948 }
else if (selected == frontAction) {
2950 }
else if (selected == leftAction) {
2952 }
else if (selected == bottomAction) {
2954 }
else if (selected == backAction) {
2956 }
else if (selected == rightAction) {
2960 if (newPreset == currentPreset) {
2964 m_subViews[viewport].preset = newPreset;
2965 saveMultiViewSettings();
2966 updateOverlayLayout();
2967 m_sceneDirty =
true; update();
2978void BrainView::removeSurfacesByPrefix(
const QString &prefix)
2981 QStringList keysToRemove;
2982 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
2983 if (it.key().startsWith(prefix))
2984 keysToRemove << it.key();
2986 for (
const QString &key : keysToRemove)
2987 m_surfaces.remove(key);
2990 QList<const QStandardItem*> itemsToRemove;
2991 for (
auto it = m_itemSurfaceMap.cbegin(); it != m_itemSurfaceMap.cend(); ++it) {
2993 for (
const QString &key : keysToRemove) {
2995 for (
auto sit = m_surfaces.cbegin(); sit != m_surfaces.cend(); ++sit) {
2996 if (sit.value() == it.value()) { found =
true;
break; }
3000 bool stillPresent =
false;
3001 for (
auto sit = m_surfaces.cbegin(); sit != m_surfaces.cend(); ++sit) {
3002 if (sit.value() == it.value()) { stillPresent =
true;
break; }
3005 itemsToRemove << it.key();
3007 for (
const QStandardItem *item : itemsToRemove) {
3008 m_itemSurfaceMap.remove(item);
3011 QStandardItem *mutableItem =
const_cast<QStandardItem*
>(item);
3012 if (mutableItem->parent())
3013 mutableItem->parent()->removeRow(mutableItem->row());
3015 m_model->removeRow(mutableItem->row());
3025 QStringList keysToRemove;
3026 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
3027 if (it.key().startsWith(
"lh_") || it.key().startsWith(
"rh_"))
3028 keysToRemove << it.key();
3032 for (
auto it = m_itemSurfaceMap.begin(); it != m_itemSurfaceMap.end(); ) {
3033 bool remove =
false;
3034 for (
const QString &key : keysToRemove) {
3035 if (m_surfaces.contains(key) && m_surfaces[key] == it.value()) {
3042 QStandardItem *mutableItem =
const_cast<QStandardItem*
>(it.key());
3043 if (mutableItem->parent())
3044 mutableItem->parent()->removeRow(mutableItem->row());
3046 m_model->removeRow(mutableItem->row());
3048 it = m_itemSurfaceMap.erase(it);
3054 for (
const QString &key : keysToRemove)
3055 m_surfaces.remove(key);
3057 m_activeSurface.reset();
3058 m_activeSurfaceType.clear();
3059 updateSceneBounds();
3060 m_sceneDirty =
true; update();
3067 QStringList keysToRemove;
3068 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
3069 if (it.key().startsWith(
"bem_"))
3070 keysToRemove << it.key();
3073 for (
auto it = m_itemSurfaceMap.begin(); it != m_itemSurfaceMap.end(); ) {
3074 bool remove =
false;
3075 for (
const QString &key : keysToRemove) {
3076 if (m_surfaces.contains(key) && m_surfaces[key] == it.value()) {
3083 QStandardItem *mutableItem =
const_cast<QStandardItem*
>(it.key());
3084 if (mutableItem->parent())
3085 mutableItem->parent()->removeRow(mutableItem->row());
3087 m_model->removeRow(mutableItem->row());
3089 it = m_itemSurfaceMap.erase(it);
3095 for (
const QString &key : keysToRemove)
3096 m_surfaces.remove(key);
3098 updateSceneBounds();
3099 m_sceneDirty =
true; update();
3106 m_sourceManager.stopStreaming();
3107 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
3108 if (it.key().startsWith(
"lh_") || it.key().startsWith(
"rh_")) {
3109 it.value()->clearSourceEstimateColors();
3112 m_sceneDirty =
true; update();
3122 for (
auto it = m_itemDipoleMap.begin(); it != m_itemDipoleMap.end(); ) {
3124 QStandardItem *mutableItem =
const_cast<QStandardItem*
>(it.key());
3125 if (mutableItem->parent())
3126 mutableItem->parent()->removeRow(mutableItem->row());
3128 m_model->removeRow(mutableItem->row());
3130 it = m_itemDipoleMap.erase(it);
3132 m_sceneDirty =
true; update();
3139 QStringList keysToRemove;
3140 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
3141 if (it.key().startsWith(
"srcsp_"))
3142 keysToRemove << it.key();
3145 for (
auto it = m_itemSurfaceMap.begin(); it != m_itemSurfaceMap.end(); ) {
3146 bool remove =
false;
3147 for (
const QString &key : keysToRemove) {
3148 if (m_surfaces.contains(key) && m_surfaces[key] == it.value()) {
3155 QStandardItem *mutableItem =
const_cast<QStandardItem*
>(it.key());
3156 if (mutableItem->parent())
3157 mutableItem->parent()->removeRow(mutableItem->row());
3159 m_model->removeRow(mutableItem->row());
3161 it = m_itemSurfaceMap.erase(it);
3167 for (
const QString &key : keysToRemove)
3168 m_surfaces.remove(key);
3170 updateSceneBounds();
3171 m_sceneDirty =
true; update();
3179 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ) {
3180 if (it.key().startsWith(QLatin1String(
"dig_live_")))
3181 it = m_surfaces.erase(it);
3186 for (
int i = 0; i < markers.size(); ++i) {
3188 {markers[i].position}, markers[i].radius, markers[i].color);
3189 surf->setVisible(
true);
3190 const QString prefix = markers[i].transparent
3191 ? QStringLiteral(
"dig_live_t_%1") : QStringLiteral(
"dig_live_%1");
3192 m_surfaces[prefix.arg(i)] = surf;
3196 m_sceneDirty =
true;
3202 bool removed =
false;
3203 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ) {
3204 if (it.key().startsWith(QLatin1String(
"dig_live_"))) {
3205 it = m_surfaces.erase(it);
3212 m_sceneDirty =
true;
3218 const QColor& color,
float radius)
3221 m_surfaces.remove(QLatin1String(
"dig_ray_0"));
3224 surf->setVisible(
true);
3225 m_surfaces[QStringLiteral(
"dig_ray_0")] = surf;
3227 m_sceneDirty =
true;
3233 if (m_surfaces.remove(QLatin1String(
"dig_ray_0"))) {
3234 m_sceneDirty =
true;
3240 float length,
const QColor& color,
3241 const QColor& glowColor,
3242 const QQuaternion& orientation)
3245 m_surfaces.remove(QLatin1String(
"dig_probe_shaft"));
3246 m_surfaces.remove(QLatin1String(
"dig_probe_tip"));
3247 m_surfaces.remove(QLatin1String(
"dig_probe_tipglow"));
3248 m_surfaces.remove(QLatin1String(
"dig_probe_axis_x"));
3249 m_surfaces.remove(QLatin1String(
"dig_probe_axis_y"));
3250 m_surfaces.remove(QLatin1String(
"dig_probe_axis_z"));
3251 m_surfaces.remove(QLatin1String(
"dig_probe_axis_x_tip"));
3252 m_surfaces.remove(QLatin1String(
"dig_probe_axis_y_tip"));
3253 m_surfaces.remove(QLatin1String(
"dig_probe_axis_z_tip"));
3254 m_surfaces.remove(QLatin1String(
"dig_probe_axis_xn_tip"));
3255 m_surfaces.remove(QLatin1String(
"dig_probe_axis_yn_tip"));
3256 m_surfaces.remove(QLatin1String(
"dig_probe_axis_zn_tip"));
3257 m_surfaces.remove(QLatin1String(
"dig_probe_cross_x"));
3258 m_surfaces.remove(QLatin1String(
"dig_probe_cross_y"));
3259 m_surfaces.remove(QLatin1String(
"dig_probe_cross_z"));
3262 if (length > 0.0f && !direction.isNull()) {
3263 const QVector3D shaftEnd = tip - direction * length;
3264 constexpr float kShaftRadius = 0.0015f;
3266 shaft->setVisible(
true);
3267 m_surfaces[QStringLiteral(
"dig_probe_shaft")] = shaft;
3271 constexpr float kTipRadius = 0.0010f;
3273 tipSurf->setVisible(
true);
3274 m_surfaces[QStringLiteral(
"dig_probe_tip")] = tipSurf;
3277 if (glowColor.alpha() > 0) {
3278 constexpr float kGlowTipRadius = 0.003f;
3280 tipGlow->setVisible(
true);
3281 m_surfaces[QStringLiteral(
"dig_probe_tipglow")] = tipGlow;
3285 if (!orientation.isNull()) {
3286 constexpr float kCrossLen = 0.008f;
3287 constexpr float kCrossRadius = 0.0002f;
3289 const QVector3D xDir = orientation.rotatedVector(QVector3D(1, 0, 0)).normalized();
3290 const QVector3D yDir = orientation.rotatedVector(QVector3D(0, 1, 0)).normalized();
3291 const QVector3D zDir = orientation.rotatedVector(QVector3D(0, 0, 1)).normalized();
3293 struct CrossDef { QVector3D dir; QColor color; QString key; };
3294 const CrossDef cross[] = {
3295 { xDir, QColor(255, 50, 50, 160), QStringLiteral(
"dig_probe_cross_x") },
3296 { yDir, QColor( 50, 220, 50, 160), QStringLiteral(
"dig_probe_cross_y") },
3297 { zDir, QColor( 80, 140, 255, 160), QStringLiteral(
"dig_probe_cross_z") },
3299 for (
const auto& c : cross) {
3300 const QVector3D from = tip - c.dir * kCrossLen;
3301 const QVector3D to = tip + c.dir * kCrossLen;
3303 cyl->setVisible(
true);
3304 m_surfaces[c.key] = cyl;
3309 if (!orientation.isNull()) {
3310 constexpr float kAxisLen = 0.012f;
3311 constexpr float kAxisRadius = 0.0004f;
3312 constexpr float kPosTipR = 0.0012f;
3313 constexpr float kNegTipR = 0.0006f;
3315 const QVector3D xDir = orientation.rotatedVector(QVector3D(1, 0, 0)).normalized();
3316 const QVector3D yDir = orientation.rotatedVector(QVector3D(0, 1, 0)).normalized();
3317 const QVector3D zDir = orientation.rotatedVector(QVector3D(0, 0, 1)).normalized();
3322 QString cylKey, posTipKey, negTipKey;
3324 const AxisDef axes[] = {
3325 { xDir, QColor(255, 50, 50), QStringLiteral(
"dig_probe_axis_x"),
3326 QStringLiteral(
"dig_probe_axis_x_tip"), QStringLiteral(
"dig_probe_axis_xn_tip") },
3327 { yDir, QColor( 50, 220, 50), QStringLiteral(
"dig_probe_axis_y"),
3328 QStringLiteral(
"dig_probe_axis_y_tip"), QStringLiteral(
"dig_probe_axis_yn_tip") },
3329 { zDir, QColor( 80, 140, 255), QStringLiteral(
"dig_probe_axis_z"),
3330 QStringLiteral(
"dig_probe_axis_z_tip"), QStringLiteral(
"dig_probe_axis_zn_tip") },
3333 for (
const auto& a : axes) {
3334 const QVector3D posEnd = tip + a.dir * kAxisLen;
3335 const QVector3D negEnd = tip - a.dir * kAxisLen;
3339 cyl->setVisible(
true);
3340 m_surfaces[a.cylKey] = cyl;
3344 posTip->setVisible(
true);
3345 m_surfaces[a.posTipKey] = posTip;
3348 QColor dimColor = a.color;
3349 dimColor.setAlpha(120);
3351 negTip->setVisible(
true);
3352 m_surfaces[a.negTipKey] = negTip;
3356 m_sceneDirty =
true;
3362 bool removed =
false;
3363 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_shaft"));
3364 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_tip"));
3365 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_tipglow"));
3366 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_axis_x"));
3367 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_axis_y"));
3368 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_axis_z"));
3369 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_axis_x_tip"));
3370 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_axis_y_tip"));
3371 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_axis_z_tip"));
3372 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_axis_xn_tip"));
3373 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_axis_yn_tip"));
3374 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_axis_zn_tip"));
3375 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_cross_x"));
3376 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_cross_y"));
3377 removed |= m_surfaces.remove(QLatin1String(
"dig_probe_cross_z"));
3379 m_sceneDirty =
true;
3386 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ) {
3387 if (it.key().startsWith(QLatin1String(
"dig_static_")))
3388 it = m_surfaces.erase(it);
3393 for (
int i = 0; i < markers.size(); ++i) {
3395 {markers[i].position}, markers[i].radius, markers[i].color);
3396 surf->setVisible(
true);
3397 m_surfaces[QStringLiteral(
"dig_static_%1").arg(i)] = surf;
3400 m_sceneDirty =
true;
3406 bool removed =
false;
3407 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ) {
3408 if (it.key().startsWith(QLatin1String(
"dig_static_"))) {
3409 it = m_surfaces.erase(it);
3416 m_sceneDirty =
true;
3423 m_cameraFocusOverride =
true;
3424 m_cameraFocusCenter = center;
3425 m_cameraFocusSize = size;
3426 m_sceneDirty =
true;
3432 if (!m_cameraFocusOverride)
3434 m_cameraFocusOverride =
false;
3435 updateSceneBounds();
3436 m_sceneDirty =
true;
3442 QStringList keysToRemove;
3443 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
3444 if (it.key().startsWith(
"sens_") || it.key().startsWith(
"dig_"))
3445 keysToRemove << it.key();
3448 for (
auto it = m_itemSurfaceMap.begin(); it != m_itemSurfaceMap.end(); ) {
3449 bool remove =
false;
3450 for (
const QString &key : keysToRemove) {
3451 if (m_surfaces.contains(key) && m_surfaces[key] == it.value()) {
3458 QStandardItem *mutableItem =
const_cast<QStandardItem*
>(it.key());
3459 if (mutableItem->parent())
3460 mutableItem->parent()->removeRow(mutableItem->row());
3462 m_model->removeRow(mutableItem->row());
3464 it = m_itemSurfaceMap.erase(it);
3470 for (
const QString &key : keysToRemove)
3471 m_surfaces.remove(key);
3473 m_devHeadTrans = QMatrix4x4();
3474 m_hasDevHead =
false;
3475 updateSceneBounds();
3476 m_sceneDirty =
true; update();
3484 m_sensorStreamManager.stopStreaming();
3485 m_sceneDirty =
true; update();
3493 refreshSensorTransforms();
3494 m_sceneDirty =
true; update();
3502 m_networkVisible =
false;
3506 for (
int r = m_model->rowCount() - 1; r >= 0; --r) {
3507 QStandardItem *item = m_model->item(r);
3508 if (item && item->text() ==
"Networks") {
3509 m_model->removeRow(r);
3514 m_sceneDirty =
true; update();
3521 m_sourceManager.cancelLoading();
3522 m_sourceManager.stopStreaming();
3523 m_sensorStreamManager.stopStreaming();
3532 if (!m_videoOverlay)
return;
3533 if (m_videoOverlay->isEnabled() == enabled)
return;
3534 m_videoOverlay->setEnabled(enabled);
3535 m_sceneDirty =
true; update();
3540 return m_videoOverlay && m_videoOverlay->isEnabled();
3545 if (!m_videoOverlay)
return;
3546 m_videoOverlay->setFocusPosition(position);
3547 if (m_videoOverlay->isEnabled()) { m_sceneDirty =
true; update(); }
3552 if (!m_videoOverlay)
return;
3553 m_videoOverlay->setUpHint(dir);
3554 if (m_videoOverlay->isEnabled()) { m_sceneDirty =
true; update(); }
3559 if (!m_videoOverlay)
return;
3560 m_videoOverlay->setSizeMeters(std::max(0.001f, meters));
3561 if (m_videoOverlay->isEnabled()) { m_sceneDirty =
true; update(); }
3566 if (!m_videoOverlay)
return;
3567 m_videoOverlay->setOpacity(std::clamp(opacity, 0.0f, 1.0f));
3568 if (m_videoOverlay->isEnabled()) { m_sceneDirty =
true; update(); }
3573 if (!m_videoOverlay)
return;
3574 m_videoOverlay->setFrame(frame);
3575 if (m_videoOverlay->isEnabled()) { m_sceneDirty =
true; update(); }
3580 if (!m_videoOverlay)
return;
3581 m_videoOverlay->setDepthEnabled(enabled);
3582 if (m_videoOverlay->isEnabled()) { m_sceneDirty =
true; update(); }
3587 if (!m_videoOverlay)
return;
3588 m_videoOverlay->setDepthScale(std::clamp(scale, 0.0f, 1.0f));
3589 if (m_videoOverlay->isEnabled()) { m_sceneDirty =
true; update(); }
3594 if (!m_videoOverlay)
return;
3595 m_videoOverlay->setDepthSteps(std::clamp(steps, 8, 64));
3596 if (m_videoOverlay->isEnabled()) { m_sceneDirty =
true; update(); }
3601 if (!m_videoOverlay)
return;
3602 m_videoOverlay->setDepthFrame(depthFrame);
3603 if (m_videoOverlay->isEnabled() && m_videoOverlay->isDepthEnabled()) {
3604 m_sceneDirty =
true; update();
3613 float closestDist = std::numeric_limits<float>::max();
3618 auto headIt = m_surfaces.constFind(QStringLiteral(
"bem_head"));
3619 if (headIt != m_surfaces.cend() && headIt.value()) {
3622 if (headIt.value()->intersects(origin, direction, dist, vertexIdx) && dist < closestDist) {
3624 hitPoint = origin + dist * direction;
3631 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
3632 const auto &surf = it.value();
3633 if (!surf || !surf->isVisible())
continue;
3636 if (surf->intersects(origin, direction, dist, vertexIdx) && dist < closestDist) {
3638 hitPoint = origin + dist * direction;
3652 if (slotIndex < 0 || slotIndex >= kMaxSliceSlots)
return;
3653 m_slices[slotIndex] = slice;
3654 m_sceneDirty =
true;
3660 if (slotIndex < 0 || slotIndex >= kMaxSliceSlots)
return;
3661 m_sliceVisible[slotIndex] = visible;
3662 m_sceneDirty =
true;
3670 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
3671 profile.mriSlices = visible;
3672 saveMultiViewSettings();
3673 m_sceneDirty =
true; update();
Instanced connectivity-graph renderable: node spheres and edge cylinders coloured by weight through a...
Single MRI volume slice rendered as a textured quad with adjustable axis, position,...
Renderable cortical / BEM mesh with interleaved vertex attributes and Qt-RHI buffer management.
Instanced-arrow renderable for fitted equivalent current dipoles, driven by QRhi instancing.
Generic live-RGB video texture overlay rendered as a screen-aligned quad with chroma keying.
String key constants for surfaces in the DISP3DLIB scene map.
QString shaderModeName(ShaderMode mode)
VisualizationMode visualizationModeFromName(const QString &name)
QString visualizationModeName(VisualizationMode mode)
bool multiViewPresetIsPerspective(int preset)
ShaderMode shaderModeFromName(const QString &name)
int normalizedVisualizationTarget(int target, int maxIndex)
QString multiViewPresetName(int preset)
Static helpers for loading FreeSurfer / MNE source-space and BEM data into the disp3D model tree.
Top-level QRhiWidget that hosts the 3-D scene, drives camera interaction and exposes the multi-view +...
Qt-RHI scene renderer: shader pipelines, lighting, dual render targets and per-frame draw orchestrati...
Static factory for procedural Qt-RHI meshes (spheres, plates, barbells, cylinders,...
Tree item holding a single category of digitizer points rendered as a batched-sphere mesh.
Tree item wrapping a fitted INVLIB::InvEcdSet of equivalent current dipoles.
Tree item holding one hemisphere of source-space dipole positions rendered as batched spheres.
Tree item wrapping a single MNELIB::MNEBemSurface (brain / inner-skull / outer-skull / scalp shell).
Tree item for a single MEG / EEG sensor with position, optional coil orientation and rendered scale.
Tree item wrapping a FreeSurfer FSLIB::FsSurface plus optional FSLIB::FsAnnotation parcellation.
QStandardItemModel hierarchy that organises every 3-D scene object (surfaces, sensors,...
Mouse-ray vs scene-object intersection for picking dipoles, electrodes and surfaces.
Boundary element model bundle (inner skull, outer skull, outer skin) loaded from -bem....
Container pairing the left and right cortical source spaces of a subject.
Graph container that stores the result of one functional-connectivity metric as nodes (sources/sensor...
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
#define FIFFV_POINT_CARDINAL
Set of averaged evoked responses sharing a FiffInfo, plus the ave-style category / rejection descript...
FIFF file I/O, in-memory data structures and high-level readers/writers.
QString sensorParentToKeyPrefix(const QString &parentText)
QString sensorTypeToObjectKey(const QString &uiType)
QMatrix4x4 toQMatrix4x4(const Eigen::Matrix4f &m)
Graph container for one connectivity metric; nodes + weighted edges + threshold/visualisation state.
const QList< QSharedPointer< NetworkNode > > & getNodes() const
static SensorLoadResult loadSensors(const QString &fifPath, const QString &megHelmetOverridePath={})
static QStringList probeEvokedSets(const QString &evokedPath)
static MNELIB::MNESourceSpaces loadSourceSpace(const QString &fwdPath)
static bool loadHeadToMriTransform(const QString &transPath, FIFFLIB::FiffCoordTrans &trans)
static std::shared_ptr< BrainSurface > loadHelmetSurface(const QString &helmetFilePath, const QMatrix4x4 &devHeadTrans=QMatrix4x4(), bool applyTrans=false)
static INVLIB::InvEcdSet loadDipoles(const QString &dipPath)
static FIFFLIB::FiffEvoked loadEvoked(const QString &evokedPath, int aveIndex=0)
Per-view toggle flags controlling which data layers (brain, sensors, sources, network) are visible.
Viewport subdivision holding its own camera, projection, and scissor rectangle.
bool matchesSurfaceType(const QString &key) const
ViewVisibilityProfile visibility
static bool isBrainSurfaceKey(const QString &key)
bool shouldRenderSurface(const QString &key) const
VisualizationMode overlayMode
static SubView defaultForIndex(int index)
static std::shared_ptr< BrainSurface > createPlate(const QVector3D ¢er, const QMatrix4x4 &orientation, const QColor &color, float size)
static std::shared_ptr< BrainSurface > createBatchedSpheres(const QVector< QVector3D > &positions, float radius, const QColor &color, int subdivisions=1)
static std::shared_ptr< BrainSurface > createBarbell(const QVector3D ¢er, const QMatrix4x4 &orientation, const QColor &color, float size)
static int sphereVertexCount(int subdivisions=1)
static std::shared_ptr< BrainSurface > createCylinder(const QVector3D &from, const QVector3D &to, float radius, const QColor &color, int sides=12)
static std::shared_ptr< BrainSurface > createSphere(const QVector3D ¢er, float radius, const QColor &color, int subdivisions=1)
Computed camera matrices (projection, view, model) and vectors for a single viewport.
static void applyMouseRotation(const QPoint &delta, QQuaternion &rotation, float speed=0.5f)
static void applyMousePan(const QPoint &delta, QVector2D &pan, float sceneSize)
Result of a ray–mesh intersection test containing the hit point, triangle index, and distance.
int vertexIndex
Vertex or element index at hit.
bool hit
True if something was hit.
QString surfaceKey
FsSurface map key of the hit surface.
QVector3D hitPoint
World-space intersection point.
QStandardItem * item
Tree item that was hit (nullable).
int regionId
FsAnnotation label ID.
static bool unproject(const QPoint &screenPos, const QRect &paneRect, const QMatrix4x4 &pvm, QVector3D &rayOrigin, QVector3D &rayDir)
static QString buildLabel(const PickResult &result, const QMap< const QStandardItem *, std::shared_ptr< BrainSurface > > &itemSurfaceMap, const QMap< QString, std::shared_ptr< BrainSurface > > &surfaces)
static PickResult pick(const QVector3D &rayOrigin, const QVector3D &rayDir, const SubView &subView, const QMap< QString, std::shared_ptr< BrainSurface > > &surfaces, const QMap< const QStandardItem *, std::shared_ptr< BrainSurface > > &itemSurfaceMap, const QMap< const QStandardItem *, std::shared_ptr< DipoleObject > > &itemDipoleMap)
Hierarchical item model organizing all 3-D scene objects (surfaces, sensors, sources,...
Base tree item providing check-state, visibility, and data-role storage for all 3-D scene items.
static constexpr int itemTypeId(ItemType type)
void setVisible(bool visible)
int type() const override
Tree item representing a BEM surface layer in the 3-D scene hierarchy.
const MNELIB::MNEBemSurface & bemSurfaceData() const
Digitizer point group tree item.
PointKind pointKind() const
const QVector< QVector3D > & positions() const
Tree item representing a set of fitted dipoles in the 3-D scene hierarchy.
const INVLIB::InvEcdSet & ecdSet() const
Tree item representing MEG or EEG sensor positions in the 3-D scene hierarchy.
bool hasOrientation() const
QVector3D position() const
const QMatrix4x4 & orientation() const
Source space point tree item.
const QVector< QVector3D > & positions() const
Tree item representing a FreeSurfer cortical surface in the 3-D scene hierarchy.
FSLIB::FsSurface surfaceData() const
FSLIB::FsAnnotation annotationData() const
Renderable cortical surface mesh with per-vertex color, curvature data, and GPU buffer management.
static constexpr VisualizationMode ModeScientific
void boundingBox(QVector3D &min, QVector3D &max) const
::VisualizationMode VisualizationMode
static constexpr VisualizationMode ModeSurface
Data model for a single 2-D MRI volume slice.
void colorsAvailable(const QString &surfaceKey, const QVector< uint32_t > &colors)
void loadingProgress(int percent, const QString &message)
void timePointChanged(int index, float time)
void loaded(int numTimePoints)
void thresholdsUpdated(float min, float mid, float max)
void realtimeColorsAvailable(const QVector< uint32_t > &colorsLh, const QVector< uint32_t > &colorsRh)
static constexpr ShaderMode Holographic
Aggregated GPU resources and render state for the 3-D brain visualization scene.
void setVideoOverlayOpacity(float opacity)
void setBemHighContrast(bool enabled)
void setSourceColormap(const QString &name)
void setVideoDepthScale(float scale)
bool loadMegHelmetSurface(const QString &helmetFilePath)
void setHemiVisible(int hemiIdx, bool visible)
void setSensorFieldTimePoint(int index)
void sourceThresholdsUpdated(float min, float mid, float max)
void setCameraFocusOverride(const QVector3D ¢er, float size)
void setInfoPanelVisible(bool visible)
void clearProbeVisualization()
void clearSourceEstimate()
int stcNumTimePoints() const
bool loadTransformation(const QString &transPath)
bool loadDipoles(const QString &dipPath)
void wheelEvent(QWheelEvent *event) override
void clearStaticMarkers()
bool sensorFieldTimeRange(float &tmin, float &tmax) const
void sourceEstimateLoaded(int numTimePoints)
void shaderModeChanged(const QString &modeName)
void setSlice(int slotIndex, DISP3DLIB::SliceObject *slice)
void setLiveRay(const QVector3D &from, const QVector3D &to, const QColor &color, float radius=0.001f)
void setSensorFieldContourVisible(const QString &type, bool visible)
bool megFieldMapOnHeadForTarget(int target) const
void pushVideoDepthFrame(const QImage &depthFrame)
void setShaderMode(const QString &mode)
void setNetworkColormap(const QString &name)
int closestSensorFieldIndex(float timeSec) const
bool isRealtimeSensorStreaming() const
void resetAllSubViewState()
bool intersectWorldRay(const QVector3D &origin, const QVector3D &direction, QVector3D &hitPoint) const
Intersect a world-space ray with loaded scene geometry.
void resizeEvent(QResizeEvent *event) override
int closestStcIndex(float timeSec) const
void pushVideoOverlayFrame(const QImage &frame)
QString bemShaderModeForTarget(int target) const
void setVideoOverlayUpHint(const QVector3D &dir)
void keyPressEvent(QKeyEvent *event) override
void setRealtimeLooping(bool enabled)
void setRealtimeInterval(int msec)
void setSensorFieldVisible(const QString &type, bool visible)
int visualizationEditTarget() const
void startRealtimeSensorStreaming(const QString &modality=QStringLiteral("MEG"))
QString overlayModeForTarget(int target) const
void clearTransformation()
void onRowsInserted(const QModelIndex &parent, int first, int last)
void stopRealtimeSensorStreaming()
QString shaderModeForTarget(int target) const
void mouseDoubleClickEvent(QMouseEvent *event) override
bool objectVisibleForTarget(const QString &object, int target) const
bool isVideoOverlayEnabled() const
void setRealtimeSensorAverages(int numAvr)
void setVideoOverlayEnabled(bool enabled)
void setSourceThresholds(float min, float mid, float max)
void mouseMoveEvent(QMouseEvent *event) override
void pushRealtimeSourceData(const Eigen::VectorXd &data)
void visualizationEditTargetChanged(int target)
void setLiveMarkers(const QVector< LiveMarker > &markers)
void setVisualizationEditTarget(int target)
void timePointChanged(int index, float time)
void setSourceSpaceVisible(bool visible)
void render(QRhiCommandBuffer *cb) override
void setInitialCameraRotation(const QQuaternion &rotation)
bool loadSensors(const QString &fifPath)
void setVisualizationMode(const QString &mode)
void initialize(QRhiCommandBuffer *cb) override
void setVideoOverlaySize(float meters)
void setModel(BrainTreeModel *model)
void mouseReleaseEvent(QMouseEvent *event) override
BrainView(QWidget *parent=nullptr)
void setSensorVisible(const QString &type, bool visible)
void setSensorFieldColormap(const QString &name)
void syncBemShadersToBrainShaders()
void setRealtimeSensorLooping(bool enabled)
void viewCountChanged(int count)
void surfacePointDoubleClicked(const QVector3D &worldPos)
void setSensorTransEnabled(bool enabled)
void setProbeVisualization(const QVector3D &tip, const QVector3D &direction, float length, const QColor &color, const QColor &glowColor=QColor(0, 0, 0, 0), const QQuaternion &orientation=QQuaternion(0, 0, 0, 0))
void startRealtimeStreaming()
bool bemTopVertexInMri(QVector3D &pos) const
Return the highest-Z vertex of the BEM head surface in MRI coords.
QMap< int, QVector3D > cardinalFiducialsInMri() const
Return cardinal fiducials (NAS/LPA/RPA) transformed to MRI coordinates.
bool loadSourceEstimate(const QString &lhPath, const QString &rhPath)
void stopRealtimeStreaming()
void setViewportCameraPreset(int index, int preset)
void setBemVisible(const QString &name, bool visible)
static QStringList probeEvokedSets(const QString &evokedPath)
void castRay(const QPoint &pos)
void setViewportEnabled(int index, bool enabled)
void setBemShaderMode(const QString &mode)
void mousePressEvent(QMouseEvent *event) override
bool loadSourceSpace(const QString &fwdPath)
void setTimePoint(int index)
QString activeSurfaceForTarget(int target) const
bool isViewportEnabled(int index) const
void setVideoDepthSteps(int steps)
bool savePng(const QString &path, int width=1200, int height=800, const QString &surfaceType=QStringLiteral("pial"))
bool loadNetwork(const CONNECTIVITYLIB::Network &network, const QString &name="Network")
void setActiveSurface(const QString &type)
void setVideoOverlayFocusPosition(const QVector3D &position)
void sensorFieldTimePointChanged(int index, float time)
bool isRealtimeStreaming() const
void setViewCount(int count)
void setStaticMarkers(const QVector< LiveMarker > &markers)
void surfacePointClicked(const QVector3D &worldPos)
void resetSingleViewCameraState()
void setMegHelmetOverride(const QString &path)
void clearCameraFocusOverride()
int viewportCameraPreset(int index) const
void setSliceVisible(int slotIndex, bool visible)
void setDipoleVisible(bool visible)
void setMriSlicesVisible(bool visible)
void setRealtimeSensorInterval(int msec)
void onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector< int > &roles)
void hoveredRegionChanged(const QString ®ionName)
void setLightingEnabled(bool enabled)
void setNetworkVisible(bool visible)
void resetViewportCameraState(int index)
void sensorFieldLoaded(int numTimePoints, int initialTimePoint=0)
void setVideoDepthEnabled(bool enabled)
void setMegFieldMapOnHead(bool useHead)
bool loadSensorField(const QString &evokedPath, int aveIndex=0)
void setRealtimeSensorColormap(const QString &name)
void resetMultiViewLayout()
void stcLoadingProgress(int percent, const QString &message)
void pushRealtimeSensorData(const Eigen::VectorXf &data)
void setNetworkThreshold(double threshold)
static Qt::CursorShape cursorForHit(SplitterHit hit)
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
Eigen::Matrix< float, 4, 4, Eigen::DontAlign > trans
Single averaged evoked response: time axis, data, baseline, channel info and averaging metadata.
BEM surface provides geometry information.