Skip to content

Commit 59051e6

Browse files
committed
further refactoring of disp3d by adding source space visulaization and revoked codecov
1 parent 7ca4d15 commit 59051e6

File tree

17 files changed

+428
-30
lines changed

17 files changed

+428
-30
lines changed

.github/workflows/pullrequest.yml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,6 @@ jobs:
290290
with:
291291
python-version: '3.7'
292292
architecture: 'x64'
293-
- name: Install Codecov
294-
if: matrix.os == 'ubuntu-latest'
295-
run: |
296-
curl -Os https://uploader.codecov.io/latest/linux/codecov
297-
chmod +x codecov
298-
echo "${pwd}" >> $GITHUB_PATH
299293
- name: Install Qt (Linux|MacOS)
300294
if: (matrix.os == 'ubuntu-20.04') || (matrix.os == 'macos-11')
301295
uses: jurplel/install-qt-action@v3
@@ -320,10 +314,14 @@ jobs:
320314
- name: Run tests (Linux)
321315
if: matrix.os == 'ubuntu-latest'
322316
env:
323-
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
324317
QTEST_FUNCTION_TIMEOUT: 900000
325318
run: |
326319
./tools/test_all.bat verbose withCoverage
320+
- name: Upload coverage to Codecov
321+
if: matrix.os == 'ubuntu-latest'
322+
uses: codecov/codecov-action@v4
323+
with:
324+
token: ${{ secrets.CODECOV_TOKEN }}
327325
- name: Run tests (MacOS)
328326
if: matrix.os == 'macos-latest'
329327
env:

.github/workflows/testci.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,6 @@ jobs:
199199
with:
200200
python-version: '3.10'
201201
architecture: ${{ matrix.os == 'macos-26' && 'arm64' || 'x64' }}
202-
- name: Install Codecov
203-
if: matrix.os == 'ubuntu-24.04'
204-
run: |
205-
sudo pip install codecov
206202
- name: Install Qt (Linux|MacOS)
207203
if: (matrix.os == 'ubuntu-24.04') || (matrix.os == 'macos-26')
208204
uses: jurplel/install-qt-action@v3
@@ -237,10 +233,14 @@ jobs:
237233
- name: Run tests (Linux)
238234
if: matrix.os == 'ubuntu-24.04'
239235
env:
240-
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
241236
QTEST_FUNCTION_TIMEOUT: 900000
242237
run: |
243238
./tools/test_all.bat verbose withCoverage
239+
- name: Upload coverage to Codecov
240+
if: matrix.os == 'ubuntu-24.04'
241+
uses: codecov/codecov-action@v4
242+
with:
243+
token: ${{ secrets.CODECOV_TOKEN }}
244244
- name: Run tests (MacOS)
245245
if: matrix.os == 'macos-26'
246246
env:

src/examples/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ add_subdirectory(ex_compute_forward)
1212

1313
## Examples requiring mne_disp3D (which needs Qt3D) - check if available
1414
find_package(Qt6 QUIET COMPONENTS 3DRender)
15-
if(Qt6_3DRender_FOUND)
15+
if(Qt63DRender_FOUND)
1616
add_subdirectory(ex_clustered_inverse_mne)
1717
add_subdirectory(ex_clustered_inverse_mne_raw)
1818
add_subdirectory(ex_clustered_inverse_pwl_rap_music_raw)

src/examples/ex_brain_view/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ set(SOURCES
2626
model/braintreemodel.cpp
2727
model/items/sensortreeitem.cpp
2828
model/items/dipoletreeitem.cpp
29+
model/items/sourcespacetreeitem.cpp
2930
)
3031

3132
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)

src/examples/ex_brain_view/app/mainwindow.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,20 @@ void MainWindow::setupUI()
299299
dipoleLayout->addWidget(m_loadDipoleBtn);
300300
dipoleLayout->addWidget(m_showDipoleCheck);
301301

302+
// ===== Source Space Group =====
303+
QGroupBox *srcSpaceGroup = new QGroupBox("Source Space");
304+
QVBoxLayout *srcSpaceLayout = new QVBoxLayout(srcSpaceGroup);
305+
srcSpaceLayout->setContentsMargins(10, 15, 10, 10);
306+
srcSpaceLayout->setSpacing(8);
307+
308+
m_loadSrcSpaceBtn = new QPushButton("Load Source Space...");
309+
m_showSrcSpaceCheck = new QCheckBox("Show Source Space");
310+
m_showSrcSpaceCheck->setChecked(false);
311+
m_showSrcSpaceCheck->setEnabled(false);
312+
313+
srcSpaceLayout->addWidget(m_loadSrcSpaceBtn);
314+
srcSpaceLayout->addWidget(m_showSrcSpaceCheck);
315+
302316
// ===== Sensor Group =====
303317
QGroupBox *sensorGroup = new QGroupBox("Sensors");
304318
QVBoxLayout *sensorLayout = new QVBoxLayout(sensorGroup);
@@ -335,6 +349,7 @@ void MainWindow::setupUI()
335349
sideLayout->addWidget(controlGroup);
336350
sideLayout->addWidget(stcGroup);
337351
sideLayout->addWidget(dipoleGroup);
352+
sideLayout->addWidget(srcSpaceGroup);
338353
sideLayout->addWidget(sensorGroup);
339354
sideLayout->addStretch();
340355

@@ -557,6 +572,20 @@ void MainWindow::setupConnections()
557572

558573
connect(m_showDipoleCheck, &QCheckBox::toggled, [this](bool checked) { m_brainView->setDipoleVisible(checked); });
559574

575+
// Source Space
576+
connect(m_loadSrcSpaceBtn, &QPushButton::clicked, [this]() {
577+
QString path = QFileDialog::getOpenFileName(this, "Select Source Space / Forward Solution", "",
578+
"Source Space Files (*-src.fif *-fwd.fif);;All FIF Files (*.fif)");
579+
if (path.isEmpty()) return;
580+
if (m_brainView->loadSourceSpace(path)) {
581+
m_showSrcSpaceCheck->setEnabled(true);
582+
m_showSrcSpaceCheck->setChecked(false);
583+
m_brainView->setSourceSpaceVisible(false);
584+
}
585+
});
586+
587+
connect(m_showSrcSpaceCheck, &QCheckBox::toggled, [this](bool checked) { m_brainView->setSourceSpaceVisible(checked); });
588+
560589
// Sync initial state
561590
m_brainView->setHemiVisible(0, m_lhCheck->isChecked());
562591
m_brainView->setHemiVisible(1, m_rhCheck->isChecked());
@@ -572,7 +601,8 @@ void MainWindow::loadInitialData(const QString &subjectPath,
572601
const QString &bemPath,
573602
const QString &transPath,
574603
const QString &stcPath,
575-
const QString &digitizerPath)
604+
const QString &digitizerPath,
605+
const QString &srcSpacePath)
576606
{
577607
qDebug() << "Loading surfaces...";
578608

@@ -604,6 +634,16 @@ void MainWindow::loadInitialData(const QString &subjectPath,
604634
m_brainView->loadTransformation(transPath);
605635
}
606636

637+
// Auto-load source space
638+
if (!srcSpacePath.isEmpty() && QFile::exists(srcSpacePath)) {
639+
qDebug() << "Auto-loading source space from:" << srcSpacePath;
640+
if (m_brainView->loadSourceSpace(srcSpacePath)) {
641+
m_showSrcSpaceCheck->setEnabled(true);
642+
m_showSrcSpaceCheck->setChecked(false);
643+
m_brainView->setSourceSpaceVisible(false);
644+
}
645+
}
646+
607647
// Auto-load STC
608648
if (!stcPath.isEmpty() && QFile::exists(stcPath)) {
609649
qDebug() << "Auto-loading source estimate from:" << stcPath;

src/examples/ex_brain_view/app/mainwindow.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ class MainWindow : public QMainWindow
9797
const QString &bemPath = QString(),
9898
const QString &transPath = QString(),
9999
const QString &stcPath = QString(),
100-
const QString &digitizerPath = QString());
100+
const QString &digitizerPath = QString(),
101+
const QString &srcSpacePath = QString());
101102

102103
private:
103104
void setupUI();
@@ -159,6 +160,10 @@ class MainWindow : public QMainWindow
159160
// Control widgets - Dipoles
160161
QPushButton *m_loadDipoleBtn = nullptr;
161162
QCheckBox *m_showDipoleCheck = nullptr;
163+
164+
// Control widgets - Source Space
165+
QPushButton *m_loadSrcSpaceBtn = nullptr;
166+
QCheckBox *m_showSrcSpaceCheck = nullptr;
162167
};
163168

164169
#endif // MAINWINDOW_H

src/examples/ex_brain_view/main.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ int main(int argc, char *argv[])
6161
QCommandLineOption transOption("trans", "Transformation file path", "path", "");
6262
QCommandLineOption stcOption("stc", "Source estimate file path", "path", "");
6363
QCommandLineOption digitizerOption("digitizer", "Digitizer/sensor file path", "path", "");
64+
QCommandLineOption srcSpaceOption("srcSpace", "Source space / forward solution file path", "path", "");
6465

65-
parser.addOptions({subjectPathOption, subjectOption, hemiOption, bemOption, transOption, stcOption, digitizerOption});
66+
parser.addOptions({subjectPathOption, subjectOption, hemiOption, bemOption, transOption, stcOption, digitizerOption, srcSpaceOption});
6667
parser.process(app);
6768

6869
MainWindow mainWindow;
@@ -72,7 +73,8 @@ int main(int argc, char *argv[])
7273
parser.value(bemOption),
7374
parser.value(transOption),
7475
parser.value(stcOption),
75-
parser.value(digitizerOption)
76+
parser.value(digitizerOption),
77+
parser.value(srcSpaceOption)
7678
);
7779
mainWindow.show();
7880

src/examples/ex_brain_view/model/braintreemodel.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
#include "bemtreeitem.h"
44
#include "sensortreeitem.h"
55
#include "dipoletreeitem.h"
6+
#include "sourcespacetreeitem.h"
67
#include <inverse/dipoleFit/ecd_set.h>
8+
#include <mne/mne_hemisphere.h>
79

810
BrainTreeModel::BrainTreeModel(QObject *parent)
911
: QStandardItemModel(parent)
@@ -133,3 +135,43 @@ void BrainTreeModel::addDipoles(const INVERSELIB::ECDSet &set)
133135
item->setCheckState(Qt::Checked);
134136
this->appendRow(item);
135137
}
138+
139+
//=============================================================================================================
140+
141+
void BrainTreeModel::addSourceSpace(const MNELIB::MNESourceSpace &srcSpace)
142+
{
143+
QStandardItem* parentItem = new QStandardItem("Source Space");
144+
parentItem->setCheckable(true);
145+
parentItem->setCheckState(Qt::Checked);
146+
147+
// Color: LH = bright cyan, RH = bright magenta
148+
QColor lhColor(0, 230, 180); // Vibrant cyan-green for left hemisphere
149+
QColor rhColor(230, 80, 230); // Vibrant magenta for right hemisphere
150+
151+
for (int h = 0; h < srcSpace.size(); ++h) {
152+
const MNELIB::MNEHemisphere &hemi = srcSpace[h];
153+
QColor color = (h == 0) ? lhColor : rhColor;
154+
QString hemiLabel = (h == 0) ? "LH" : "RH";
155+
156+
// Collect all source point positions for this hemisphere
157+
QVector<QVector3D> positions;
158+
positions.reserve(hemi.vertno.size());
159+
for (int i = 0; i < hemi.vertno.size(); ++i) {
160+
int vIdx = hemi.vertno(i);
161+
if (vIdx < 0 || vIdx >= hemi.rr.rows()) continue;
162+
positions.append(QVector3D(hemi.rr(vIdx, 0), hemi.rr(vIdx, 1), hemi.rr(vIdx, 2)));
163+
}
164+
165+
// One item per hemisphere with all positions batched
166+
parentItem->appendRow(new SourceSpaceTreeItem(hemiLabel, positions, color, 0.00025f));
167+
qDebug() << "BrainTreeModel: Source space" << hemiLabel
168+
<< "- points:" << positions.size()
169+
<< "coord_frame:" << hemi.coord_frame;
170+
if (!positions.isEmpty()) {
171+
qDebug() << " First point:" << positions.first()
172+
<< " Last point:" << positions.last();
173+
}
174+
}
175+
176+
this->appendRow(parentItem);
177+
}

src/examples/ex_brain_view/model/braintreemodel.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <fs/surface.h>
66
#include <fs/annotation.h>
77
#include <mne/mne_bem_surface.h>
8+
#include <mne/mne_sourcespace.h>
89

910
namespace INVERSELIB {
1011
class ECDSet;
@@ -35,6 +36,9 @@ class BrainTreeModel : public QStandardItemModel
3536
void addSensors(const QString &type, const QList<QStandardItem*> &items);
3637
void addDipoles(const INVERSELIB::ECDSet &set);
3738

39+
// Add source space points
40+
void addSourceSpace(const MNELIB::MNESourceSpace &srcSpace);
41+
3842
private:
3943
// Helpers to find specific items
4044
QStandardItem* getSubjectItem(const QString &subject);

src/examples/ex_brain_view/model/items/abstracttreeitem.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ class AbstractTreeItem : public QStandardItem
2222
SurfaceItem,
2323
BemItem,
2424
SensorItem,
25-
DipoleItem
25+
DipoleItem,
26+
SourceSpaceItem
2627
};
2728

2829
explicit AbstractTreeItem(const QString &text = "", int type = AbstractItem);

0 commit comments

Comments
 (0)