From 9912dab8bf600c24da413140794fc7cb07a4e909 Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Wed, 8 Jul 2026 10:56:20 -0400 Subject: [PATCH 1/7] feat(autobidsify): add EEG file-type detection and modality options --- src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx | 4 +++- .../User/Dashboard/DatasetOrganizer/utils/fileProcessors.ts | 3 +++ src/redux/projects/types/projects.interface.ts | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx index f96c413..a8bf62e 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx +++ b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx @@ -1590,6 +1590,8 @@ const LLMPanel: React.FC = ({ MRI NIRS + EEG + iEEG Mixed {modalityError && ( @@ -1605,7 +1607,7 @@ const LLMPanel: React.FC = ({ setDescribeText(e.target.value)} size="small" diff --git a/src/components/User/Dashboard/DatasetOrganizer/utils/fileProcessors.ts b/src/components/User/Dashboard/DatasetOrganizer/utils/fileProcessors.ts index aacea87..3598887 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/utils/fileProcessors.ts +++ b/src/components/User/Dashboard/DatasetOrganizer/utils/fileProcessors.ts @@ -30,6 +30,9 @@ export const getFileType = (name: string): string => { matlab: ["mat"], dicom: ["dcm"], nirs: ["nirs"], + eegEdf: ["edf", "bdf"], + eegBrainvision: ["vhdr", "vmrk", "eeg"], + eegEeglab: ["set", "fdt"], }; for (const [type, extensions] of Object.entries(fileTypes)) { diff --git a/src/redux/projects/types/projects.interface.ts b/src/redux/projects/types/projects.interface.ts index af9f319..d6ef75b 100644 --- a/src/redux/projects/types/projects.interface.ts +++ b/src/redux/projects/types/projects.interface.ts @@ -14,6 +14,9 @@ export interface FileItem { | "matlab" | "dicom" | "nirs" + | "eegEdf" + | "eegBrainvision" + | "eegEeglab" | "array" | "other"; content?: string; From 769793debfe11174d292b15ba58359ef8e747bc3 Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Wed, 8 Jul 2026 10:58:11 -0400 Subject: [PATCH 2/7] feat(autobidsify):remove ieeg option --- src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx index a8bf62e..63cc92f 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx +++ b/src/components/User/Dashboard/DatasetOrganizer/LLMPanel.tsx @@ -1591,7 +1591,6 @@ const LLMPanel: React.FC = ({ MRI NIRS EEG - iEEG Mixed {modalityError && ( From 02e8a7362444e00e7aefd2a3e143aee0aaf7d4ab Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Wed, 8 Jul 2026 11:11:25 -0400 Subject: [PATCH 3/7] feat(autobidsify): recognize EEG files in planning stage (DATA_EXTENSIONS + PROMPT_BIDS_PLAN EEG rules) --- .../Dashboard/DatasetOrganizer/utils/llm.ts | 40 +++++++++++++++++++ .../DatasetOrganizer/utils/plannerHelpers.ts | 5 ++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/components/User/Dashboard/DatasetOrganizer/utils/llm.ts b/src/components/User/Dashboard/DatasetOrganizer/utils/llm.ts index ca06785..1cabf3c 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/utils/llm.ts +++ b/src/components/User/Dashboard/DatasetOrganizer/utils/llm.ts @@ -375,6 +375,15 @@ fNIRS FORMATS (modality: nirs): • Homer3 (.nirs) → convert_to: snirf • MATLAB (.mat) → convert_to: snirf +EEG FORMATS (modality: eeg): + • EDF/EDF+ (.edf) → format_ready: true (copy directly) + • BrainVision (.vhdr) → format_ready: true (copy directly) + • EEGLAB (.set) → format_ready: true (copy directly) + • Biosemi (.bdf) → format_ready: true (copy directly) + CRITICAL: EEG files are NEVER converted. Always format_ready: true, convert_to: none. + CRITICAL: EEG bids_template MUST end with '_eeg.' (e.g. '_eeg.edf'). + NEVER use NIfTI suffixes (T1w, T2w, bold) for EEG data. + ═══════════════════════════════════════════════════════════════════════ SUBJECT IDENTIFICATION — MOST IMPORTANT STEP ═══════════════════════════════════════════════════════════════════════ @@ -495,6 +504,23 @@ For MRI: use acq- to distinguish different scan series from same subject. VHFCT1mm-Ankle.dcm → acq-ankle_T1w VHFCT1mm-Head.dcm → acq-head_T1w +For EEG: infer task and run from filename suffixes or directory names. + RULE 1 — If each subject has multiple EEG files, each file is a separate scan. + Identify what differs between files of the same subject (suffix, keyword, directory). + Map each variant to a distinct task- or run- label from user description. + If task labels cannot be inferred, use run-1, run-2, run-N. + RULE 2 — Create one mapping entry per unique file variant across subjects. + RULE 3 — BIDS directory for EEG is always 'eeg/', never 'anat/' or 'nirs/'. + RULE 4 — BIDS filename suffix is always '_eeg' + original extension. + +EEG FILENAME EXAMPLES (CRITICAL — follow exactly): + ✓ sub-01_task-rest_eeg.edf + ✓ sub-01_task-arithmetic_eeg.edf + ✓ sub-01_run-1_eeg.edf + ✗ sub-01_T1w.nii.gz ← NEVER for EEG + ✗ sub-01_unknown.nii.gz ← NEVER for EEG + ✗ sub-01_bold.nii.gz ← NEVER for EEG + ═══════════════════════════════════════════════════════════════════════ OUTPUT FORMAT ═══════════════════════════════════════════════════════════════════════ @@ -527,6 +553,20 @@ mappings: - match_pattern: '.*' bids_template: 'sub-X_task-walking_nirs.snirf' + # EEG example — when each subject has ONE edf file: + - modality: eeg + match: ['**/*.edf'] + exclude: [] + format_ready: true + convert_to: none + filename_rules: + - match_pattern: '.*' + bids_template: 'sub-X_task-rest_eeg.edf' + + # EEG example — when each subject has MULTIPLE edf files (different tasks/runs): + # Create one mapping entry per task/run, use match_pattern to distinguish them. + # The match_pattern must be derived from what actually differs in the filenames. + OUTPUT: Raw YAML only (no markdown, no explanation)`; export const PROMPT_MAT_SNIRF_MAPPING = `You are an fNIRS data format expert. diff --git a/src/components/User/Dashboard/DatasetOrganizer/utils/plannerHelpers.ts b/src/components/User/Dashboard/DatasetOrganizer/utils/plannerHelpers.ts index 820c61f..fba2ed4 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/utils/plannerHelpers.ts +++ b/src/components/User/Dashboard/DatasetOrganizer/utils/plannerHelpers.ts @@ -70,9 +70,10 @@ export interface BuildBidsPlanResult { // Mirrors planner.py _DATA_EXTS, evidence.py TRIO_NAMES // ============================================================================ +// Mirrors planner.py _DATA_EXTS — EEG primary formats (edf/vhdr/set/bdf) +// are data files; EEG aux (.vmrk/.eeg/.fdt) are handled separately, not here. const DATA_EXTENSIONS = - // /\.(snirf|nii|nii\.gz|dcm|mat|nirs|jnii|bnii|h5|hdf5|edf|bdf)$/i; - /\.(snirf|nii|nii\.gz|dcm|mat|nirs|jnii|bnii)$/i; + /\.(snirf|nii|nii\.gz|dcm|mat|nirs|jnii|bnii|edf|vhdr|set|bdf)$/i; const TRIO_FILENAMES = new Set([ "dataset_description.json", From 8281e7f3794ee0b21e282976a048e5f22bc39356 Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Wed, 8 Jul 2026 11:41:01 -0400 Subject: [PATCH 4/7] feat(autobidsify): classify EEG kind and modality in evidence stage. --- .../DatasetOrganizer/utils/fileAnalyzers.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/components/User/Dashboard/DatasetOrganizer/utils/fileAnalyzers.ts b/src/components/User/Dashboard/DatasetOrganizer/utils/fileAnalyzers.ts index 8318a87..ebc68d1 100644 --- a/src/components/User/Dashboard/DatasetOrganizer/utils/fileAnalyzers.ts +++ b/src/components/User/Dashboard/DatasetOrganizer/utils/fileAnalyzers.ts @@ -81,6 +81,19 @@ export const categorizeFile = (file: FileItem): string => { ) return "nirs"; + // eeg — mirrors EEG_EXT = {'.edf', '.vhdr', '.set', '.bdf'} + if ([".edf", ".vhdr", ".set", ".bdf"].some((e) => name.endsWith(e))) + return "eeg"; + + // eeg_aux — mirrors EEG_AUX_EXT {'.vmrk', '.eeg', '.fdt'} + // + EEG_EVENT_EXT {'.event', '.events', '.evt', '.mrk'} + if ( + [".vmrk", ".eeg", ".fdt", ".event", ".events", ".evt", ".mrk"].some((e) => + name.endsWith(e) + ) + ) + return "eeg_aux"; + // mri — mirrors MRI_EXT = {'.nii', '.dcm'} + .nii.gz if ( name.endsWith(".nii.gz") || @@ -140,6 +153,13 @@ export const detectModality = (files: FileItem[]): string => { files.some((f) => f.name.endsWith(".snirf")) ) return "nirs"; + // eeg — fileType keys from fileProcessors.ts getFileType() + if ( + counts.eegEdf > 0 || + counts.eegBrainvision > 0 || + counts.eegEeglab > 0 + ) + return "eeg"; return "mixed"; }; From 63efdbe059ed497516b85b10d248c33a6e360a5b Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Mon, 13 Jul 2026 12:08:56 -0400 Subject: [PATCH 5/7] feat(search): surface AI-summary matches on dataset cards --- src/components/SearchPage/DatasetCard.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/SearchPage/DatasetCard.tsx b/src/components/SearchPage/DatasetCard.tsx index 7ecefae..85e4978 100644 --- a/src/components/SearchPage/DatasetCard.tsx +++ b/src/components/SearchPage/DatasetCard.tsx @@ -40,6 +40,7 @@ interface DatasetCardProps { value: { name?: string; readme?: string; + aisummary?: string; modality?: string[]; subj?: string[]; info?: { @@ -101,8 +102,11 @@ function findMatchSnippet( ): { label: string; html: string } | null { if (!kw) return null; - // Which fields to scan (can add/remove fields here) + // Which fields to scan (can add/remove fields here). + // "AI Summary" is first so a topic-word hit in the generated summary is the + // explanation shown (its text lives in the dbinfo view's `aisummary` field). const CANDIDATE_FIELDS: Array<[string, (v: any) => string | undefined]> = [ + ["AI Summary", (v) => v?.aisummary], ["Acknowledgements", (v) => v?.info?.Acknowledgements], [ "Funding", @@ -377,7 +381,8 @@ const DatasetCard: React.FC = ({ paragraph sx={{ textOverflow: "ellipsis" }} > - Summary: {highlightKeyword(readme, keyword)} + README: {highlightKeyword(readme, keyword)} + {readme.length >= 256 ? "…" : ""} )} From db79d858937c0b19be64abd79999b3ac8fa63337 Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Mon, 13 Jul 2026 12:26:11 -0400 Subject: [PATCH 6/7] feat(search): explain matches in info.Description on dataset cards --- src/components/SearchPage/DatasetCard.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/components/SearchPage/DatasetCard.tsx b/src/components/SearchPage/DatasetCard.tsx index 85e4978..25d2505 100644 --- a/src/components/SearchPage/DatasetCard.tsx +++ b/src/components/SearchPage/DatasetCard.tsx @@ -40,7 +40,7 @@ interface DatasetCardProps { value: { name?: string; readme?: string; - aisummary?: string; + aisummary?: string | Record; modality?: string[]; subj?: string[]; info?: { @@ -95,6 +95,17 @@ const containsKeyword = (text?: string, kw?: string) => { return words.some((w) => t.includes(w)); }; +/** AISummary can be a plain string OR a sectioned object + * ({Introduction, Methods, Results, Conclusion}). Flatten to one string so we + * can search/snippet it — mirrors the detail page's handling. */ +const flattenAiSummary = (s: any): string | undefined => { + if (!s) return undefined; + if (typeof s === "string") return s; + if (typeof s === "object") + return Object.values(s).filter(Boolean).join(" "); + return undefined; +}; + /** Find a short snippet in secondary fields if not already visible */ function findMatchSnippet( v: any, @@ -106,7 +117,8 @@ function findMatchSnippet( // "AI Summary" is first so a topic-word hit in the generated summary is the // explanation shown (its text lives in the dbinfo view's `aisummary` field). const CANDIDATE_FIELDS: Array<[string, (v: any) => string | undefined]> = [ - ["AI Summary", (v) => v?.aisummary], + ["AI Summary", (v) => flattenAiSummary(v?.aisummary)], + ["Description", (v) => v?.info?.Description], ["Acknowledgements", (v) => v?.info?.Acknowledgements], [ "Funding", From 7de0af8e0a7c448dea27688a8fb150063806c065 Mon Sep 17 00:00:00 2001 From: elainefan331 Date: Thu, 6 Aug 2026 12:20:19 -0400 Subject: [PATCH 7/7] fix: replace broken OrbitControls URL to restore 3D preview --- src/utils/preview.js | 138 +++++++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 65 deletions(-) diff --git a/src/utils/preview.js b/src/utils/preview.js index 231a85e..14f37ef 100644 --- a/src/utils/preview.js +++ b/src/utils/preview.js @@ -200,7 +200,7 @@ function drawpreview(cfg) { // console.log("🔄 Converting MeshNode & MeshSurf to ndarrays..."); drawsurf( nj.array(cfg.MeshNode, "float32"), - nj.array(cfg.MeshSurf, "uint32") + nj.array(cfg.MeshSurf, "uint32"), ); } else { // console.log("🔄 Converting MeshNode & MeshSurf from plain arrays..."); @@ -210,7 +210,7 @@ function drawpreview(cfg) { .reshape(cfg.MeshNode.length / 3, 3), nj .array(Array.from(cfg.MeshSurf), "uint32") - .reshape(cfg.MeshSurf.length / 3, 3) + .reshape(cfg.MeshSurf.length / 3, 3), ); } } @@ -298,14 +298,22 @@ function previewdata(key, idx, isinternal, hastime) { // console.log("key in previewdata", key); if (!hasthreejs) { $.when( - $.getScript("https://mcx.space/cloud/js/OrbitControls.js"), + // $.getScript("https://mcx.space/cloud/js/OrbitControls.js"), + $.getScript( + "https://unpkg.com/three@0.145.0/examples/js/controls/OrbitControls.js", + ), $.Deferred(function (deferred) { $(deferred.resolve); + }), + ) + .done(function () { + hasthreejs = true; + dopreview(key, idx, isinternal, hastime); }) - ).done(function () { - hasthreejs = true; - dopreview(key, idx, isinternal, hastime); - }); + .fail(function (_, __, error) { + console.error("Failed to load OrbitControls:", error); + $("#loadingdiv").hide(); + }); } else { dopreview(key, idx, isinternal, hastime); } @@ -426,7 +434,7 @@ function dopreview(key, idx, isinternal, hastime) { "Double-click to restore all signals (Windows)  |  " + "⌘+Click the same selected item to restore all signals (Mac)" + "" + - '
' + '
', ); if (dataroot instanceof nj.NdArray) { @@ -474,7 +482,7 @@ function dopreview(key, idx, isinternal, hastime) { uplotInstance = new uPlot( opts, plotdata, - document.getElementById("plotchart") + document.getElementById("plotchart"), ); // Reset all series on double-click (works on both Mac and Windows) @@ -499,7 +507,7 @@ function dopreview(key, idx, isinternal, hastime) { uplotInstance = new uPlot( opts, [[...Array(dataroot.length).keys()], dataroot], - document.getElementById("plotchart") + document.getElementById("plotchart"), ); // uplotInstance.root.addEventListener("dblclick", (e) => { @@ -536,7 +544,7 @@ function dopreview(key, idx, isinternal, hastime) { $("body").animate( { scrollTop: $("#chartpanel").offset().top - 20 }, - "fast" + "fast", ); } else { if (typeof scene === "undefined") { @@ -601,7 +609,7 @@ function drawshape(shape, index) { boundingbox = createbox( shape.Grid.Size, shape.Grid.hasOwnProperty("O") ? shape.Grid.O : [0, 0, 0], - shape.Grid.Tag + shape.Grid.Tag, ); const geo = new THREE.EdgesGeometry(boundingbox.geometry); const mat = new THREE.LineDashedMaterial({ @@ -619,13 +627,13 @@ function drawshape(shape, index) { controls.target.set( shape.Grid.Size[0] * 0.5 + shape.Grid.O[0], shape.Grid.Size[1] * 0.5 + shape.Grid.O[1], - shape.Grid.Size[2] * 0.5 + shape.Grid.O[2] + shape.Grid.Size[2] * 0.5 + shape.Grid.O[2], ); else controls.target.set( shape.Grid.Size[0] * 0.5, shape.Grid.Size[1] * 0.5, - shape.Grid.Size[2] * 0.5 + shape.Grid.Size[2] * 0.5, ); break; @@ -634,7 +642,7 @@ function drawshape(shape, index) { break; case "Subgrid": boundingbox.add( - createbox(shape.Subgrid.Size, shape.Subgrid.O, shape.Subgrid.Tag) + createbox(shape.Subgrid.Size, shape.Subgrid.O, shape.Subgrid.Tag), ); break; case "XLayers": @@ -643,7 +651,7 @@ function drawshape(shape, index) { if (shape[keys[0]] != null) for (let i = 0; i < shape[keys[0]].length; i++) boundingbox.add( - createlayer(shape[keys[0]][i], dir[keys[0]], shape[keys[0]][i][2]) + createlayer(shape[keys[0]][i], dir[keys[0]], shape[keys[0]][i][2]), ); break; case "XSlabs": @@ -656,7 +664,7 @@ function drawshape(shape, index) { else for (let i = 0; i < slabs.length; i++) boundingbox.add( - createlayer(slabs[i], dir[keys[0]], shape[keys[0]].Tag) + createlayer(slabs[i], dir[keys[0]], shape[keys[0]].Tag), ); } break; @@ -677,12 +685,12 @@ function drawshape(shape, index) { c0 = new THREE.Vector3( shape.Cylinder.C0[0], shape.Cylinder.C0[1], - shape.Cylinder.C0[2] + shape.Cylinder.C0[2], ); c1 = new THREE.Vector3( shape.Cylinder.C1[0], shape.Cylinder.C1[1], - shape.Cylinder.C1[2] + shape.Cylinder.C1[2], ); dc = c1; height = c0.distanceTo(c1); @@ -690,7 +698,7 @@ function drawshape(shape, index) { shape.Cylinder.R, shape.Cylinder.R, height, - 32 + 32, ); geometry.translate(0, height * 0.5 - 1, 0); geometry.rotateX(Math.PI * 0.5); // orient along z-axis - required @@ -724,7 +732,7 @@ function drawsurf(node, tri) { // console.log("📌 MeshSurf Shape:", tri.shape); $("#mip-radio-button,#iso-radio-button,#interp-radio-button").prop( "disabled", - true + true, ); const geometry = new THREE.BufferGeometry(); @@ -735,7 +743,7 @@ function drawsurf(node, tri) { geometry.setIndex(new THREE.BufferAttribute(tri.selection.data, 1)); geometry.setAttribute( "position", - new THREE.BufferAttribute(node.selection.data, 3) + new THREE.BufferAttribute(node.selection.data, 3), ); geometry.computeVertexNormals(); @@ -792,7 +800,7 @@ function drawsurf(node, tri) { function resetscene(s) { let diag = Math.sqrt(s[0] * s[0] + s[1] * s[1] + s[2] * s[2]); let distcenter = Math.sqrt( - s[0] * s[0] + 1.5 * 1.5 * s[1] * s[1] + 1.5 * 1.5 * s[2] * s[2] + s[0] * s[0] + 1.5 * 1.5 * s[1] * s[1] + 1.5 * 1.5 * s[2] * s[2], ); let near = distcenter - diag; let far = distcenter + diag; @@ -829,7 +837,7 @@ function resetscene(s) { 0.7 * Math.min( $("#canvas").width() / Math.sqrt(s[0] * s[0] + s[1] * s[1]), - $("#canvas").height() / s[2] + $("#canvas").height() / s[2], ); camera.updateProjectionMatrix(); camera.updateMatrix(); @@ -840,7 +848,7 @@ function createbox(bsize, orig, tag) { geometry.translate( bsize[0] * 0.5 + orig[0], bsize[1] * 0.5 + orig[1], - bsize[2] * 0.5 + orig[2] + bsize[2] * 0.5 + orig[2], ); const material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide, @@ -876,7 +884,7 @@ const texture_scale = { function drawvolume(volume) { $("#mip-radio-button,#iso-radio-button,#interp-radio-button").prop( "disabled", - false + false, ); lastvolumedim = volume.shape; @@ -897,20 +905,20 @@ function drawvolume(volume) { $("#cross-t").prop("min") + "," + $("#cross-t").prop("max") + - "]" + "]", ); } lastvolumedata = nj.array( volume.transpose().flatten().selection.data, - "float32" + "float32", ); texture = new THREE.DataTexture3D( lastvolumedata.selection.data, dim[0], dim[1], - dim[2] + dim[2], ); texture.format = THREE.RedFormat; texture.type = THREE.FloatType; @@ -922,11 +930,11 @@ function drawvolume(volume) { const cmtextures = { viridis: new THREE.TextureLoader().load( "https://threejs.org/examples/textures/cm_viridis.png", - render + render, ), gray: new THREE.TextureLoader().load( "https://threejs.org/examples/textures/cm_gray.png", - render + render, ), }; let shader; @@ -943,7 +951,7 @@ function drawvolume(volume) { } catch (e) { console.warn( "⚠️ Shader selection failed, using MipRenderShader by default", - e + e, ); shader = MipRenderShader; // Safe fallback } @@ -962,12 +970,12 @@ function drawvolume(volume) { uniforms["u_minslice"].value.set( parseFloat($("#cross-x-low").val()), parseFloat($("#cross-y-low").val()), - parseFloat($("#cross-z-low").val()) + parseFloat($("#cross-z-low").val()), ); uniforms["u_maxslice"].value.set( parseFloat($("#cross-x-hi").val()), parseFloat($("#cross-y-hi").val()), - parseFloat($("#cross-z-hi").val()) + parseFloat($("#cross-z-hi").val()), ); lastclim = uniforms["u_clim"].value; @@ -978,7 +986,7 @@ function drawvolume(volume) { $("#clim-low").val(lastclim.x); $("#clim-low").prop( "title", - "" + lastclim.x + "[" + lastclim.x + "," + lastclim.y + "]" + "" + lastclim.x + "[" + lastclim.x + "," + lastclim.y + "]", ); $("#clim-hi").prop("disabled", false); @@ -987,7 +995,7 @@ function drawvolume(volume) { $("#clim-hi").val(lastclim.y); $("#clim-hi").prop( "title", - "" + lastclim.y + "[" + lastclim.x + "," + lastclim.y + "]" + "" + lastclim.y + "[" + lastclim.x + "," + lastclim.y + "]", ); $("#isothreshold").prop("disabled", false); @@ -1002,7 +1010,7 @@ function drawvolume(volume) { $("#isothreshold").prop("min") + "," + $("#isothreshold").prop("max") + - "]" + "]", ); $("#x_thickness").prop("max", dim[0]); @@ -1057,7 +1065,7 @@ function initcanvas() { canvas.height() / 2, canvas.height() / -2, 1, - 1000 + 1000, ); camera.up = new THREE.Vector3(0, 0, 1); @@ -1295,13 +1303,13 @@ function initcanvas() { $(this).prop("min") + "," + $(this).prop("max") + - "]" + "]", ); if (lastvolume !== null) { let val = lastvolume.material.uniforms["u_clim"].value; lastvolume.material.uniforms["u_clim"].value.set( parseFloat($(this).val()), - val.y + val.y, ); renderer.updateComplete = false; } @@ -1318,13 +1326,13 @@ function initcanvas() { $(this).prop("min") + "," + $(this).prop("max") + - "]" + "]", ); if (lastvolume !== null) { let val = lastvolume.material.uniforms["u_clim"].value; lastvolume.material.uniforms["u_clim"].value.set( val.x, - parseFloat($(this).val()) + parseFloat($(this).val()), ); renderer.updateComplete = false; } @@ -1341,11 +1349,11 @@ function initcanvas() { $(this).prop("min") + "," + $(this).prop("max") + - "]" + "]", ); if (lastvolume !== null) { lastvolume.material.uniforms["u_renderthreshold"].value = parseFloat( - $(this).val() + $(this).val(), ); renderer.updateComplete = false; } @@ -1437,7 +1445,7 @@ function initcanvas() { $("#" + linkedeid2).val(1); } else { $("#" + linkedeid1).val( - ($("#" + linkedeid1).val() + $("#" + linkedeid2).val()) * 0.5 + ($("#" + linkedeid1).val() + $("#" + linkedeid2).val()) * 0.5, ); } setcrosssectionsizes($("#" + linkedeid1)); @@ -1486,7 +1494,7 @@ function initcanvas() { $(this).prop("min") + "," + $(this).prop("max") + - "]" + "]", ); if (lastvolume !== null && lastvolumedata !== undefined) { let dim = lastvolumedim; @@ -1495,11 +1503,11 @@ function initcanvas() { let texture = new THREE.Data3DTexture( lastvolumedata.selection.data.slice( offset - 1, - offset + dim[0] * dim[1] * dim[2] - 1 + offset + dim[0] * dim[1] * dim[2] - 1, ), dim[0], dim[1], - dim[2] + dim[2], ); texture.format = THREE.RedFormat; texture.type = texture_dtype[lastvolumedata.dtype]; @@ -1520,7 +1528,7 @@ function initcanvas() { $(this).prop("min") + "," + $(this).prop("max") + - "]" + "]", ); if (lastvolume !== null && lastvolumedata !== undefined) { let dim = lastvolumedim; @@ -1530,11 +1538,11 @@ function initcanvas() { let texture = new THREE.Data3DTexture( lastvolumedata.selection.data.slice( offset - 1, - offset + dim[0] * dim[1] * dim[2] - 1 + offset + dim[0] * dim[1] * dim[2] - 1, ), dim[0], dim[1], - dim[2] + dim[2], ); texture.format = THREE.RedFormat; texture.type = texture_dtype[lastvolumedata.dtype]; @@ -2010,13 +2018,13 @@ function setcrosssectionsizes(e) { $(othereid).prop("min") + "," + $(othereid).prop("max") + - "]" + "]", ); } $(e).prop( "title", - $(e).val() + " [" + $(e).prop("min") + "," + $(e).prop("max") + "]" + $(e).val() + " [" + $(e).prop("min") + "," + $(e).prop("max") + "]", ); // 🔐 Ensure uniform exists @@ -2028,7 +2036,7 @@ function setcrosssectionsizes(e) { !lastvolume.material.uniforms["u_maxslice"] ) { console.warn( - "⚠️ Skipping slice update — uniforms missing (not a volume shader)" + "⚠️ Skipping slice update — uniforms missing (not a volume shader)", ); return; } @@ -2037,12 +2045,12 @@ function setcrosssectionsizes(e) { lastvolume.material.uniforms["u_minslice"].value.set( parseFloat($("#cross-x-low").val()), parseFloat($("#cross-y-low").val()), - parseFloat($("#cross-z-low").val()) + parseFloat($("#cross-z-low").val()), ); lastvolume.material.uniforms["u_maxslice"].value.set( parseFloat($("#cross-x-hi").val()), parseFloat($("#cross-y-hi").val()), - parseFloat($("#cross-z-hi").val()) + parseFloat($("#cross-z-hi").val()), ); renderer.updateComplete = false; @@ -2124,13 +2132,13 @@ function previewdataurl(url, idx) { "S" + cached.data.measurementList[i].sourceIndex + "D" + - cached.data.measurementList[i].detectorIndex + cached.data.measurementList[i].detectorIndex, ); } const plotData2D = nj.concatenate( cached.data.time.reshape(cached.data.time.size, 1), - cached.data.dataTimeSeries + cached.data.dataTimeSeries, ).T; previewdata(plotData2D, idx, false, serieslabel); // triggers __onPreviewReady @@ -2197,14 +2205,14 @@ function previewdataurl(url, idx) { if (typedfun[typename] == null) typedfun[typename] = new Function( "d,o,l", - "return new " + typename + "(d,o,l)" + "return new " + typename + "(d,o,l)", ); let typecast = typedfun[typename]; bjd = nj.array( typecast(origdata.buffer, Math.floor(voxeloffset), totallen), - niitype[datatype] + niitype[datatype], ); bjd = { NIFTIHeader: { VoxelSize: voxelsize }, @@ -2309,11 +2317,11 @@ function previewdataurl(url, idx) { previewdata( nj.concatenate( plotdata.data.time.reshape(plotdata.data.time.size, 1), - plotdata.data.dataTimeSeries + plotdata.data.dataTimeSeries, ).T, idx, false, - serieslabel + serieslabel, ); } @@ -2372,18 +2380,18 @@ function previewdataurl(url, idx) { "S" + plotdata.data.measurementList[i].sourceIndex + "D" + - plotdata.data.measurementList[i].detectorIndex + plotdata.data.measurementList[i].detectorIndex, ); } previewdata( nj.concatenate( plotdata.data.time.reshape(plotdata.data.time.size, 1), - plotdata.data.dataTimeSeries + plotdata.data.dataTimeSeries, ).T, idx, false, - serieslabel + serieslabel, ); }