69#include <QResizeEvent>
71#include <QCoreApplication>
73#include <QStandardItem>
93 setMinimumSize(800, 600);
95 setAutoRenderTarget(
false);
97#if defined(WASMBUILD) || defined(__EMSCRIPTEN__)
99#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
101#elif defined(Q_OS_WIN)
102 setApi(Api::Direct3D11);
107 setMouseTracking(
true);
113 m_fpsLabel =
new QLabel(
this);
114 m_fpsLabel->setStyleSheet(
"color: white; font-weight: bold; font-family: monospace; font-size: 13px; background: transparent; padding: 5px;");
115 m_fpsLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
116 m_fpsLabel->setAlignment(Qt::AlignRight | Qt::AlignTop);
117 m_fpsLabel->setText(
"FPS: --.-\nVertices: 0");
118 m_fpsLabel->adjustSize();
119 m_fpsLabel->move(width() - m_fpsLabel->width() - 10, 10);
122 m_singleViewInfoLabel =
new QLabel(
this);
123 m_singleViewInfoLabel->setStyleSheet(
"color: white; font-family: monospace; font-size: 10px; background: rgba(0,0,0,110); border-radius: 3px; padding: 2px 4px;");
124 m_singleViewInfoLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
125 m_singleViewInfoLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
126 m_singleViewInfoLabel->setText(
"");
127 m_singleViewInfoLabel->adjustSize();
128 m_singleViewInfoLabel->hide();
132 m_regionLabel =
new QLabel(
this);
133 m_regionLabel->setStyleSheet(
"color: white; font-weight: bold; font-family: sans-serif; font-size: 16px; background: transparent; padding: 5px;");
134 m_regionLabel->setText(
"");
135 m_regionLabel->move(10, 10);
136 m_regionLabel->resize(300, 30);
137 m_regionLabel->hide();
140 m_subViews.resize(kDefaultViewportCount);
141 m_viewportNameLabels.resize(kDefaultViewportCount,
nullptr);
142 m_viewportInfoLabels.resize(kDefaultViewportCount,
nullptr);
143 for (
int i = 0; i < kDefaultViewportCount; ++i) {
146 m_viewportNameLabels[i] =
new QLabel(
this);
147 m_viewportNameLabels[i]->setStyleSheet(
"color: white; font-weight: bold; font-family: sans-serif; font-size: 12px; background: transparent; padding: 2px 4px;");
148 m_viewportNameLabels[i]->setAttribute(Qt::WA_TransparentForMouseEvents);
150 m_viewportNameLabels[i]->adjustSize();
151 m_viewportNameLabels[i]->hide();
153 m_viewportInfoLabels[i] =
new QLabel(
this);
154 m_viewportInfoLabels[i]->setStyleSheet(
"color: white; font-family: monospace; font-size: 10px; background: rgba(0,0,0,110); border-radius: 3px; padding: 2px 4px;");
155 m_viewportInfoLabels[i]->setAttribute(Qt::WA_TransparentForMouseEvents);
156 m_viewportInfoLabels[i]->setAlignment(Qt::AlignLeft | Qt::AlignTop);
157 m_viewportInfoLabels[i]->setText(
"");
158 m_viewportInfoLabels[i]->adjustSize();
159 m_viewportInfoLabels[i]->hide();
162 m_verticalSeparator =
new QFrame(
this);
163 m_verticalSeparator->setFrameShape(QFrame::NoFrame);
164 m_verticalSeparator->setAttribute(Qt::WA_TransparentForMouseEvents);
165 m_verticalSeparator->hide();
167 m_horizontalSeparator =
new QFrame(
this);
168 m_horizontalSeparator->setFrameShape(QFrame::NoFrame);
169 m_horizontalSeparator->setAttribute(Qt::WA_TransparentForMouseEvents);
170 m_horizontalSeparator->hide();
172 QColor sepColor = palette().color(QPalette::Midlight);
173 if (sepColor.alpha() == 255) {
174 sepColor.setAlpha(180);
176 const QString sepStyle = QString(
"background-color: rgba(%1,%2,%3,%4);")
178 .arg(sepColor.green())
179 .arg(sepColor.blue())
180 .arg(sepColor.alpha());
181 m_verticalSeparator->setStyleSheet(sepStyle);
182 m_horizontalSeparator->setStyleSheet(sepStyle);
184 loadMultiViewSettings();
185 updateViewportSeparators();
186 updateOverlayLayout();
190 QColor(200, 255, 255, 160));
194 this, &BrainView::onSourceEstimateLoaded);
202 this, &BrainView::onRealtimeColorsAvailable);
206 this, &BrainView::onSensorStreamColorsAvailable);
213 saveMultiViewSettings();
232 m_cameraRotation = rotation;
233 saveMultiViewSettings();
234 m_sceneDirty =
true; update();
240 if (!m_model)
return;
242 for (
int i = first; i <= last; ++i) {
243 QModelIndex index = m_model->index(i, 0, parent);
244 QStandardItem* item = m_model->itemFromIndex(index);
251 auto brainSurf = std::make_shared<BrainSurface>();
257 if (absItem->parent()) {
258 QString parentText = absItem->parent()->text();
259 if (parentText ==
"lh") brainSurf->setHemi(0);
260 else if (parentText ==
"rh") brainSurf->setHemi(1);
264 brainSurf->setVisible(surfItem->
isVisible());
269 m_itemSurfaceMap[item] = brainSurf;
273 if (absItem->parent()) {
274 key = absItem->parent()->text() +
"_" + surfItem->text();
276 key = surfItem->text();
278 m_surfaces[key] = brainSurf;
286 if (!m_activeSurface) {
287 m_activeSurface = brainSurf;
288 m_activeSurfaceType = surfItem->text();
296 auto brainSurf = std::make_shared<BrainSurface>();
299 brainSurf->fromBemSurface(bemSurfData, bemItem->
color());
301 brainSurf->setVisible(bemItem->
isVisible());
304 QString surfName = bemItem->text().toLower();
305 if (surfName.contains(
"head") || surfName.contains(
"skin") || surfName.contains(
"scalp")) {
307 }
else if (surfName.contains(
"outer") && surfName.contains(
"skull")) {
309 }
else if (surfName.contains(
"inner") && surfName.contains(
"skull")) {
311 }
else if (surfName.contains(
"skull")) {
313 }
else if (surfName.contains(
"brain")) {
317 m_itemSurfaceMap[item] = brainSurf;
320 m_surfaces[
"bem_" + bemItem->text()] = brainSurf;
327 std::shared_ptr<BrainSurface> brainSurf;
329 QString parentText =
"";
330 if (sensItem->parent()) parentText = sensItem->parent()->text();
332 if (parentText.contains(
"MEG/Grad") && sensItem->
hasOrientation()) {
335 }
else if (parentText.contains(
"MEG/Mag") && sensItem->
hasOrientation()) {
345 m_itemSurfaceMap[item] = brainSurf;
349 if (!m_headToMriTrans.isEmpty()) {
351 if (m_applySensorTrans) {
354 brainSurf->applyTransform(m);
360 QString key = keyPrefix + sensItem->text() +
"_" + QString::number((quintptr)sensItem);
361 m_surfaces[key] = brainSurf;
369 auto dipObject = std::make_shared<DipoleObject>();
370 dipObject->load(dipItem->
ecdSet());
371 dipObject->setVisible(dipItem->
isVisible());
373 m_itemDipoleMap[item] = dipObject;
379 const QVector<QVector3D>& positions = srcItem->
positions();
380 if (positions.isEmpty())
continue;
384 brainSurf->setVisible(srcItem->
isVisible());
385 m_itemSurfaceMap[item] = brainSurf;
387 QString key =
"srcsp_" + srcItem->text();
388 m_surfaces[key] = brainSurf;
394 const QVector<QVector3D>& positions = digItem->
positions();
395 if (positions.isEmpty())
continue;
399 brainSurf->setVisible(digItem->
isVisible());
402 if (!m_headToMriTrans.isEmpty()) {
404 if (m_applySensorTrans) {
407 brainSurf->applyTransform(m);
410 m_itemSurfaceMap[item] = brainSurf;
420 QString key =
"dig_" + catName;
421 m_surfaces[key] = brainSurf;
426 if (m_model->hasChildren(index)) {
430 updateInflatedSurfaceTransforms();
432 m_vertexCountDirty =
true;
433 m_sceneDirty =
true; update();
439 for (
int i = topLeft.row(); i <= bottomRight.row(); ++i) {
440 QModelIndex index = m_model->index(i, 0, topLeft.parent());
441 QStandardItem* item = m_model->itemFromIndex(index);
443 if (m_itemSurfaceMap.contains(item)) {
444 auto surf = m_itemSurfaceMap[item];
450 m_vertexCountDirty =
true;
465 m_sceneDirty =
true; update();
472 subViewForTarget(m_visualizationEditTarget).surfaceType = type;
474 m_activeSurfaceType = type;
477 QString key =
"lh_" + type;
478 if (m_surfaces.contains(key)) m_activeSurface = m_surfaces[key];
481 if (m_surfaces.contains(key)) m_activeSurface = m_surfaces[key];
484 updateInflatedSurfaceTransforms();
485 saveMultiViewSettings();
488 m_vertexCountDirty =
true;
489 m_sceneDirty =
true; update();
492void BrainView::updateSceneBounds()
494 QVector3D min(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
495 QVector3D max(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
496 bool hasContent =
false;
499 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
500 if (it.value()->isVisible()) {
501 QVector3D sMin, sMax;
502 it.value()->boundingBox(sMin, sMax);
504 min.setX(std::min(min.x(), sMin.x()));
505 min.setY(std::min(min.y(), sMin.y()));
506 min.setZ(std::min(min.z(), sMin.z()));
508 max.setX(std::max(max.x(), sMax.x()));
509 max.setY(std::max(max.y(), sMax.y()));
510 max.setZ(std::max(max.z(), sMax.z()));
516 for (
auto it = m_itemDipoleMap.begin(); it != m_itemDipoleMap.end(); ++it) {
517 if (it.value()->isVisible()) {
526 m_sceneCenter = (min + max) * 0.5f;
528 QVector3D diag = max - min;
529 m_sceneSize = std::max(diag.x(), std::max(diag.y(), diag.z()));
532 if (m_sceneSize < 0.01f) m_sceneSize = 0.3f;
536 m_sceneCenter = QVector3D(0,0,0);
546 subViewForTarget(m_visualizationEditTarget).brainShader = mode;
548 m_brainShaderMode = mode;
549 saveMultiViewSettings();
550 m_sceneDirty =
true; update();
557 const int prev = m_visualizationEditTarget;
560 const SubView &sv = subViewForTarget(m_visualizationEditTarget);
567 const bool remapMegSurface = (m_fieldMapper.megFieldMapOnHead() != visibility.
megFieldMapOnHead);
569 m_dipolesVisible = visibility.
dipoles;
570 m_networkVisible = visibility.
network;
578 if (m_fieldMapper.isLoaded()) {
579 if (remapMegSurface) {
580 m_fieldMapper.buildMapping(m_surfaces, m_headToMriTrans, m_applySensorTrans);
582 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
586 updateViewportLabelHighlight();
588 saveMultiViewSettings();
590 if (prev != m_visualizationEditTarget) {
599 return m_visualizationEditTarget;
606 return subViewForTarget(target).surfaceType;
641 return subViewForTarget(target).visibility;
646SubView& BrainView::subViewForTarget(
int target)
649 return (normalized < 0) ? m_singleView : m_subViews[normalized];
654const SubView& BrainView::subViewForTarget(
int target)
const
657 return (normalized < 0) ? m_singleView : m_subViews[normalized];
669 return visibilityProfileForTarget(target).isObjectVisible(
object);
676 return visibilityProfileForTarget(target).megFieldMapOnHead;
681void BrainView::updateInflatedSurfaceTransforms()
683 const bool needsInflated = (m_singleView.
surfaceType ==
"inflated")
684 || std::any_of(m_subViews.cbegin(), m_subViews.cend(),
685 [](
const SubView &sv) { return sv.surfaceType ==
"inflated"; });
687 const QString lhKey =
"lh_inflated";
688 const QString rhKey =
"rh_inflated";
690 if (!m_surfaces.contains(lhKey) || !m_surfaces.contains(rhKey)) {
694 auto lhSurf = m_surfaces[lhKey];
695 auto rhSurf = m_surfaces[rhKey];
698 lhSurf->applyTransform(identity);
699 rhSurf->applyTransform(identity);
701 if (!needsInflated) {
705 const float lhMaxX = lhSurf->maxX();
706 const float rhMinX = rhSurf->minX();
708 const float gap = 0.005f;
709 const float lhOffset = -gap / 2.0f - lhMaxX;
710 const float rhOffset = gap / 2.0f - rhMinX;
712 lhSurf->translateX(lhOffset);
713 rhSurf->translateX(rhOffset);
720 subViewForTarget(m_visualizationEditTarget).bemShader = mode;
722 m_bemShaderMode = mode;
723 saveMultiViewSettings();
724 m_sceneDirty =
true; update();
731 m_singleView.bemShader = m_singleView.brainShader;
732 for (
int i = 0; i < m_subViews.size(); ++i) {
733 m_subViews[i].bemShader = m_subViews[i].brainShader;
736 m_bemShaderMode = subViewForTarget(m_visualizationEditTarget).bemShader;
738 saveMultiViewSettings();
739 m_sceneDirty =
true; update();
745 if (
object.isEmpty())
return;
747 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
748 profile.setObjectVisible(
object, visible);
753 if (type == QLatin1String(
"MEG")) {
754 profile.sensMegGrad = visible;
755 profile.sensMegMag = visible;
756 }
else if (type == QLatin1String(
"EEG")) {
758 }
else if (type == QLatin1String(
"Digitizer")) {
759 profile.digCardinal = visible;
760 profile.digHpi = visible;
761 profile.digEeg = visible;
762 profile.digExtra = visible;
765 saveMultiViewSettings();
766 m_sceneDirty =
true; update();
771 if (m_applySensorTrans != enabled) {
772 m_applySensorTrans = enabled;
773 refreshSensorTransforms();
774 m_sceneDirty =
true; update();
782 m_megHelmetOverridePath = path;
787 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
788 profile.dipoles = visible;
789 m_dipolesVisible = visible;
790 saveMultiViewSettings();
791 m_sceneDirty =
true; update();
799 SubView &sv = subViewForTarget(m_visualizationEditTarget);
802 m_currentVisMode = mode;
808 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
809 const QString &key = it.key();
810 if (key.startsWith(
"lh_") || key.startsWith(
"rh_")) {
811 it.value()->setVisualizationMode(mode);
815 saveMultiViewSettings();
816 m_sceneDirty =
true; update();
823 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
825 profile.lh = visible;
826 }
else if (hemiIdx == 1) {
827 profile.rh = visible;
829 saveMultiViewSettings();
830 m_sceneDirty =
true; update();
837 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
838 profile.setObjectVisible(
"bem_" + name, visible);
839 saveMultiViewSettings();
840 m_sceneDirty =
true; update();
845 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
846 if (it.key().startsWith(
"bem_")) {
847 it.value()->setUseDefaultColor(enabled);
850 m_sceneDirty =
true; update();
857 m_lightingEnabled = enabled;
858 m_sceneDirty =
true; update();
865 QImage img = grabFramebuffer();
866 QString fileName = QString(
"snapshot_refactor_%1.png").arg(m_snapshotCounter++, 4, 10, QChar(
'0'));
876 m_isDraggingSplitter =
false;
879 saveMultiViewSettings();
880 updateViewportSeparators();
881 updateOverlayLayout();
882 m_sceneDirty =
true; update();
890 saveMultiViewSettings();
891 updateViewportSeparators();
892 updateOverlayLayout();
893 m_sceneDirty =
true; update();
900 count = std::clamp(count, 1,
static_cast<int>(m_subViews.size()));
905 m_isDraggingSplitter =
false;
912 if (m_visualizationEditTarget < 0)
917 for (
int i = 0; i < m_subViews.size(); ++i)
918 m_subViews[i].enabled = (i < count);
920 saveMultiViewSettings();
921 updateViewportSeparators();
922 updateOverlayLayout();
923 m_sceneDirty =
true; update();
930 m_layout.resetSplits();
931 m_multiSplitX = m_layout.splitX();
932 m_multiSplitY = m_layout.splitY();
933 saveMultiViewSettings();
934 updateViewportSeparators();
935 updateOverlayLayout();
936 m_sceneDirty =
true; update();
941 if (index < 0 || index >= m_subViews.size()) {
945 return m_subViews[index].enabled;
950int BrainView::enabledViewportCount()
const
957 for (
int i = 0; i < m_subViews.size(); ++i) {
958 if (m_subViews[i].enabled) {
963 return numEnabled > 0 ? numEnabled : 1;
968QVector<int> BrainView::enabledViewportIndices()
const
972 for (
int i = 0; i < m_subViews.size(); ++i) {
973 if (m_subViews[i].enabled)
986int BrainView::viewportIndexAt(
const QPoint& pos)
const
992 const auto enabledViewports = enabledViewportIndices();
993 return m_layout.viewportIndexAt(pos, enabledViewports, size());
998QRect BrainView::multiViewSlotRect(
int slot,
int numEnabled,
const QSize& outputSize)
const
1000 return m_layout.slotRect(slot, numEnabled, outputSize);
1005SplitterHit BrainView::hitTestSplitter(
const QPoint& pos,
int numEnabled,
const QSize& outputSize)
const
1007 if (m_viewMode !=
MultiView || numEnabled <= 1) {
1010 return m_layout.hitTestSplitter(pos, numEnabled, outputSize);
1015void BrainView::updateSplitterCursor(
const QPoint& pos)
1017 const SplitterHit hit = hitTestSplitter(pos, enabledViewportCount(), size());
1019 if (shape == Qt::ArrowCursor) {
1028void BrainView::updateViewportSeparators()
1030 if (!m_verticalSeparator || !m_horizontalSeparator) {
1034 m_verticalSeparator->hide();
1035 m_horizontalSeparator->hide();
1037 const int numEnabled = enabledViewportCount();
1038 if (m_viewMode !=
MultiView || numEnabled <= 1) {
1043 m_layout.separatorGeometries(numEnabled, size(), vRect, hRect);
1045 if (!vRect.isEmpty()) {
1046 m_verticalSeparator->setGeometry(vRect);
1047 m_verticalSeparator->show();
1048 m_verticalSeparator->raise();
1050 if (!hRect.isEmpty()) {
1051 m_horizontalSeparator->setGeometry(hRect);
1052 m_horizontalSeparator->show();
1053 m_horizontalSeparator->raise();
1056 updateOverlayLayout();
1061void BrainView::updateOverlayLayout()
1063 const auto enabledViewports = enabledViewportIndices();
1066 m_fpsLabel->setVisible(m_infoPanelVisible);
1067 m_fpsLabel->adjustSize();
1068 const int perfBottomMargin = 2;
1071 m_fpsLabel->move(width() - m_fpsLabel->width() - 10,
1072 height() - m_fpsLabel->height() - perfBottomMargin);
1074 m_fpsLabel->move(width() - m_fpsLabel->width() - 10,
1075 height() - m_fpsLabel->height() - perfBottomMargin);
1078 m_fpsLabel->raise();
1081 if (m_singleViewInfoLabel) {
1082 const bool showSingleInfo = (m_viewMode ==
SingleView) && m_infoPanelVisible;
1083 m_singleViewInfoLabel->setVisible(showSingleInfo);
1084 if (showSingleInfo) {
1085 m_singleViewInfoLabel->adjustSize();
1086 m_singleViewInfoLabel->move(width() - m_singleViewInfoLabel->width() - 8, 8);
1087 m_singleViewInfoLabel->raise();
1091 if (m_regionLabel) {
1092 const int regionY = (m_viewMode ==
MultiView) ? 38 : 10;
1093 m_regionLabel->move(10, regionY);
1094 if (!m_regionLabel->text().isEmpty()) {
1095 m_regionLabel->raise();
1099 for (
int i = 0; i < m_viewportNameLabels.size(); ++i) {
1100 if (m_viewportNameLabels[i]) {
1101 m_viewportNameLabels[i]->hide();
1103 if (m_viewportInfoLabels[i]) {
1104 m_viewportInfoLabels[i]->hide();
1112 const int numEnabled = enabledViewports.size();
1113 const QSize overlaySize = size();
1114 for (
int slot = 0; slot < numEnabled; ++slot) {
1115 const int vp = enabledViewports[slot];
1116 QLabel* label = m_viewportNameLabels[vp];
1117 QLabel* infoLabel = m_viewportInfoLabels[vp];
1122 const int preset = std::clamp(m_subViews[vp].preset, 0, 6);
1125 const QRect pane = multiViewSlotRect(slot, numEnabled, overlaySize);
1126 label->adjustSize();
1127 label->move(pane.x() + 8, pane.y() + 8);
1128 label->setVisible(
true);
1132 infoLabel->adjustSize();
1133 infoLabel->move(pane.x() + pane.width() - infoLabel->width() - 8,
1135 infoLabel->setVisible(m_infoPanelVisible);
1140 updateViewportLabelHighlight();
1145void BrainView::updateViewportLabelHighlight()
1147 static const QString normalStyle =
1148 QStringLiteral(
"color: white; font-weight: bold; font-family: sans-serif; "
1149 "font-size: 12px; background: transparent; padding: 2px 4px;");
1150 static const QString selectedStyle =
1151 QStringLiteral(
"color: #FFD54F; font-weight: bold; font-family: sans-serif; "
1152 "font-size: 13px; background: rgba(255,213,79,40); "
1153 "border: 1px solid #FFD54F; border-radius: 3px; padding: 2px 6px;");
1155 for (
int i = 0; i < m_viewportNameLabels.size(); ++i) {
1156 if (!m_viewportNameLabels[i])
continue;
1157 const bool selected = (m_viewMode ==
MultiView && m_visualizationEditTarget == i);
1158 m_viewportNameLabels[i]->setStyleSheet(selected ? selectedStyle : normalStyle);
1159 m_viewportNameLabels[i]->adjustSize();
1165void BrainView::logPerspectiveRotation(
const QString& context)
const
1172void BrainView::loadMultiViewSettings()
1174 QSettings settings(
"MNECPP");
1175 settings.beginGroup(
"ex_brain_view/BrainView");
1177 m_multiSplitX = settings.value(
"multiSplitX", 0.5f).toFloat();
1178 m_multiSplitY = settings.value(
"multiSplitY", 0.5f).toFloat();
1180 const int savedViewMode = settings.value(
"viewMode",
static_cast<int>(
SingleView)).toInt();
1182 m_viewCount = std::clamp(settings.value(
"viewCount", 1).toInt(), 1,
static_cast<int>(m_subViews.size()));
1184 if (m_viewCount > 1) m_viewMode =
MultiView;
1187 const bool hasCameraQuat = settings.contains(
"cameraRotW")
1188 && settings.contains(
"cameraRotX")
1189 && settings.contains(
"cameraRotY")
1190 && settings.contains(
"cameraRotZ");
1191 if (hasCameraQuat) {
1192 const float w = settings.value(
"cameraRotW", 1.0f).toFloat();
1193 const float x = settings.value(
"cameraRotX", 0.0f).toFloat();
1194 const float y = settings.value(
"cameraRotY", 0.0f).toFloat();
1195 const float z = settings.value(
"cameraRotZ", 0.0f).toFloat();
1196 m_cameraRotation = QQuaternion(w, x, y, z);
1197 if (m_cameraRotation.lengthSquared() <= std::numeric_limits<float>::epsilon()) {
1198 m_cameraRotation = QQuaternion();
1200 m_cameraRotation.normalize();
1205 for (
int i = 0; i < m_subViews.size(); ++i) {
1207 m_subViews[i].enabled = (i < m_viewCount);
1211 m_singleView.load(settings,
"single_", m_cameraRotation);
1212 for (
int i = 0; i < m_subViews.size(); ++i)
1213 m_subViews[i].load(settings, QStringLiteral(
"multi%1_").arg(i), m_cameraRotation);
1215 const int maxIdx =
static_cast<int>(m_subViews.size()) - 1;
1217 settings.value(
"visualizationEditTarget", -1).toInt(), maxIdx);
1219 m_infoPanelVisible = settings.value(
"infoPanelVisible",
true).toBool();
1221 settings.endGroup();
1223 m_multiSplitX = std::clamp(m_multiSplitX, 0.15f, 0.85f);
1224 m_multiSplitY = std::clamp(m_multiSplitY, 0.15f, 0.85f);
1225 m_layout.setSplitX(m_multiSplitX);
1226 m_layout.setSplitY(m_multiSplitY);
1233void BrainView::saveMultiViewSettings()
const
1235 QSettings settings(
"MNECPP");
1236 settings.beginGroup(
"ex_brain_view/BrainView");
1237 settings.setValue(
"multiSplitX", m_multiSplitX);
1238 settings.setValue(
"multiSplitY", m_multiSplitY);
1239 settings.setValue(
"viewMode",
static_cast<int>(m_viewMode));
1240 settings.setValue(
"viewCount", m_viewCount);
1241 settings.setValue(
"cameraRotW", m_cameraRotation.scalar());
1242 settings.setValue(
"cameraRotX", m_cameraRotation.x());
1243 settings.setValue(
"cameraRotY", m_cameraRotation.y());
1244 settings.setValue(
"cameraRotZ", m_cameraRotation.z());
1245 for (
int i = 0; i < m_subViews.size(); ++i)
1246 settings.setValue(QStringLiteral(
"viewportEnabled%1").arg(i), m_subViews[i].enabled);
1247 settings.setValue(
"visualizationEditTarget", m_visualizationEditTarget);
1248 settings.setValue(
"infoPanelVisible", m_infoPanelVisible);
1251 m_singleView.save(settings,
"single_");
1252 for (
int i = 0; i < m_subViews.size(); ++i)
1253 m_subViews[i].save(settings, QStringLiteral(
"multi%1_").arg(i));
1255 settings.endGroup();
1262 if (index >= 0 && index < m_subViews.size()) {
1263 m_subViews[index].enabled = enabled;
1264 saveMultiViewSettings();
1265 updateViewportSeparators();
1266 updateOverlayLayout();
1267 m_sceneDirty =
true; update();
1275 if (index < 0 || index >=
static_cast<int>(m_subViews.size()))
1277 preset = std::clamp(preset, 0, 6);
1278 if (m_subViews[index].preset == preset)
1280 m_subViews[index].preset = preset;
1281 saveMultiViewSettings();
1282 updateOverlayLayout();
1283 m_sceneDirty =
true; update();
1290 if (index < 0 || index >=
static_cast<int>(m_subViews.size()))
1292 return std::clamp(m_subViews[index].preset, 0, 6);
1299 m_infoPanelVisible = visible;
1300 saveMultiViewSettings();
1301 updateOverlayLayout();
1308 QRhiWidget::resizeEvent(event);
1309 updateViewportSeparators();
1310 updateOverlayLayout();
1319 m_renderer = std::make_unique<BrainRenderer>();
1324 m_renderer->ensureRenderTargets(rhi(), colorTexture(), colorTexture()->pixelSize());
1332 bool hasSurfaces = !m_surfaces.isEmpty();
1333 bool hasDipoles = !m_itemDipoleMap.isEmpty() || m_dipoles;
1336 if (!hasSurfaces && !hasDipoles) {
1339 m_renderer = std::make_unique<BrainRenderer>();
1341 m_renderer->ensureRenderTargets(rhi(), colorTexture(), colorTexture()->pixelSize());
1342 m_renderer->initialize(rhi(), m_renderer->rtClear()->renderPassDescriptor(), sampleCount());
1343 m_renderer->beginFrame(cb);
1344 m_renderer->endPass(cb);
1349 if (!m_activeSurface && !m_surfaces.isEmpty()) {
1350 m_activeSurface = m_surfaces.begin().value();
1355 if (m_fpsTimer.elapsed() >= 500) {
1356 float fps = m_frameCount / (m_fpsTimer.elapsed() / 1000.0f);
1359 if (m_vertexCountDirty) {
1360 auto countVerticesForSubView = [
this](
const SubView &sv) -> qint64 {
1362 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
1363 const QString &key = it.key();
1364 auto surface = it.value();
1365 if (!surface)
continue;
1370 if (!surface->isVisible())
continue;
1372 total += surface->vertexCount();
1379 for (
int vp : enabledViewportIndices()) {
1380 vCount += countVerticesForSubView(m_subViews[vp]);
1383 vCount = countVerticesForSubView(m_singleView);
1385 m_cachedVertexCount = vCount;
1386 m_vertexCountDirty =
false;
1389 m_fpsLabel->setText(QString(
"FPS: %1\nVertices: %2").arg(fps, 0,
'f', 1).arg(m_cachedVertexCount));
1390 updateOverlayLayout();
1391 m_fpsLabel->raise();
1393 m_fpsTimer.restart();
1397 m_renderer->ensureRenderTargets(rhi(), colorTexture(), colorTexture()->pixelSize());
1398 m_renderer->initialize(rhi(), m_renderer->rtClear()->renderPassDescriptor(), sampleCount());
1401 QSize outputSize = m_renderer->rtClear()->pixelSize();
1404 const auto enabledViewports = enabledViewportIndices();
1405 int numEnabled = enabledViewports.size();
1425 QRhiResourceUpdateBatch *preUpload = rhi()->nextResourceUpdateBatch();
1426 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1427#ifdef __EMSCRIPTEN__
1434 it.value()->updateBuffers(rhi(), preUpload);
1436#ifndef __EMSCRIPTEN__
1437 if (m_debugPointerSurface) {
1438 m_debugPointerSurface->updateBuffers(rhi(), preUpload);
1440 for (
auto it = m_itemDipoleMap.begin(); it != m_itemDipoleMap.end(); ++it) {
1441 it.value()->updateBuffers(rhi(), preUpload);
1444 m_dipoles->updateBuffers(rhi(), preUpload);
1448#ifdef __EMSCRIPTEN__
1455 const SubView &sv = (m_viewMode ==
MultiView) ? m_subViews[0] : m_singleView;
1457 QVector<BrainSurface*> allSurfaces;
1460 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1464 allSurfaces.append(it.value().get());
1468 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1469 if (!it.key().startsWith(
"srcsp_"))
continue;
1471 if (!it.value()->isVisible())
continue;
1472 allSurfaces.append(it.value().get());
1476 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1477 if (!it.key().startsWith(
"dig_"))
continue;
1479 if (!it.value()->isVisible())
continue;
1480 allSurfaces.append(it.value().get());
1484 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1485 bool isSensor = it.key().startsWith(
"sens_");
1486 bool isBem = it.key().startsWith(
"bem_");
1487 if (!isSensor && !isBem)
continue;
1489 if (!it.value()->isVisible())
continue;
1490 allSurfaces.append(it.value().get());
1493 m_renderer->prepareMergedSurfaces(rhi(), preUpload, allSurfaces, QStringLiteral(
"default"));
1498 cb->resourceUpdate(preUpload);
1502 m_renderer->beginFrame(cb);
1504 for (
int slot = 0; slot < numEnabled; ++slot) {
1505 int vp = (m_viewMode ==
MultiView) ? enabledViewports[slot] : 0;
1506 const SubView &sv = (m_viewMode ==
MultiView) ? m_subViews[vp] : m_singleView;
1507 const int preset = (m_viewMode ==
MultiView) ? std::clamp(sv.
preset, 0, 6) : 1;
1509 const QRect paneRect = (m_viewMode ==
MultiView)
1510 ? multiViewSlotRect(slot, numEnabled, outputSize)
1511 : QRect(0, 0, outputSize.width(), outputSize.height());
1513 QRect renderRect = paneRect;
1514 if (m_viewMode ==
MultiView && numEnabled > 1) {
1515 constexpr int separatorPx = 2;
1517 if (numEnabled == 2) {
1519 renderRect.setWidth(std::max(1, renderRect.width() - separatorPx));
1521 }
else if (numEnabled == 3) {
1525 renderRect.setHeight(std::max(1, renderRect.height() - separatorPx));
1526 }
else if (slot == 1) {
1528 renderRect.setWidth(std::max(1, renderRect.width() - separatorPx));
1532 const int col = slot % 2;
1533 const int row = slot / 2;
1535 const bool hasRightNeighbor = (col == 0)
1536 && (slot + 1 < numEnabled)
1537 && ((slot / 2) == ((slot + 1) / 2));
1538 const bool hasBottomNeighbor = (row == 0)
1539 && (slot + 2 < numEnabled);
1541 if (hasRightNeighbor) {
1542 renderRect.setWidth(std::max(1, renderRect.width() - separatorPx));
1544 if (hasBottomNeighbor) {
1545 renderRect.setHeight(std::max(1, renderRect.height() - separatorPx));
1550 const int viewX = renderRect.x();
1551 const int viewY = outputSize.height() - (renderRect.y() + renderRect.height());
1552 const int viewW = std::max(1, renderRect.width());
1553 const int viewH = std::max(1, renderRect.height());
1555 QRhiViewport viewport(viewX, viewY, viewW, viewH);
1556 QRhiScissor scissor(viewX, viewY, viewW, viewH);
1557 const float aspectRatio = float(viewW) / float(viewH);
1560 cb->setViewport(viewport);
1561 cb->setScissor(scissor);
1564 m_camera.setSceneCenter(m_sceneCenter);
1565 m_camera.setSceneSize(m_sceneSize);
1566 m_camera.setRotation(m_cameraRotation);
1567 m_camera.setZoom(m_zoom);
1569 ? m_camera.computeMultiView(sv, aspectRatio)
1570 : m_camera.computeSingleView(aspectRatio);
1573 sceneData.
mvp = rhi()->clipSpaceCorrMatrix();
1602#ifndef __EMSCRIPTEN__
1603 QStringList drawnKeys;
1605 const QString drawnInfo = QStringLiteral(
"merged");
1608 if (m_viewMode ==
MultiView && m_viewportInfoLabels[vp]) {
1609 m_viewportInfoLabels[vp]->setText(
1610 QString(
"Shader: %1\nSurface: %2\nOverlay: %3")
1612 }
else if (m_viewMode ==
SingleView && m_singleViewInfoLabel) {
1613 m_singleViewInfoLabel->setText(
1614 QString(
"Shader: %1\nSurface: %2\nOverlay: %3")
1618 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1622#ifdef __EMSCRIPTEN__
1627#ifndef __EMSCRIPTEN__
1628 drawnKeys << it.key();
1630 m_renderer->renderSurface(cb, rhi(), sceneData, it.value().get(), currentShader);
1633#ifndef __EMSCRIPTEN__
1636 const QString drawnInfo = drawnKeys.isEmpty() ? QStringLiteral(
"none") : drawnKeys.join(QStringLiteral(
", "));
1637 if (m_viewMode ==
MultiView && m_viewportInfoLabels[vp]) {
1638 m_viewportInfoLabels[vp]->setText(m_viewportInfoLabels[vp]->text()
1639 + QStringLiteral(
"\nDrawn: ") + drawnInfo);
1640 }
else if (m_viewMode ==
SingleView && m_singleViewInfoLabel) {
1641 m_singleViewInfoLabel->setText(m_singleViewInfoLabel->text()
1642 + QStringLiteral(
"\nDrawn: ") + drawnInfo);
1647#ifdef __EMSCRIPTEN__
1658 m_renderer->drawMergedSurfaces(cb, rhi(), sceneData, currentShader, QStringLiteral(
"default"));
1671 const QString &megFieldKey = m_fieldMapper.megSurfaceKey();
1672 const QString &eegFieldKey = m_fieldMapper.eegSurfaceKey();
1682 QVector<DrawItem> opaqueDraws;
1683 QVector<DrawItem> transparentDraws;
1685 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
1686 const QString &key = it.key();
1692 }
else if (key.startsWith(
"srcsp_") || key.startsWith(
"dig_")) {
1695 opaqueDraws.append({surf, currentShader,
1699 bool isSensor = key.startsWith(
"sens_");
1700 bool isBem = key.startsWith(
"bem_");
1701 if (!isSensor && !isBem)
continue;
1705 QVector3D bmin, bmax;
1707 QVector3D center = (bmin + bmax) * 0.5f;
1708 float dist = (sceneData.
cameraPos - center).lengthSquared();
1712 if (key == megFieldKey && !megFieldVisible)
1714 else if (key == eegFieldKey && !eegFieldVisible)
1717 transparentDraws.append({surf, mode, itemOverlay, dist, -1});
1722 std::sort(transparentDraws.begin(), transparentDraws.end(),
1723 [](
const DrawItem &a,
const DrawItem &b) { return a.distSq > b.distSq; });
1726 QRhiResourceUpdateBatch *surfBatch = rhi()->nextResourceUpdateBatch();
1729 for (
auto &item : opaqueDraws) {
1731 item.uniformOffset = m_renderer->prepareSurfaceDraw(surfBatch, batchData, item.surface);
1733 for (
auto &item : transparentDraws) {
1735 item.uniformOffset = m_renderer->prepareSurfaceDraw(surfBatch, batchData, item.surface);
1738 cb->resourceUpdate(surfBatch);
1741 cb->setViewport(viewport);
1742 cb->setScissor(scissor);
1745 for (
const auto &item : opaqueDraws)
1746 m_renderer->issueSurfaceDraw(cb, item.surface, item.mode, item.uniformOffset);
1747 for (
const auto &item : transparentDraws)
1748 m_renderer->issueSurfaceDraw(cb, item.surface, item.mode, item.uniformOffset);
1751 for(
auto it = m_itemDipoleMap.begin(); it != m_itemDipoleMap.end(); ++it) {
1753 m_renderer->renderDipoles(cb, rhi(), sceneData, it.value().get());
1758 m_renderer->renderDipoles(cb, rhi(), sceneData, m_dipoles.get());
1763 m_renderer->renderNetwork(cb, rhi(), sceneData, m_network.get());
1767 if (m_hasIntersection && m_debugPointerSurface) {
1771 QMatrix4x4 translation;
1772 translation.translate(m_lastIntersectionPoint);
1783 m_renderer->endPass(cb);
1793 if (e->button() == Qt::LeftButton) {
1794 m_perspectiveRotatedSincePress =
false;
1797 if (e->button() == Qt::LeftButton && m_viewMode ==
MultiView) {
1798 const int clickedVp = viewportIndexAt(e->pos());
1799 if (clickedVp >= 0 && m_viewportNameLabels[clickedVp] && m_viewportNameLabels[clickedVp]->isVisible()) {
1800 if (m_viewportNameLabels[clickedVp]->geometry().contains(e->pos())) {
1801 if (clickedVp != m_visualizationEditTarget) {
1804 showViewportPresetMenu(clickedVp, mapToGlobal(e->pos()));
1805 m_lastMousePos = e->pos();
1810 const int numEnabled = enabledViewportCount();
1811 const SplitterHit hit = hitTestSplitter(e->pos(), numEnabled, size());
1813 m_isDraggingSplitter =
true;
1814 m_activeSplitter = hit;
1815 m_lastMousePos = e->pos();
1816 updateSplitterCursor(e->pos());
1821 const int clickedVpForSelection = viewportIndexAt(e->pos());
1822 if (clickedVpForSelection >= 0 && clickedVpForSelection != m_visualizationEditTarget) {
1827 m_lastMousePos = e->pos();
1834 if (m_isDraggingSplitter && (event->buttons() & Qt::LeftButton)) {
1835 m_layout.dragSplitter(event->pos(), m_activeSplitter, size());
1836 m_multiSplitX = m_layout.splitX();
1837 m_multiSplitY = m_layout.splitY();
1839 m_lastMousePos =
event->pos();
1840 updateViewportSeparators();
1841 m_sceneDirty =
true; update();
1845 if (event->buttons() & Qt::LeftButton) {
1847 const int activeVp = viewportIndexAt(event->pos());
1848 const int activePreset = (activeVp >= 0 && activeVp < m_subViews.size())
1849 ? std::clamp(m_subViews[activeVp].preset, 0, 6)
1854 const QPoint diff =
event->pos() - m_lastMousePos;
1856 m_lastMousePos =
event->pos();
1857 m_sceneDirty =
true; update();
1863 QPoint diff =
event->pos() - m_lastMousePos;
1866 m_perspectiveRotatedSincePress =
true;
1867 m_lastMousePos =
event->pos();
1868 m_sceneDirty =
true; update();
1872 m_lastMousePos =
event->pos();
1877 QPoint diff =
event->pos() - m_lastMousePos;
1880 m_lastMousePos =
event->pos();
1881 m_sceneDirty =
true; update();
1884 updateSplitterCursor(event->pos());
1896 if (event->button() == Qt::LeftButton && m_isDraggingSplitter) {
1897 m_isDraggingSplitter =
false;
1899 saveMultiViewSettings();
1900 updateSplitterCursor(event->pos());
1904 if (event->button() == Qt::LeftButton && m_viewMode ==
MultiView && m_perspectiveRotatedSincePress) {
1905 m_perspectiveRotatedSincePress =
false;
1906 saveMultiViewSettings();
1910 if (event->button() == Qt::LeftButton && m_viewMode ==
MultiView && !m_perspectiveRotatedSincePress) {
1911 saveMultiViewSettings();
1915 updateSplitterCursor(event->pos());
1925 const float delta =
event->angleDelta().y() / 120.0f;
1928 const int vp = viewportIndexAt(event->position().toPoint());
1929 if (vp >= 0 && vp < m_subViews.size()) {
1930 m_subViews[vp].zoom += delta;
1931 saveMultiViewSettings();
1936 m_sceneDirty =
true; update();
1943 if (event->key() == Qt::Key_S) {
1945 }
else if (event->key() == Qt::Key_R) {
1946 m_cameraRotation = QQuaternion();
1947 logPerspectiveRotation(
"reset-initial");
1948 saveMultiViewSettings();
1949 m_sceneDirty =
true; update();
1957 return m_sourceManager.load(lhPath, rhPath, m_surfaces, m_activeSurfaceType);
1962void BrainView::onSourceEstimateLoaded(
int numTimePoints)
1973 m_sourceManager.setTimePoint(index, m_surfaces, m_singleView, m_subViews);
1974 m_sceneDirty =
true; update();
1981 m_sourceManager.setColormap(name);
1989 m_sourceManager.setThresholds(min, mid, max);
1998 m_sourceManager.startStreaming(m_surfaces, m_singleView, m_subViews);
2005 m_sourceManager.stopStreaming();
2012 return m_sourceManager.isStreaming();
2019 m_sourceManager.pushData(data);
2026 m_sourceManager.setInterval(msec);
2033 m_sourceManager.setLooping(enabled);
2038void BrainView::onRealtimeColorsAvailable(
const QVector<uint32_t> &colorsLh,
2039 const QVector<uint32_t> &colorsRh)
2042 QSet<QString> activeTypes;
2044 for (
int i = 0; i < m_subViews.size(); ++i) {
2045 activeTypes.insert(m_subViews[i].surfaceType);
2048 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
2052 for (
const QString &type : activeTypes) {
2053 if (it.key().endsWith(type)) {
2054 int hemi = it.value()->hemi();
2055 const QVector<uint32_t> &colors = (hemi == 0) ? colorsLh : colorsRh;
2056 if (!colors.isEmpty()) {
2057 it.value()->applySourceEstimateColors(colors);
2064 m_sceneDirty =
true; update();
2072 if (evoked.isEmpty())
return false;
2076 const int previousTimePoint = m_fieldMapper.timePoint();
2077 const bool canReuse = m_fieldMapper.hasMappingFor(evoked);
2079 m_fieldMapper.setEvoked(evoked);
2083 if (!m_fieldMapper.buildMapping(m_surfaces, m_headToMriTrans, m_applySensorTrans)) {
2089 m_fieldMapper.computeNormRange();
2093 const int numTimes =
static_cast<int>(m_fieldMapper.evoked().times.size());
2094 const int tp = qBound(0, previousTimePoint, numTimes - 1);
2112 if (!m_fieldMapper.isLoaded() || m_fieldMapper.evoked().isEmpty()) {
2116 int maxIdx =
static_cast<int>(m_fieldMapper.evoked().times.size()) - 1;
2121 m_fieldMapper.setTimePoint(qBound(0, index, maxIdx));
2122 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
2124 m_sceneDirty =
true; update();
2131 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
2132 if (type ==
"MEG") {
2133 profile.megFieldMap = visible;
2134 }
else if (type ==
"EEG") {
2135 profile.eegFieldMap = visible;
2140 saveMultiViewSettings();
2141 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
2142 m_sceneDirty =
true; update();
2149 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
2150 if (type ==
"MEG") {
2151 profile.megFieldContours = visible;
2152 }
else if (type ==
"EEG") {
2153 profile.eegFieldContours = visible;
2158 saveMultiViewSettings();
2159 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
2160 m_sceneDirty =
true; update();
2167 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
2168 if (profile.megFieldMapOnHead == useHead && m_fieldMapper.megFieldMapOnHead() == useHead) {
2172 profile.megFieldMapOnHead = useHead;
2173 m_fieldMapper.setMegFieldMapOnHead(useHead);
2174 saveMultiViewSettings();
2175 if (m_fieldMapper.isLoaded()) {
2176 m_fieldMapper.buildMapping(m_surfaces, m_headToMriTrans, m_applySensorTrans);
2177 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
2178 m_sceneDirty =
true; update();
2186 if (m_fieldMapper.colormap() == name) {
2189 m_fieldMapper.setColormap(name);
2190 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
2191 m_sceneDirty =
true; update();
2198 return m_sourceManager.tstep();
2205 return m_sourceManager.tmin();
2212 return m_sourceManager.numTimePoints();
2219 if (!m_fieldMapper.isLoaded() || m_fieldMapper.evoked().nave == -1 || m_fieldMapper.evoked().times.size() == 0) {
2224 float bestDist = std::abs(m_fieldMapper.evoked().times(0) - timeSec);
2225 for (
int i = 1; i < m_fieldMapper.evoked().times.size(); ++i) {
2226 float dist = std::abs(m_fieldMapper.evoked().times(i) - timeSec);
2227 if (dist < bestDist) {
2239 return m_sourceManager.closestIndex(timeSec);
2246 if (!m_fieldMapper.isLoaded() || m_fieldMapper.evoked().nave == -1 || m_fieldMapper.evoked().times.size() == 0) {
2249 tmin = m_fieldMapper.evoked().times(0);
2250 tmax = m_fieldMapper.evoked().times(m_fieldMapper.evoked().times.size() - 1);
2260 m_sensorStreamManager.startStreaming(modality, m_fieldMapper, m_surfaces);
2267 m_sensorStreamManager.stopStreaming();
2274 return m_sensorStreamManager.isStreaming();
2281 m_sensorStreamManager.pushData(data);
2288 m_sensorStreamManager.setInterval(msec);
2295 m_sensorStreamManager.setLooping(enabled);
2302 m_sensorStreamManager.setAverages(numAvr);
2309 m_sensorStreamManager.setColormap(name);
2314void BrainView::onSensorStreamColorsAvailable(
const QString &surfaceKey,
2315 const QVector<uint32_t> &colors)
2317 if (surfaceKey.isEmpty() || !m_surfaces.contains(surfaceKey)) {
2321 auto surface = m_surfaces[surfaceKey];
2322 if (surface && !colors.isEmpty()) {
2323 surface->applySourceEstimateColors(colors);
2326 m_sceneDirty =
true; update();
2333 if (!r.hasInfo && !r.hasDigitizer)
return false;
2336 m_devHeadTrans = r.devHeadTrans;
2337 m_hasDevHead = r.hasDevHead;
2339 if (!r.megGradItems.isEmpty()) m_model->addSensors(
"MEG/Grad", r.megGradItems);
2340 if (!r.megMagItems.isEmpty()) m_model->addSensors(
"MEG/Mag", r.megMagItems);
2341 if (!r.eegItems.isEmpty()) m_model->addSensors(
"EEG", r.eegItems);
2343 if (r.helmetSurface) {
2344 m_surfaces[
"sens_surface_meg"] = r.helmetSurface;
2346 qWarning() <<
"BrainView::loadSensors: NO helmet surface returned from DataLoader!";
2349 if (!r.digitizerPoints.isEmpty())
2350 m_model->addDigitizerData(r.digitizerPoints);
2360 qWarning() <<
"BrainView::loadMegHelmetSurface: DataLoader returned nullptr!";
2364 m_surfaces[
"sens_surface_meg"] = surface;
2365 refreshSensorTransforms();
2366 updateSceneBounds();
2367 m_sceneDirty =
true; update();
2376 if (ecdSet.size() == 0)
return false;
2377 m_model->addDipoles(ecdSet);
2385 if (network.
getNodes().isEmpty())
return false;
2387 m_network = std::make_unique<NetworkObject>();
2388 m_network->load(network);
2389 m_network->setVisible(
true);
2392 m_model->addNetwork(network, name);
2394 m_sceneDirty =
true; update();
2402 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
2403 profile.network = visible;
2404 m_networkVisible = visible;
2405 if (m_network) m_network->setVisible(visible);
2406 saveMultiViewSettings();
2407 m_sceneDirty =
true; update();
2415 m_network->setThreshold(threshold);
2416 m_sceneDirty =
true; update();
2425 m_network->setColormap(name);
2426 m_sceneDirty =
true; update();
2435 if (srcSpace.isEmpty())
return false;
2436 m_model->addSourceSpace(srcSpace);
2444 auto &profile = visibilityProfileForTarget(m_visualizationEditTarget);
2445 profile.sourceSpace = visible;
2446 saveMultiViewSettings();
2447 m_sceneDirty =
true; update();
2458 m_headToMriTrans = trans;
2459 refreshSensorTransforms();
2463void BrainView::refreshSensorTransforms()
2466 if (m_applySensorTrans && !m_headToMriTrans.
isEmpty()) {
2471 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
2472 if ((it.key().startsWith(
"sens_") || it.key().startsWith(
"dig_")) && it.value()) {
2473 it.value()->applyTransform(qmat);
2478 if (m_fieldMapper.isLoaded()) {
2479 m_fieldMapper.buildMapping(m_surfaces, m_headToMriTrans, m_applySensorTrans);
2480 m_fieldMapper.apply(m_surfaces, m_singleView, m_subViews);
2489 const QSize outputSize = size();
2491 const auto enabledViewports = enabledViewportIndices();
2493 const int numEnabled = enabledViewports.size();
2495 QRect activePane(0, 0, outputSize.width(), outputSize.height());
2497 bool hasValidPane =
true;
2498 if (m_viewMode ==
MultiView && numEnabled > 1) {
2499 bool foundSlot =
false;
2500 for (
int slot = 0; slot < numEnabled; ++slot) {
2501 const QRect pane = multiViewSlotRect(slot, numEnabled, outputSize);
2502 if (pane.contains(pos)) {
2510 hasValidPane = foundSlot;
2513 const int vp = (m_viewMode ==
MultiView) ? enabledViewports[activeSlot] : 0;
2514 const SubView &sv = (m_viewMode ==
MultiView) ? m_subViews[vp] : m_singleView;
2516 m_camera.setSceneCenter(m_sceneCenter);
2517 m_camera.setSceneSize(m_sceneSize);
2518 m_camera.setRotation(m_cameraRotation);
2519 m_camera.setZoom(m_zoom);
2520 const float aspect = float(std::max(1, activePane.width())) / float(std::max(1, activePane.height()));
2522 ? m_camera.computeMultiView(sv, aspect)
2523 : m_camera.computeSingleView(aspect);
2527 QVector3D rayOrigin, rayDir;
2534 pickResult =
RayPicker::pick(rayOrigin, rayDir, sv, m_surfaces, m_itemSurfaceMap, m_itemDipoleMap);
2536 m_hasIntersection = pickResult.
hit;
2537 if (pickResult.
hit) {
2538 m_lastIntersectionPoint = pickResult.
hitPoint;
2541 QStandardItem *hitItem = pickResult.
item;
2546 const QString &hitKey = pickResult.
surfaceKey;
2547 int currentRegionId = pickResult.
regionId;
2549 if (displayLabel != m_hoveredRegion) {
2550 m_hoveredRegion = displayLabel;
2552 if (m_regionLabel) {
2553 if (m_hoveredRegion.isEmpty()) {
2554 m_regionLabel->hide();
2556 m_regionLabel->setText(m_hoveredRegion);
2557 m_regionLabel->show();
2562 QString hoveredSurfaceKey;
2563 if (hitKey.startsWith(
"sens_surface_meg")) {
2564 hoveredSurfaceKey = hitKey;
2567 if (hitItem != m_hoveredItem || hitIndex != m_hoveredIndex || hoveredSurfaceKey != m_hoveredSurfaceKey) {
2569 if (m_hoveredItem) {
2570 if (m_itemSurfaceMap.contains(m_hoveredItem)) {
2571 m_itemSurfaceMap[m_hoveredItem]->setSelected(
false);
2572 m_itemSurfaceMap[m_hoveredItem]->setSelectedRegion(-1);
2573 m_itemSurfaceMap[m_hoveredItem]->setSelectedVertexRange(-1, 0);
2574 }
else if (m_itemDipoleMap.contains(m_hoveredItem)) {
2575 m_itemDipoleMap[m_hoveredItem]->setSelected(m_hoveredIndex,
false);
2578 if (!m_hoveredSurfaceKey.isEmpty() && m_surfaces.contains(m_hoveredSurfaceKey)) {
2579 m_surfaces[m_hoveredSurfaceKey]->setSelected(
false);
2580 m_surfaces[m_hoveredSurfaceKey]->setSelectedRegion(-1);
2581 m_surfaces[m_hoveredSurfaceKey]->setSelectedVertexRange(-1, 0);
2584 m_hoveredItem = hitItem;
2585 m_hoveredIndex = hitIndex;
2586 m_hoveredSurfaceKey = hoveredSurfaceKey;
2588 if (m_hoveredItem) {
2590 if (m_itemSurfaceMap.contains(m_hoveredItem)) {
2593 bool isDigitizer = absHitSel &&
2596 if (isDigitizer && m_hoveredIndex >= 0) {
2598 int sphereIdx = m_hoveredIndex / vertsPerSphere;
2599 m_itemSurfaceMap[m_hoveredItem]->setSelectedVertexRange(
2600 sphereIdx * vertsPerSphere, vertsPerSphere);
2601 }
else if (currentRegionId != -1) {
2602 m_itemSurfaceMap[m_hoveredItem]->setSelectedRegion(currentRegionId);
2606 m_itemSurfaceMap[m_hoveredItem]->setSelected(
true);
2608 m_itemSurfaceMap[m_hoveredItem]->setSelected(
true);
2609 m_itemSurfaceMap[m_hoveredItem]->setSelectedRegion(-1);
2611 }
else if (m_itemDipoleMap.contains(m_hoveredItem)) {
2612 m_itemDipoleMap[m_hoveredItem]->setSelected(m_hoveredIndex,
true);
2614 }
else if (!m_hoveredSurfaceKey.isEmpty() && m_surfaces.contains(m_hoveredSurfaceKey)) {
2615 m_surfaces[m_hoveredSurfaceKey]->setSelected(
true);
2616 m_surfaces[m_hoveredSurfaceKey]->setSelectedRegion(-1);
2618 }
else if (m_hoveredItem && m_itemSurfaceMap.contains(m_hoveredItem)) {
2620 bool isDigitizer = absHitUpd &&
2623 if (isDigitizer && m_hoveredIndex >= 0) {
2625 int sphereIdx = m_hoveredIndex / vertsPerSphere;
2626 m_itemSurfaceMap[m_hoveredItem]->setSelectedVertexRange(
2627 sphereIdx * vertsPerSphere, vertsPerSphere);
2628 }
else if (currentRegionId != -1) {
2629 m_itemSurfaceMap[m_hoveredItem]->setSelectedRegion(currentRegionId);
2630 m_itemSurfaceMap[m_hoveredItem]->setSelected(
true);
2632 m_itemSurfaceMap[m_hoveredItem]->setSelectedRegion(-1);
2633 m_itemSurfaceMap[m_hoveredItem]->setSelected(
true);
2635 }
else if (!m_hoveredSurfaceKey.isEmpty() && m_surfaces.contains(m_hoveredSurfaceKey)) {
2636 m_surfaces[m_hoveredSurfaceKey]->setSelected(
true);
2638 m_sceneDirty =
true; update();
2643void BrainView::showViewportPresetMenu(
int viewport,
const QPoint &globalPos)
2645 if (viewport < 0 || viewport >= m_subViews.size()) {
2650 QAction *topAction = menu.addAction(
"Top");
2651 QAction *perspectiveAction = menu.addAction(
"Perspective");
2652 QAction *frontAction = menu.addAction(
"Front");
2653 QAction *leftAction = menu.addAction(
"Left");
2654 menu.addSeparator();
2655 QAction *bottomAction = menu.addAction(
"Bottom");
2656 QAction *backAction = menu.addAction(
"Back");
2657 QAction *rightAction = menu.addAction(
"Right");
2659 const int currentPreset = std::clamp(m_subViews[viewport].preset, 0, 6);
2660 topAction->setCheckable(
true);
2661 perspectiveAction->setCheckable(
true);
2662 frontAction->setCheckable(
true);
2663 leftAction->setCheckable(
true);
2664 bottomAction->setCheckable(
true);
2665 backAction->setCheckable(
true);
2666 rightAction->setCheckable(
true);
2668 topAction->setChecked(currentPreset == 0);
2669 perspectiveAction->setChecked(currentPreset == 1);
2670 frontAction->setChecked(currentPreset == 2);
2671 leftAction->setChecked(currentPreset == 3);
2672 bottomAction->setChecked(currentPreset == 4);
2673 backAction->setChecked(currentPreset == 5);
2674 rightAction->setChecked(currentPreset == 6);
2676 QAction *selected = menu.exec(globalPos);
2681 int newPreset = currentPreset;
2682 if (selected == topAction) {
2684 }
else if (selected == perspectiveAction) {
2686 }
else if (selected == frontAction) {
2688 }
else if (selected == leftAction) {
2690 }
else if (selected == bottomAction) {
2692 }
else if (selected == backAction) {
2694 }
else if (selected == rightAction) {
2698 if (newPreset == currentPreset) {
2702 m_subViews[viewport].preset = newPreset;
2703 saveMultiViewSettings();
2704 updateOverlayLayout();
2705 m_sceneDirty =
true; update();
2716void BrainView::removeSurfacesByPrefix(
const QString &prefix)
2719 QStringList keysToRemove;
2720 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
2721 if (it.key().startsWith(prefix))
2722 keysToRemove << it.key();
2724 for (
const QString &key : keysToRemove)
2725 m_surfaces.remove(key);
2728 QList<const QStandardItem*> itemsToRemove;
2729 for (
auto it = m_itemSurfaceMap.cbegin(); it != m_itemSurfaceMap.cend(); ++it) {
2731 for (
const QString &key : keysToRemove) {
2733 for (
auto sit = m_surfaces.cbegin(); sit != m_surfaces.cend(); ++sit) {
2734 if (sit.value() == it.value()) { found =
true;
break; }
2738 bool stillPresent =
false;
2739 for (
auto sit = m_surfaces.cbegin(); sit != m_surfaces.cend(); ++sit) {
2740 if (sit.value() == it.value()) { stillPresent =
true;
break; }
2743 itemsToRemove << it.key();
2745 for (
const QStandardItem *item : itemsToRemove) {
2746 m_itemSurfaceMap.remove(item);
2749 QStandardItem *mutableItem =
const_cast<QStandardItem*
>(item);
2750 if (mutableItem->parent())
2751 mutableItem->parent()->removeRow(mutableItem->row());
2753 m_model->removeRow(mutableItem->row());
2763 QStringList keysToRemove;
2764 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
2765 if (it.key().startsWith(
"lh_") || it.key().startsWith(
"rh_"))
2766 keysToRemove << it.key();
2770 for (
auto it = m_itemSurfaceMap.begin(); it != m_itemSurfaceMap.end(); ) {
2771 bool remove =
false;
2772 for (
const QString &key : keysToRemove) {
2773 if (m_surfaces.contains(key) && m_surfaces[key] == it.value()) {
2780 QStandardItem *mutableItem =
const_cast<QStandardItem*
>(it.key());
2781 if (mutableItem->parent())
2782 mutableItem->parent()->removeRow(mutableItem->row());
2784 m_model->removeRow(mutableItem->row());
2786 it = m_itemSurfaceMap.erase(it);
2792 for (
const QString &key : keysToRemove)
2793 m_surfaces.remove(key);
2795 m_activeSurface.reset();
2796 m_activeSurfaceType.clear();
2797 updateSceneBounds();
2798 m_sceneDirty =
true; update();
2805 QStringList keysToRemove;
2806 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
2807 if (it.key().startsWith(
"bem_"))
2808 keysToRemove << it.key();
2811 for (
auto it = m_itemSurfaceMap.begin(); it != m_itemSurfaceMap.end(); ) {
2812 bool remove =
false;
2813 for (
const QString &key : keysToRemove) {
2814 if (m_surfaces.contains(key) && m_surfaces[key] == it.value()) {
2821 QStandardItem *mutableItem =
const_cast<QStandardItem*
>(it.key());
2822 if (mutableItem->parent())
2823 mutableItem->parent()->removeRow(mutableItem->row());
2825 m_model->removeRow(mutableItem->row());
2827 it = m_itemSurfaceMap.erase(it);
2833 for (
const QString &key : keysToRemove)
2834 m_surfaces.remove(key);
2836 updateSceneBounds();
2837 m_sceneDirty =
true; update();
2844 m_sourceManager.stopStreaming();
2845 for (
auto it = m_surfaces.begin(); it != m_surfaces.end(); ++it) {
2846 if (it.key().startsWith(
"lh_") || it.key().startsWith(
"rh_")) {
2847 it.value()->clearSourceEstimateColors();
2850 m_sceneDirty =
true; update();
2860 for (
auto it = m_itemDipoleMap.begin(); it != m_itemDipoleMap.end(); ) {
2862 QStandardItem *mutableItem =
const_cast<QStandardItem*
>(it.key());
2863 if (mutableItem->parent())
2864 mutableItem->parent()->removeRow(mutableItem->row());
2866 m_model->removeRow(mutableItem->row());
2868 it = m_itemDipoleMap.erase(it);
2870 m_sceneDirty =
true; update();
2877 QStringList keysToRemove;
2878 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
2879 if (it.key().startsWith(
"srcsp_"))
2880 keysToRemove << it.key();
2883 for (
auto it = m_itemSurfaceMap.begin(); it != m_itemSurfaceMap.end(); ) {
2884 bool remove =
false;
2885 for (
const QString &key : keysToRemove) {
2886 if (m_surfaces.contains(key) && m_surfaces[key] == it.value()) {
2893 QStandardItem *mutableItem =
const_cast<QStandardItem*
>(it.key());
2894 if (mutableItem->parent())
2895 mutableItem->parent()->removeRow(mutableItem->row());
2897 m_model->removeRow(mutableItem->row());
2899 it = m_itemSurfaceMap.erase(it);
2905 for (
const QString &key : keysToRemove)
2906 m_surfaces.remove(key);
2908 updateSceneBounds();
2909 m_sceneDirty =
true; update();
2916 QStringList keysToRemove;
2917 for (
auto it = m_surfaces.cbegin(); it != m_surfaces.cend(); ++it) {
2918 if (it.key().startsWith(
"sens_") || it.key().startsWith(
"dig_"))
2919 keysToRemove << it.key();
2922 for (
auto it = m_itemSurfaceMap.begin(); it != m_itemSurfaceMap.end(); ) {
2923 bool remove =
false;
2924 for (
const QString &key : keysToRemove) {
2925 if (m_surfaces.contains(key) && m_surfaces[key] == it.value()) {
2932 QStandardItem *mutableItem =
const_cast<QStandardItem*
>(it.key());
2933 if (mutableItem->parent())
2934 mutableItem->parent()->removeRow(mutableItem->row());
2936 m_model->removeRow(mutableItem->row());
2938 it = m_itemSurfaceMap.erase(it);
2944 for (
const QString &key : keysToRemove)
2945 m_surfaces.remove(key);
2947 m_devHeadTrans = QMatrix4x4();
2948 m_hasDevHead =
false;
2949 updateSceneBounds();
2950 m_sceneDirty =
true; update();
2958 m_sensorStreamManager.stopStreaming();
2959 m_sceneDirty =
true; update();
2967 refreshSensorTransforms();
2968 m_sceneDirty =
true; update();
2976 m_networkVisible =
false;
2980 for (
int r = m_model->rowCount() - 1; r >= 0; --r) {
2981 QStandardItem *item = m_model->item(r);
2982 if (item && item->text() ==
"Networks") {
2983 m_model->removeRow(r);
2988 m_sceneDirty =
true; update();
DataLoader — static helpers for loading MNE data files.
FsSurface key constants and type-to-key mappings.
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)
BrainView class declaration.
BrainRenderer class declaration.
RayPicker class declaration — ray casting and intersection testing.
MeshFactory class declaration — static utilities for generating primitive meshes (spheres,...
BrainSurface class declaration.
DipoleObject class declaration.
NetworkObject class declaration.
BrainTreeModel class declaration.
SourceSpaceTreeItem class declaration.
SensorTreeItem class declaration.
DigitizerTreeItem class declaration.
BemTreeItem class declaration.
DipoleTreeItem class declaration.
SurfaceTreeItem class declaration.
FiffEvokedSet class declaration.
MNEBem class declaration.
MNESourceSpaces class declaration.
Network class declaration.
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
QString sensorParentToKeyPrefix(const QString &parentText)
QString sensorTypeToObjectKey(const QString &uiType)
QMatrix4x4 toQMatrix4x4(const Eigen::Matrix4f &m)
This class holds information about a network, can compute a distance table and provide network metric...
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 > 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
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 setBemHighContrast(bool enabled)
void setSourceColormap(const QString &name)
bool loadMegHelmetSurface(const QString &helmetFilePath)
void setHemiVisible(int hemiIdx, bool visible)
void setSensorFieldTimePoint(int index)
void sourceThresholdsUpdated(float min, float mid, float max)
void setInfoPanelVisible(bool visible)
void clearSourceEstimate()
int stcNumTimePoints() const
bool loadTransformation(const QString &transPath)
bool loadDipoles(const QString &dipPath)
void wheelEvent(QWheelEvent *event) override
bool sensorFieldTimeRange(float &tmin, float &tmax) const
void sourceEstimateLoaded(int numTimePoints)
bool loadNetwork(const CONNLIB::Network &network, const QString &name="Network")
void setSensorFieldContourVisible(const QString &type, bool visible)
bool megFieldMapOnHeadForTarget(int target) const
void setShaderMode(const QString &mode)
void setNetworkColormap(const QString &name)
int closestSensorFieldIndex(float timeSec) const
bool isRealtimeSensorStreaming() const
void resizeEvent(QResizeEvent *event) override
int closestStcIndex(float timeSec) const
QString bemShaderModeForTarget(int target) const
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
bool objectVisibleForTarget(const QString &object, int target) const
void setRealtimeSensorAverages(int numAvr)
void setSourceThresholds(float min, float mid, float max)
void mouseMoveEvent(QMouseEvent *event) override
void pushRealtimeSourceData(const Eigen::VectorXd &data)
void visualizationEditTargetChanged(int target)
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 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 setSensorTransEnabled(bool enabled)
void startRealtimeStreaming()
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 setActiveSurface(const QString &type)
void sensorFieldTimePointChanged(int index, float time)
bool isRealtimeStreaming() const
void setViewCount(int count)
void setMegHelmetOverride(const QString &path)
int viewportCameraPreset(int index) const
void setDipoleVisible(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 sensorFieldLoaded(int numTimePoints, int initialTimePoint=0)
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)
Coordinate transformation description.
Eigen::Matrix< float, 4, 4, Eigen::DontAlign > trans
BEM surface provides geometry information.