Last active
February 5, 2021 15:32
-
-
Save agirault/1779e12ad36ae8bca7da526fbbd772a8 to your computer and use it in GitHub Desktop.
This, but in JS: https://gist.github.com/agirault/c32ccdc6755698f720d876374bebfa92
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import vtkImageMapper from 'vtk.js/Sources/Rendering/Core/ImageMapper'; | |
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math'; | |
const { SlicingMode } = vtkImageMapper; | |
const ScreenSide = { | |
top: Symbol('top'), | |
bottom: Symbol('bottom'), | |
left: Symbol('left'), | |
right: Symbol('right'), | |
close: Symbol('close'), | |
far: Symbol('far'), | |
}; | |
function LPSCoordinatesOfScreenSide(screenSide) { | |
switch (screenSide) { | |
case ScreenSide.top: return [0, -1, 0]; | |
case ScreenSide.bottom: return [0, 1, 0]; | |
case ScreenSide.left: return [-1, 0, 0]; | |
case ScreenSide.right: return [1, 0, 0]; | |
case ScreenSide.close: return [0, 0, -1]; | |
case ScreenSide.far: return [0, 0, 1]; | |
default: return []; | |
} | |
} | |
const Label = { | |
left: Symbol('Left'), | |
right: Symbol('Right'), | |
posterior: Symbol('Posterior'), | |
anterior: Symbol('Anterior'), | |
superior: Symbol('Superior'), | |
inferior: Symbol('Inferior'), | |
}; | |
function LPSCoordinatesOfLabel(label) { | |
switch (label) { | |
case Label.left: return [1, 0, 0]; | |
case Label.right: return [-1, 0, 0]; | |
case Label.posterior: return [0, 1, 0]; | |
case Label.anterior: return [0, -1, 0]; | |
case Label.superior: return [0, 0, 1]; | |
case Label.inferior: return [0, 0, -1]; | |
default: return []; | |
} | |
} | |
function labelFor(screenSide, mapper) { | |
// Method for image space slicing (I, J, K) | |
function labelForImageSpaceSlicing(mat3x3) { | |
// Create a 3x3 matrix transformation from the image space | |
// (IJK, JKI or KIJ) to world space (LPS) | |
const dir9 = mapper.getInputData().getDirection(); | |
const dir3x3 = [ | |
[dir9[0], dir9[3], dir9[6]], | |
[dir9[1], dir9[4], dir9[7]], | |
[dir9[2], dir9[5], dir9[8]], | |
]; | |
vtkMath.multiply3x3_mat3(dir3x3, mat3x3, mat3x3); | |
// Apply the transformation to the coordinate of the side | |
let outVec = [0, 0, 0]; | |
const inVec = LPSCoordinatesOfScreenSide(screenSide); | |
vtkMath.multiply3x3_vect3(mat3x3, inVec, outVec); | |
// Restrict to the unit vectors | |
outVec = outVec.map((val) => { | |
const threshold = Math.sqrt(0.5); | |
if (val < -threshold) { | |
return -1; | |
} | |
if (val > threshold) { | |
return 1; | |
} | |
return 0; | |
}); | |
// Find matching label | |
const labels = Object.values(Label); | |
return labels.find((label) => vtkMath.areMatricesEqual(outVec, LPSCoordinatesOfLabel(label))); | |
} | |
switch (mapper.getSlicingMode()) { | |
case SlicingMode.I: { | |
// JKI -> IJK | |
const mat3x3 = [[0, 1, 0], [0, 0, -1], [-1, 0, 0]]; | |
return labelForImageSpaceSlicing(mat3x3); | |
} | |
case SlicingMode.J: { | |
// KIJ -> IJK | |
const mat3x3 = [[1, 0, 0], [0, 0, -1], [0, 1, 0]]; | |
return labelForImageSpaceSlicing(mat3x3); | |
} | |
case SlicingMode.K: { | |
// IJK -> IJK | |
const mat3x3 = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]; | |
return labelForImageSpaceSlicing(mat3x3); | |
} | |
case SlicingMode.X: | |
switch (screenSide) { | |
case ScreenSide.top: return Label.superior; | |
case ScreenSide.bottom: return Label.inferior; | |
case ScreenSide.left: return Label.anterior; | |
case ScreenSide.right: return Label.posterior; | |
case ScreenSide.close: return Label.left; | |
case ScreenSide.far: return Label.right; | |
default: return null; | |
} | |
case SlicingMode.Y: | |
switch (side) { | |
case ScreenSide.top: return Label.superior; | |
case ScreenSide.bottom: return Label.inferior; | |
case ScreenSide.left: return Label.right; | |
case ScreenSide.right: return Label.left; | |
case ScreenSide.close: return Label.anterior; | |
case ScreenSide.far: return Label.posterior; | |
default: return null; | |
} | |
case SlicingMode.Z: | |
switch (screenSide) { | |
case ScreenSide.top: return Label.anterior; | |
case ScreenSide.bottom: return Label.posterior; | |
case ScreenSide.left: return Label.right; | |
case ScreenSide.right: return Label.left; | |
case ScreenSide.close: return Label.inferior; | |
case ScreenSide.far: return Label.superior; | |
default: return null; | |
} | |
default: return null; | |
} | |
} | |
export default { | |
ScreenSide, | |
labelFor, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Not sure to fully understand, but it seems that you know what you are doing :-)
As a side note, I did not realize that
Side
was for screen positions, maybeScreenSide
would be more meaningful...