Initial commit

This commit is contained in:
redclawsystems
2026-03-04 00:08:42 +00:00
commit 4d88dc0584
4449 changed files with 1556714 additions and 0 deletions
@@ -0,0 +1,255 @@
use serde::{Deserialize, Serialize};
/// Spatial orientation codes following neuroimaging conventions
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Orientation {
/// Right to Left
RightToLeft,
/// Left to Right
LeftToRight,
/// Posterior to Anterior
PosteriorToAnterior,
/// Anterior to Posterior
AnteriorToPosterior,
/// Inferior to Superior
InferiorToSuperior,
/// Superior to Inferior
SuperiorToInferior,
}
/// Voxel spacing in millimeters
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct VoxelSpacing {
/// X-axis spacing in millimeters
pub x: f32,
/// Y-axis spacing in millimeters
pub y: f32,
/// Z-axis spacing in millimeters
pub z: f32,
}
impl VoxelSpacing {
/// Create new voxel spacing
pub fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z }
}
/// Create isotropic voxel spacing
pub fn isotropic(spacing: f32) -> Self {
Self::new(spacing, spacing, spacing)
}
/// Check if voxel spacing is isotropic
pub fn is_isotropic(&self) -> bool {
(self.x - self.y).abs() < f32::EPSILON && (self.y - self.z).abs() < f32::EPSILON
}
}
/// Medical image metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MedicalMetadata {
/// Image dimensions (width, height, depth) in voxels
pub dimensions: (usize, usize, usize),
/// Voxel spacing in millimeters (physical size of each voxel)
pub voxel_spacing: VoxelSpacing,
/// Patient identifier (if available in DICOM/NIfTI metadata)
pub patient_id: Option<String>,
/// Study description text (if available)
pub study_description: Option<String>,
/// Acquisition date in ISO format (if available)
pub acquisition_date: Option<String>,
}
impl MedicalMetadata {
/// Create new metadata with required fields
pub fn new(dimensions: (usize, usize, usize), voxel_spacing: VoxelSpacing) -> Self {
Self {
dimensions,
voxel_spacing,
patient_id: None,
study_description: None,
acquisition_date: None,
}
}
/// Set patient ID
pub fn with_patient_id(mut self, patient_id: String) -> Self {
self.patient_id = Some(patient_id);
self
}
/// Set study description
pub fn with_study_description(mut self, description: String) -> Self {
self.study_description = Some(description);
self
}
/// Set acquisition date
pub fn with_acquisition_date(mut self, date: String) -> Self {
self.acquisition_date = Some(date);
self
}
/// Calculate total number of voxels
pub fn voxel_count(&self) -> usize {
self.dimensions.0 * self.dimensions.1 * self.dimensions.2
}
/// Calculate volume in cubic millimeters
pub fn volume_mm3(&self) -> f32 {
let (w, h, d) = self.dimensions;
(w as f32 * self.voxel_spacing.x)
* (h as f32 * self.voxel_spacing.y)
* (d as f32 * self.voxel_spacing.z)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_voxel_spacing_new() {
let spacing = VoxelSpacing::new(1.0, 2.0, 3.0);
assert_eq!(spacing.x, 1.0);
assert_eq!(spacing.y, 2.0);
assert_eq!(spacing.z, 3.0);
}
#[test]
fn test_voxel_spacing_isotropic() {
let spacing = VoxelSpacing::isotropic(1.5);
assert_eq!(spacing.x, 1.5);
assert_eq!(spacing.y, 1.5);
assert_eq!(spacing.z, 1.5);
assert!(spacing.is_isotropic());
}
#[test]
fn test_voxel_spacing_is_isotropic_true() {
let spacing = VoxelSpacing::new(1.0, 1.0, 1.0);
assert!(spacing.is_isotropic());
}
#[test]
fn test_voxel_spacing_is_isotropic_false() {
let spacing = VoxelSpacing::new(1.0, 2.0, 1.0);
assert!(!spacing.is_isotropic());
}
#[test]
fn test_medical_metadata_new() {
let spacing = VoxelSpacing::isotropic(1.0);
let metadata = MedicalMetadata::new((256, 256, 128), spacing);
assert_eq!(metadata.dimensions, (256, 256, 128));
assert!(metadata.patient_id.is_none());
assert!(metadata.study_description.is_none());
assert!(metadata.acquisition_date.is_none());
}
#[test]
fn test_medical_metadata_with_patient_id() {
let spacing = VoxelSpacing::isotropic(1.0);
let metadata =
MedicalMetadata::new((256, 256, 128), spacing).with_patient_id("PT001".to_string());
assert_eq!(metadata.patient_id, Some("PT001".to_string()));
}
#[test]
fn test_medical_metadata_with_study_description() {
let spacing = VoxelSpacing::isotropic(1.0);
let metadata = MedicalMetadata::new((256, 256, 128), spacing)
.with_study_description("Brain MRI".to_string());
assert_eq!(metadata.study_description, Some("Brain MRI".to_string()));
}
#[test]
fn test_medical_metadata_with_acquisition_date() {
let spacing = VoxelSpacing::isotropic(1.0);
let metadata = MedicalMetadata::new((256, 256, 128), spacing)
.with_acquisition_date("2024-01-15".to_string());
assert_eq!(metadata.acquisition_date, Some("2024-01-15".to_string()));
}
#[test]
fn test_medical_metadata_builder_chain() {
let spacing = VoxelSpacing::isotropic(1.0);
let metadata = MedicalMetadata::new((256, 256, 128), spacing)
.with_patient_id("PT001".to_string())
.with_study_description("Brain MRI".to_string())
.with_acquisition_date("2024-01-15".to_string());
assert_eq!(metadata.patient_id, Some("PT001".to_string()));
assert_eq!(metadata.study_description, Some("Brain MRI".to_string()));
assert_eq!(metadata.acquisition_date, Some("2024-01-15".to_string()));
}
#[test]
fn test_voxel_count() {
let spacing = VoxelSpacing::isotropic(1.0);
let metadata = MedicalMetadata::new((256, 256, 128), spacing);
assert_eq!(metadata.voxel_count(), 256 * 256 * 128);
}
#[test]
fn test_voxel_count_small() {
let spacing = VoxelSpacing::isotropic(1.0);
let metadata = MedicalMetadata::new((10, 10, 10), spacing);
assert_eq!(metadata.voxel_count(), 1000);
}
#[test]
fn test_volume_mm3_isotropic() {
let spacing = VoxelSpacing::isotropic(1.0);
let metadata = MedicalMetadata::new((10, 10, 10), spacing);
assert_eq!(metadata.volume_mm3(), 1000.0);
}
#[test]
fn test_volume_mm3_anisotropic() {
let spacing = VoxelSpacing::new(1.0, 1.0, 2.0);
let metadata = MedicalMetadata::new((10, 10, 10), spacing);
assert_eq!(metadata.volume_mm3(), 2000.0);
}
#[test]
fn test_orientation_equality() {
assert_eq!(Orientation::RightToLeft, Orientation::RightToLeft);
assert_ne!(Orientation::RightToLeft, Orientation::LeftToRight);
}
#[test]
fn test_voxel_spacing_clone() {
let spacing1 = VoxelSpacing::isotropic(1.0);
let spacing2 = spacing1.clone();
assert_eq!(spacing1, spacing2);
}
#[test]
fn test_medical_metadata_clone() {
let spacing = VoxelSpacing::isotropic(1.0);
let metadata1 = MedicalMetadata::new((256, 256, 128), spacing);
let metadata2 = metadata1.clone();
assert_eq!(metadata1.dimensions, metadata2.dimensions);
assert_eq!(metadata1.voxel_spacing, metadata2.voxel_spacing);
}
#[test]
fn test_serde_voxel_spacing() {
let spacing = VoxelSpacing::isotropic(1.5);
let json = serde_json::to_string(&spacing).unwrap();
let deserialized: VoxelSpacing = serde_json::from_str(&json).unwrap();
assert_eq!(spacing, deserialized);
}
#[test]
fn test_serde_medical_metadata() {
let spacing = VoxelSpacing::isotropic(1.0);
let metadata =
MedicalMetadata::new((256, 256, 128), spacing).with_patient_id("PT001".to_string());
let json = serde_json::to_string(&metadata).unwrap();
let deserialized: MedicalMetadata = serde_json::from_str(&json).unwrap();
assert_eq!(metadata.dimensions, deserialized.dimensions);
assert_eq!(metadata.patient_id, deserialized.patient_id);
}
}