Skip to content

Instantly share code, notes, and snippets.

@agirault
Last active February 1, 2021 21:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save agirault/c32ccdc6755698f720d876374bebfa92 to your computer and use it in GitHub Desktop.
Save agirault/c32ccdc6755698f720d876374bebfa92 to your computer and use it in GitHub Desktop.
Generate the anatomical labels for each side of the screen given a dataset (orientation) and its display mode (data vs world mapping)
import simd
enum Axis {
enum Data {
case I
case J
case K
}
enum World {
case X // LPS: X = R->L (sagittal)
case Y // LPS: Y = A->P (coronal)
case Z // LPS: Z = I->S (axial)
}
case data(Data)
case world(World)
}
enum Side {
case top
case bottom
case left
case right
case close
case far
var coordinates: simd_double3 {
// no transform & LPS (axial view)
switch self {
case .top: return simd_double3(0, -1, 0)
case .bottom: return simd_double3(0, 1, 0)
case .left: return simd_double3(-1, 0, 0)
case .right: return simd_double3(1, 0, 0)
case .close: return simd_double3(0, 0, -1)
case .far: return simd_double3(0, 0, 1)
}
}
}
enum Label: CaseIterable {
case left
case right
case posterior
case anterior
case superior
case inferior
var name: String {
switch self {
case .left: return "Left"
case .right: return "Right"
case .posterior: return "Posterior"
case .anterior: return "Anterior"
case .superior: return "Superior"
case .inferior: return "Inferior"
}
}
var initial: String {
return name.first!.uppercased()
}
var coordinates: simd_int3 {
// in LPS coordinate system
switch self {
case .left: return simd_int3(1, 0, 0)
case .right: return simd_int3(-1, 0, 0)
case .posterior: return simd_int3(0, 1, 0)
case .anterior: return simd_int3(0, -1, 0)
case .superior: return simd_int3(0, 0, 1)
case .inferior: return simd_int3(0, 0, -1)
}
}
init(location side: Side, axis: Axis, orientation R: simd_double3x3) {
switch axis {
case .data(let dataAxis):
// Adjust if not K (scan direction)
var M: simd_double3x3
switch dataAxis {
case .I:
M = simd_double3x3(simd_double3(0, 1.0, 0), simd_double3(0, 0, -1.0), simd_double3(-1.0, 0, 0))
case .J:
M = simd_double3x3(simd_double3(1.0, 0, 0), simd_double3(0, 0, -1.0), simd_double3(0, 1.0, 0))
case .K:
M = matrix_identity_double3x3
}
let MR = M * R
// Apply the orientation matrix to the side coordinates
let outV3 = MR * side.coordinates
// Find the closest axis
func rounded(val: Double) -> Int32 {
let threshold = sqrt(0.5)
if val < -threshold {
return -1
}
if val > threshold {
return 1
}
return 0
}
let outV3Rounded = simd_int3(
rounded(val: outV3.x),
rounded(val: outV3.y),
rounded(val: outV3.z)
)
// Get the label for that vector
self = Label.allCases.first(where: { $0.coordinates == outV3Rounded })!
case .world(let worldAxis):
switch worldAxis {
case .X:
switch side {
case .top: self = .superior
case .bottom: self = .inferior
case .left: self = .anterior
case .right: self = .posterior
case .close: self = .left
case .far: self = .right
}
case .Y:
switch side {
case .top: self = .superior
case .bottom: self = .inferior
case .left: self = .right
case .right: self = .left
case .close: self = .anterior
case .far: self = .posterior
}
case .Z:
switch side {
case .top: self = .anterior
case .bottom: self = .posterior
case .left: self = .right
case .right: self = .left
case .close: self = .inferior
case .far: self = .superior
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment