Skip to content

Commit 07eec2f

Browse files
Add 41 third-party montages (Brainstorm, DIPFIT, EEGLAB)
Expands the catalog from 12 to 53 montages (1619 → 6183 channels) by importing templates that MNE doesn't ship: ANT Waveguard 32–256, BrainProducts ActiCap 65/68/97/128, BrainProducts EasyCap 64/128, Neuroscan MagLink 65 + Quik-cap 64/68/123/128, BioSemi label variants, EGI classic GSN 64v1/64v2/128/256, EGI infant nets (0–2, 2–9, 9–18 months × 32/64/128 + 256), EGI Adult Avg 32/64/128/256, BESA TemplateRed/Yellow 254, Wearable Sensing DSI-24, U562 128. New build script parses Brainstorm .mat, DIPFIT BESA .mat, and EEGLAB .sfp formats; rotates Brainstorm SCS (+X=nose) to MNE head (+Y=nose); sphere-fits for unit normalization with a plausibility guard; drops non-EEG channels by 'Type' or label pattern; falls back to position-based region coloring when labels use grid schemes (E1..EN, BESA A1..H24) without 10-20 hints. UI groups the 53 montages into 11 vendor/family buckets in a scrollable sidebar.
1 parent bd656af commit 07eec2f

6 files changed

Lines changed: 92217 additions & 23 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@
88

99
# Node (if we ever add a build step)
1010
node_modules/
11+
12+
# Third-party montage source files, re-downloaded on demand by
13+
# scripts/build-third-party-montages.py. Not needed in the repo.
14+
scripts/_vendor-cache/

index.html

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
99
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600&display=swap" />
1010
<link rel="stylesheet" href="styles.css?v=4" />
11-
<script src="montages.js?v=5"></script>
11+
<script src="montages.js?v=6"></script>
1212
<script src="bids-loader.js?v=2"></script>
1313
<script src="topo2d.js?v=6"></script>
1414
</head>
@@ -209,14 +209,23 @@ <h3 class="rail-title"><span>Quick actions</span></h3>
209209
const stage2d = document.getElementById('stage-2d');
210210
EEGTopo2D.init(stage2d);
211211

212-
/* Montage list */
212+
/* Montage list — grouped by vendor/family. Keys must exist in MONTAGES. */
213213
const montageList = document.getElementById('montage-list');
214-
const MONTAGE_ORDER = [
215-
'10-20', '10-10', '10-05',
216-
'biosemi16', 'biosemi32', 'biosemi64', 'biosemi128', 'biosemi256',
217-
'egi128', 'egi256',
218-
'mgh60', 'easycap-M1',
214+
const MONTAGE_GROUPS = [
215+
['10–20 system', ['10-20', '10-10', '10-05']],
216+
['BioSemi', ['biosemi16', 'biosemi32', 'biosemi64', 'biosemi64-10-10', 'biosemi128', 'biosemi128-A01', 'biosemi256', 'biosemi256-A001']],
217+
['BrainProducts', ['easycap-M1', 'easycap64', 'easycap128', 'acticap65', 'acticap68', 'acticap97', 'acticap128']],
218+
['ANT Waveguard', ['antwg32', 'antwg64', 'antwg64d', 'antwg128', 'antwg256']],
219+
['Neuroscan', ['neuroscan-maglink-65', 'neuroscan-quikcap-64', 'neuroscan-quikcap-68', 'neuroscan-quikcap-123', 'neuroscan-quikcap-128']],
220+
['EGI HydroCel', ['egi128', 'egi256']],
221+
['EGI classic GSN', ['egi-gsn-64v1', 'egi-gsn-64v2', 'egi-gsn-128', 'egi-gsn-256']],
222+
['EGI infant nets', ['egi-infant-0-2-32', 'egi-infant-0-2-64', 'egi-infant-0-2-128', 'egi-infant-2-9-32', 'egi-infant-2-9-64', 'egi-infant-2-9-128', 'egi-infant-9-18-32', 'egi-infant-9-18-64', 'egi-infant-9-18-128', 'egi-infant-9-18-256']],
223+
['EGI adult avg', ['egi-adult-32', 'egi-adult-64', 'egi-adult-128', 'egi-adult-256']],
224+
['BESA HD-EEG', ['besa-red-254', 'besa-yellow-254']],
225+
['Other', ['mgh60', 'dsi24', 'u562-128']],
219226
];
227+
const MONTAGE_ORDER = MONTAGE_GROUPS.flatMap(([, keys]) => keys).filter(k => MONTAGES[k]);
228+
220229
function renderMontageButton(btn, label, count) {
221230
btn.textContent = '';
222231
const name = document.createElement('span');
@@ -227,14 +236,22 @@ <h3 class="rail-title"><span>Quick actions</span></h3>
227236
btn.append(name, countEl);
228237
}
229238

230-
MONTAGE_ORDER.forEach(key => {
231-
const m = MONTAGES[key];
232-
const btn = document.createElement('button');
233-
btn.className = 'montage-btn';
234-
btn.dataset.key = key;
235-
renderMontageButton(btn, m.label, m.count);
236-
btn.addEventListener('click', () => setMontage(key));
237-
montageList.appendChild(btn);
239+
MONTAGE_GROUPS.forEach(([groupLabel, keys]) => {
240+
const present = keys.filter(k => MONTAGES[k]);
241+
if (!present.length) return;
242+
const head = document.createElement('div');
243+
head.className = 'montage-group-head';
244+
head.textContent = groupLabel;
245+
montageList.appendChild(head);
246+
present.forEach(key => {
247+
const m = MONTAGES[key];
248+
const btn = document.createElement('button');
249+
btn.className = 'montage-btn';
250+
btn.dataset.key = key;
251+
renderMontageButton(btn, m.label, m.count);
252+
btn.addEventListener('click', () => setMontage(key));
253+
montageList.appendChild(btn);
254+
});
238255
});
239256

240257
/* ========= BIDS loader wiring (electrodes.tsv + coordsystem.json) ========= */

0 commit comments

Comments
 (0)