Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/configuration_file.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Use a 2D array of view type strings to specify both the grid layout and which vi
}
```

Available view type strings: `"axial"`, `"coronal"`, `"sagittal"`, `"volume"`, `"oblique"`
Available view type strings: `"axial"`, `"coronal"`, `"sagittal"`, `"volume"`, `"oblique"`, `"enface"`

### 2. Simple Grid (gridSize)

Expand Down Expand Up @@ -111,6 +111,7 @@ View object properties:
- 2D views: `type: "2D"`, `orientation: "Axial" | "Coronal" | "Sagittal"`, `name` (optional)
- 3D views: `type: "3D"`, `viewDirection` (optional), `viewUp` (optional), `name` (optional)
- Oblique views: `type: "Oblique"`, `name` (optional)
- En face views: `type: "EnFace"`, `name` (optional)

### Multiple Layouts Example

Expand Down Expand Up @@ -147,7 +148,7 @@ Use `disabledViewTypes` to prevent certain view types from being available in th
}
```

This removes the specified view types from the dropdown menu and replaces them in the default layout with allowed types. Valid values: `"2D"`, `"3D"`, `"Oblique"`
This removes the specified view types from the dropdown menu and replaces them in the default layout with allowed types. Valid values: `"2D"`, `"3D"`, `"Oblique"`, `"EnFace"`.

## Segments

Expand Down
5 changes: 5 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,11 @@ export default tseslint.config(
},
},
...featureBoundaries([
{
dir: 'enface',
publicSurface: false,
pure: ['src/enface/sliceGeometry.ts'],
},
{
dir: 'segmentation',
pure: [
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@
"backend-contract"
],
"scripts": {
"prepare": "node scripts/apply-patches.mjs",
"dev": "cross-env VITE_SHOW_SAMPLE_DATA=true vite",
"preview": "vite preview",
"serve": "npm run dev",
"prebuild": "patch-package",
"prebuild": "patch-package --error-on-fail",
"contract:generate": "tsx backend-contract/scripts/generate-json-schema.ts && tsx backend-contract/scripts/generate-openapi.ts",
"build": "vue-tsc --noEmit && vite build",
"build:analyze": "cross-env ANALYZE_BUNDLE=1 npm run build",
"test": "vitest",
"test:e2e:chrome": "cross-env VITE_SHOW_SAMPLE_DATA=true npm run build && wdio run ./wdio.chrome.conf.ts",
"test:e2e:chrome:skip-build": "wdio run ./wdio.chrome.conf.ts",
"test:e2e:dev": "cross-env VITE_SHOW_SAMPLE_DATA=true concurrently -P \"npm run dev\" \"wdio run ./wdio.dev.conf.ts --watch {@}\"",
"test:e2e:chrome": "cross-env VITE_SHOW_SAMPLE_DATA=true npm run build && cross-env HEADED=0 wdio run ./wdio.chrome.conf.ts",
"test:e2e:chrome:skip-build": "cross-env HEADED=0 wdio run ./wdio.chrome.conf.ts",
"test:e2e:dev": "cross-env HEADED=0 VITE_SHOW_SAMPLE_DATA=true concurrently -P \"npm run dev\" \"wdio run ./wdio.dev.conf.ts --watch {@}\"",
"lint": "vue-tsc --noEmit && eslint \"src/**/*.{js,ts,vue}\" \"tests/**/*.{js,ts}\" \"scripts/**/*.mjs\"",
"build:all": "npm run build:dicom && npm run build:resample && npm run build",
"build:dicom": "itk-wasm -s src/io/itk-dicom/ build ",
Expand Down Expand Up @@ -78,6 +79,7 @@
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-vue": "^10.6.2",
"fast-deep-equal": "^3.1.3",
"fflate": "0.7.5",
"file-saver": "^2.0.5",
"gl-matrix": "3.4.3",
"globals": "^16.2.0",
Expand Down
116 changes: 114 additions & 2 deletions patches/@kitware+vtk.js+38.0.4.patch
Original file line number Diff line number Diff line change
@@ -1,8 +1,108 @@
diff --git a/node_modules/@kitware/vtk.js/Rendering/OpenGL/ImageResliceMapper.js b/node_modules/@kitware/vtk.js/Rendering/OpenGL/ImageResliceMapper.js
--- a/node_modules/@kitware/vtk.js/Rendering/OpenGL/ImageResliceMapper.js
+++ b/node_modules/@kitware/vtk.js/Rendering/OpenGL/ImageResliceMapper.js
@@ -574,7 +574,7 @@
let needRebuild = false;
if (!model.currentRenderPass && model.lastRenderPassShaderReplacement || model.currentRenderPass && model.currentRenderPass.getShaderReplacement() !== model.lastRenderPassShaderReplacement) needRebuild = true;
const numValidInputs = model.currentValidInputs?.length ?? 0;
- if (needRebuild || model.lastHaveSeenDepthRequest !== model.haveSeenDepthRequest || model.lastNumberOfComponents !== model.numberOfComponents || model.lastMultiTexturePerVolumeEnabled !== model.multiTexturePerVolumeEnabled || cellBO.getProgram()?.getHandle() === 0 || model.lastIndependentComponents !== iComp || model.lastUseLabelOutline !== useLabelOutline || model.lastNumValidInputs !== numValidInputs || model.lastSlabThickness !== slabTh || model.lastSlabType !== slabType || model.lastSlabTrapezoidIntegration !== slabTrap) {
+ if (needRebuild || model.lastHaveSeenDepthRequest !== model.haveSeenDepthRequest || model.lastNumberOfComponents !== model.numberOfComponents || model.lastMultiTexturePerVolumeEnabled !== model.multiTexturePerVolumeEnabled || cellBO.getProgram()?.getHandle() === 0 || model.lastIndependentComponents !== iComp || model.lastUseLabelOutline !== useLabelOutline || model.lastNumValidInputs !== numValidInputs || (model.lastSlabThickness > 0) !== (slabTh > 0) || model.lastSlabType !== slabType || model.lastSlabTrapezoidIntegration !== slabTrap) {
model.lastHaveSeenDepthRequest = model.haveSeenDepthRequest;
model.lastNumberOfComponents = model.numberOfComponents;
model.lastMultiTexturePerVolumeEnabled = model.multiTexturePerVolumeEnabled;
@@ -724,33 +724,25 @@
}
function getSlabSampleLoopLines() {
return [
- "// Get the first and last samples",
+ "// The center sample is already included in tvalue.",
"int numSlices = 1;",
- "float scaling = min(min(spacing.x, spacing.y), spacing.z) * 0.5;",
+ "float scaling = min(min(abs(spacing.x), abs(spacing.y)), abs(spacing.z)) * 0.5;",
+ "float halfThickness = slabThickness * 0.5;",
+ "// Independently rounded thickness and spacing can differ at an endpoint.",
+ "float boundaryTolerance = halfThickness * 4.76837158203125e-7;",
"vec3 slabNormal = normalize(normalWCVSOutput);",
- "vec3 normalxspacing = scaling * slabNormal;",
- "float distTraveled = length(normalxspacing);",
- "int trapezoid = 0;",
- "// Each march direction leaves the volume for good once it exits the",
- "// unit cube (the sample positions are monotonic along a line and the",
- "// cube is convex), so the loop can stop as soon as both directions",
- "// have exited. Slab thicknesses larger than the volume then only cost",
- "// the in-volume portion of the march.",
+ "int stepIndex = 1;",
+ "float distTraveled = min(scaling, halfThickness);",
+ "// A ray cannot reenter the convex volume after leaving it.",
"bool negExited = false;",
"bool posExited = false;",
- "while (distTraveled < slabThickness * 0.5)",
+ "while (distTraveled > 0.0 && distTraveled <= halfThickness)",
"{",
- " distTraveled += length(normalxspacing);",
- " float fnumSlices = float(numSlices);",
- " bool atSlabBoundary = false;",
- " if (distTraveled > slabThickness * 0.5)",
- " {",
- " // Before stepping outside the slab, sample at the boundaries",
- " normalxspacing = slabNormal * slabThickness * 0.5 / fnumSlices;",
- " trapezoid = slabTrapezoid;",
- " atSlabBoundary = true;",
- " }",
- " vec3 worldPosNeg = vertexWCVSOutput.xyz - fnumSlices * normalxspacing * vboScaling;",
+ " bool atSlabBoundary = halfThickness - distTraveled <= boundaryTolerance;",
+ " float sampleDistance = atSlabBoundary ? halfThickness : distTraveled;",
+ " int trapezoid = atSlabBoundary ? slabTrapezoid : 0;",
+ " vec3 slabOffset = sampleDistance * slabNormal * vboScaling;",
+ " vec3 worldPosNeg = vertexWCVSOutput.xyz - slabOffset;",
" vec3 fragTCoordNeg = (WCTCMatrix0 * vec4(worldPosNeg, 1.0)).xyz;",
" if (!any(greaterThan(fragTCoordNeg, vec3(1.0))) && !any(lessThan(fragTCoordNeg, vec3(0.0))))",
" {",
@@ -758,11 +750,11 @@
" tvalue = compositeValue(tvalue, newVal, trapezoid);",
" numSlices += 1;",
" }",
- " else if (!atSlabBoundary)",
+ " else",
" {",
" negExited = true;",
" }",
- " vec3 worldPosPos = vertexWCVSOutput.xyz + fnumSlices * normalxspacing * vboScaling;",
+ " vec3 worldPosPos = vertexWCVSOutput.xyz + slabOffset;",
" vec3 fragTCoordPos = (WCTCMatrix0 * vec4(worldPosPos, 1.0)).xyz;",
" if (!any(greaterThan(fragTCoordPos, vec3(1.0))) && !any(lessThan(fragTCoordPos, vec3(0.0))))",
" {",
@@ -770,11 +762,15 @@
" tvalue = compositeValue(tvalue, newVal, trapezoid);",
" numSlices += 1;",
" }",
- " else if (!atSlabBoundary)",
+ " else",
" {",
" posExited = true;",
" }",
- " if (negExited && posExited) { break; }",
+ " if (atSlabBoundary || (negExited && posExited)) { break; }",
+ " stepIndex += 1;",
+ " float nextDistance = min(float(stepIndex) * scaling, halfThickness);",
+ " if (nextDistance <= distTraveled) { break; }",
+ " distTraveled = nextDistance;",
"}",
"// Finally, if slab type is *mean*, divide the sum by the numSlices",
"if (slabType == 2)",
@@ -1155,7 +1151,7 @@
let FSSource = shaders.Fragment;
const slabThickness = model.renderable.getSlabThickness();
let posVCVSDec = ["attribute vec4 vertexWC;", "varying vec4 vertexWCVSOutput;"];
- posVCVSDec = posVCVSDec.concat([`//${publicAPI.getMTime()}${model.resliceGeomUpdateString}`]);
+ posVCVSDec = posVCVSDec.concat([`//${publicAPI.getMTime()}`]);
if (slabThickness > 0) posVCVSDec = posVCVSDec.concat(["attribute vec3 normalWC;", "varying vec3 normalWCVSOutput;"]);
VSSource = ShaderProgram_default.substitute(VSSource, "//VTK::PositionVC::Dec", posVCVSDec).result;
let posVCVSImpl = ["gl_Position = MCPCMatrix * vertexWC;", "vertexWCVSOutput = vertexWC;"];
diff --git a/node_modules/@kitware/vtk.js/Rendering/OpenGL/Renderer.js b/node_modules/@kitware/vtk.js/Rendering/OpenGL/Renderer.js
index ba92841..a59d269 100644
--- a/node_modules/@kitware/vtk.js/Rendering/OpenGL/Renderer.js
+++ b/node_modules/@kitware/vtk.js/Rendering/OpenGL/Renderer.js
@@ -87,7 +87,7 @@ function vtkOpenGLRenderer(publicAPI, model) {
@@ -87,7 +87,7 @@
publicAPI.clear = () => {
let clearMask = 0;
const gl = model.context;
Expand All @@ -11,3 +111,15 @@ index ba92841..a59d269 100644
const background = model.renderable.getBackgroundByReference();
gl.clearColor(background[0], background[1], background[2], background[3]);
clearMask |= gl.COLOR_BUFFER_BIT;
diff --git a/node_modules/@kitware/vtk.js/Rendering/OpenGL/VolumeMapper.js b/node_modules/@kitware/vtk.js/Rendering/OpenGL/VolumeMapper.js
--- a/node_modules/@kitware/vtk.js/Rendering/OpenGL/VolumeMapper.js
+++ b/node_modules/@kitware/vtk.js/Rendering/OpenGL/VolumeMapper.js
@@ -868,7 +868,7 @@
const reBuildTex = !tex?.oglObject?.getHandle() || tex?.hash !== scalarsHash;
const updatedExtents = volumeProperty.getUpdatedExtents();
const hasUpdatedExtents = !!updatedExtents.length;
- if (reBuildTex && !hasUpdatedExtents) {
+ if (reBuildTex && (!hasUpdatedExtents || !tex?.oglObject?.getHandle())) {
const newScalarTexture = Texture_default.newInstance();
newScalarTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
const dims = imageData.getDimensions();
12 changes: 12 additions & 0 deletions scripts/apply-patches.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';

if (process.env.NODE_ENV !== 'production') {
const result = spawnSync(
process.execPath,
[fileURLToPath(import.meta.resolve('patch-package')), '--error-on-fail'],
{ stdio: 'inherit' }
);
if (result.error) throw result.error;
process.exitCode = result.status ?? 1;
}
Loading
Loading