Files
rustytorch/demos/mre-shared/src/geometry.rs
T
2026-03-04 00:08:42 +00:00

178 lines
5.0 KiB
Rust

//! Geometry types for MRE tissue domains and phantoms
use serde::{Deserialize, Serialize};
/// 2D point with vector operations
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)]
pub struct Point2D {
pub x: f32,
pub y: f32,
}
impl Point2D {
/// Create a new point
#[must_use]
pub const fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
/// Distance to another point
#[must_use]
pub fn distance(&self, other: &Self) -> f32 {
let dx = self.x - other.x;
let dy = self.y - other.y;
(dx * dx + dy * dy).sqrt()
}
/// Squared distance (faster, no sqrt)
#[must_use]
pub fn distance_sq(&self, other: &Self) -> f32 {
let dx = self.x - other.x;
let dy = self.y - other.y;
dx * dx + dy * dy
}
}
/// A circular tissue region with specific stiffness
/// Used to define tumors, lesions, or other inclusions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TissueRegion {
/// Center of the circular region
pub center: Point2D,
/// Radius in meters
pub radius: f32,
/// Stiffness in kiloPascals (kPa)
/// Typical values:
/// - Healthy liver: 2-6 kPa
/// - Fibrotic liver: 8-12 kPa
/// - Tumor/HCC: 15-75 kPa
pub stiffness_kpa: f32,
}
impl TissueRegion {
/// Create a tumor-like inclusion
#[must_use]
pub fn tumor(center: Point2D, radius: f32) -> Self {
Self {
center,
radius,
stiffness_kpa: 15.0, // Typical tumor stiffness
}
}
/// Create a fibrotic region
#[must_use]
pub fn fibrotic(center: Point2D, radius: f32) -> Self {
Self {
center,
radius,
stiffness_kpa: 10.0,
}
}
/// Check if a point is inside this region
#[must_use]
pub fn contains(&self, point: &Point2D) -> bool {
self.center.distance_sq(point) <= self.radius * self.radius
}
}
/// Configuration for generating synthetic phantoms
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PhantomConfig {
/// Physical domain size in meters (width, height)
pub domain_size: (f32, f32),
/// Background tissue stiffness in kPa (healthy tissue)
pub background_stiffness: f32,
/// List of tissue inclusions (tumors, lesions, etc.)
pub inclusions: Vec<TissueRegion>,
/// Grid resolution for stiffness map (nx, ny)
pub resolution: (usize, usize),
}
impl Default for PhantomConfig {
fn default() -> Self {
Self {
domain_size: (0.1, 0.1), // 10cm x 10cm
background_stiffness: 3.0, // 3 kPa (healthy liver)
inclusions: vec![TissueRegion::tumor(Point2D::new(0.05, 0.05), 0.015)],
resolution: (64, 64),
}
}
}
impl PhantomConfig {
/// Create a simple phantom with a single centered tumor
#[must_use]
pub fn single_tumor() -> Self {
let (w, h) = (0.1, 0.1);
Self {
domain_size: (w, h),
background_stiffness: 3.0,
inclusions: vec![TissueRegion::tumor(Point2D::new(w / 2.0, h / 2.0), 0.015)],
resolution: (64, 64),
}
}
/// Create a phantom with multiple lesions
#[must_use]
pub fn multiple_lesions() -> Self {
let (w, h) = (0.1, 0.1);
Self {
domain_size: (w, h),
background_stiffness: 3.0,
inclusions: vec![
TissueRegion::tumor(Point2D::new(0.03, 0.05), 0.01),
TissueRegion::fibrotic(Point2D::new(0.07, 0.05), 0.012),
TissueRegion {
center: Point2D::new(0.05, 0.075),
radius: 0.008,
stiffness_kpa: 20.0, // Very stiff lesion
},
],
resolution: (64, 64),
}
}
/// Get stiffness at a point based on phantom configuration
#[must_use]
pub fn stiffness_at(&self, point: &Point2D) -> f32 {
// Check each inclusion (last one wins for overlaps)
for region in self.inclusions.iter().rev() {
if region.contains(point) {
return region.stiffness_kpa;
}
}
self.background_stiffness
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_point_distance() {
let p1 = Point2D::new(0.0, 0.0);
let p2 = Point2D::new(3.0, 4.0);
assert!((p1.distance(&p2) - 5.0).abs() < 1e-6);
}
#[test]
fn test_region_contains() {
let region = TissueRegion::tumor(Point2D::new(0.5, 0.5), 0.1);
assert!(region.contains(&Point2D::new(0.5, 0.5)));
assert!(region.contains(&Point2D::new(0.55, 0.55)));
assert!(!region.contains(&Point2D::new(0.0, 0.0)));
}
#[test]
fn test_phantom_stiffness() {
let config = PhantomConfig::single_tumor();
// Background
assert!((config.stiffness_at(&Point2D::new(0.01, 0.01)) - 3.0).abs() < 0.01);
// Inside tumor
assert!((config.stiffness_at(&Point2D::new(0.05, 0.05)) - 15.0).abs() < 0.01);
}
}