Initial commit
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
//! Preset geometries for Digital Twin demo.
|
||||
|
||||
use rtx_digital_twin::{OrganGeometry, TissueLabel, TissueType};
|
||||
use rtx_digital_twin_shared::{DigitalTwinDemoError, DigitalTwinDemoResult};
|
||||
|
||||
/// Preset geometry generator.
|
||||
pub struct GeometryPresets;
|
||||
|
||||
impl GeometryPresets {
|
||||
/// Create geometry from preset name.
|
||||
pub fn create(
|
||||
name: &str,
|
||||
resolution: [usize; 3],
|
||||
spacing: [f32; 3],
|
||||
) -> DigitalTwinDemoResult<OrganGeometry> {
|
||||
match name.to_lowercase().as_str() {
|
||||
"liver_tumor" => Self::liver_with_tumor(resolution, spacing),
|
||||
"kidney" => Self::kidney(resolution, spacing),
|
||||
"brain" => Self::brain(resolution, spacing),
|
||||
"simple_sphere" => Self::simple_sphere(resolution, spacing),
|
||||
_ => Err(DigitalTwinDemoError::InvalidConfig(format!(
|
||||
"Unknown preset: {name}"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
/// List available preset names.
|
||||
#[must_use]
|
||||
pub fn available() -> Vec<&'static str> {
|
||||
vec!["liver_tumor", "kidney", "brain", "simple_sphere"]
|
||||
}
|
||||
|
||||
/// Create liver with embedded tumor.
|
||||
fn liver_with_tumor(
|
||||
resolution: [usize; 3],
|
||||
spacing: [f32; 3],
|
||||
) -> DigitalTwinDemoResult<OrganGeometry> {
|
||||
let [nx, ny, nz] = resolution;
|
||||
let mut geometry = OrganGeometry::new(resolution, spacing);
|
||||
|
||||
// Create ellipsoidal liver
|
||||
let center = [nx as f32 / 2.0, ny as f32 / 2.0, nz as f32 / 2.0];
|
||||
let radii = [nx as f32 * 0.4, ny as f32 * 0.3, nz as f32 * 0.35];
|
||||
|
||||
for z in 0..nz {
|
||||
for y in 0..ny {
|
||||
for x in 0..nx {
|
||||
let dx = (x as f32 - center[0]) / radii[0];
|
||||
let dy = (y as f32 - center[1]) / radii[1];
|
||||
let dz = (z as f32 - center[2]) / radii[2];
|
||||
|
||||
if dx * dx + dy * dy + dz * dz <= 1.0 {
|
||||
geometry.set_label(x, y, z, TissueLabel::from(TissueType::Liver));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add tumor
|
||||
let tumor_center = [center[0] + radii[0] * 0.2, center[1], center[2]];
|
||||
let tumor_radius = nx as f32 * 0.1;
|
||||
|
||||
geometry.create_sphere(
|
||||
[
|
||||
tumor_center[0] * spacing[0],
|
||||
tumor_center[1] * spacing[1],
|
||||
tumor_center[2] * spacing[2],
|
||||
],
|
||||
tumor_radius * spacing[0],
|
||||
TissueLabel::from(TissueType::Tumor),
|
||||
);
|
||||
|
||||
Ok(geometry)
|
||||
}
|
||||
|
||||
/// Create kidney geometry.
|
||||
fn kidney(resolution: [usize; 3], spacing: [f32; 3]) -> DigitalTwinDemoResult<OrganGeometry> {
|
||||
let [nx, ny, nz] = resolution;
|
||||
let mut geometry = OrganGeometry::new(resolution, spacing);
|
||||
|
||||
// Bean-shaped kidney approximation
|
||||
let center = [nx as f32 / 2.0, ny as f32 / 2.0, nz as f32 / 2.0];
|
||||
|
||||
for z in 0..nz {
|
||||
for y in 0..ny {
|
||||
for x in 0..nx {
|
||||
let dx = (x as f32 - center[0]) / (nx as f32 * 0.25);
|
||||
let dy = (y as f32 - center[1]) / (ny as f32 * 0.35);
|
||||
let dz = (z as f32 - center[2]) / (nz as f32 * 0.2);
|
||||
|
||||
// Outer ellipsoid
|
||||
let dist = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
// Create indentation on one side
|
||||
let indent = if dx > 0.0 {
|
||||
0.3 * (-dy * dy * 4.0).exp()
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
if dist <= 1.0 + indent {
|
||||
geometry.set_label(x, y, z, TissueLabel::from(TissueType::Kidney));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(geometry)
|
||||
}
|
||||
|
||||
/// Create brain geometry.
|
||||
fn brain(resolution: [usize; 3], spacing: [f32; 3]) -> DigitalTwinDemoResult<OrganGeometry> {
|
||||
let [nx, ny, nz] = resolution;
|
||||
let mut geometry = OrganGeometry::new(resolution, spacing);
|
||||
|
||||
// Simplified brain as two hemispheres
|
||||
let center = [nx as f32 / 2.0, ny as f32 / 2.0, nz as f32 / 2.0];
|
||||
|
||||
for z in 0..nz {
|
||||
for y in 0..ny {
|
||||
for x in 0..nx {
|
||||
let dx = (x as f32 - center[0]) / (nx as f32 * 0.35);
|
||||
let dy = (y as f32 - center[1]) / (ny as f32 * 0.4);
|
||||
let dz = (z as f32 - center[2]) / (nz as f32 * 0.3);
|
||||
|
||||
// Main brain shape
|
||||
let dist = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
// Create central fissure
|
||||
let x_f = x as f32;
|
||||
let fissure = if x_f > center[0] - 2.0 && x_f < center[0] + 2.0 {
|
||||
0.1
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
if dist <= 1.0 - fissure {
|
||||
geometry.set_label(x, y, z, TissueLabel::from(TissueType::BrainGrayMatter));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(geometry)
|
||||
}
|
||||
|
||||
/// Create simple sphere for testing.
|
||||
fn simple_sphere(
|
||||
resolution: [usize; 3],
|
||||
spacing: [f32; 3],
|
||||
) -> DigitalTwinDemoResult<OrganGeometry> {
|
||||
let [nx, ny, nz] = resolution;
|
||||
let mut geometry = OrganGeometry::new(resolution, spacing);
|
||||
|
||||
let center = [
|
||||
nx as f32 / 2.0 * spacing[0],
|
||||
ny as f32 / 2.0 * spacing[1],
|
||||
nz as f32 / 2.0 * spacing[2],
|
||||
];
|
||||
let radius = nx.min(ny).min(nz) as f32 / 3.0 * spacing[0];
|
||||
|
||||
geometry.create_sphere(center, radius, TissueLabel::from(TissueType::Muscle));
|
||||
|
||||
Ok(geometry)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_available_presets() {
|
||||
let presets = GeometryPresets::available();
|
||||
assert!(presets.contains(&"liver_tumor"));
|
||||
assert!(presets.contains(&"kidney"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_liver_tumor() {
|
||||
let geometry =
|
||||
GeometryPresets::create("liver_tumor", [32, 32, 32], [1.0, 1.0, 1.0]).unwrap();
|
||||
|
||||
assert_eq!(geometry.shape(), [32, 32, 32]);
|
||||
|
||||
// Should have liver and tumor tissue
|
||||
let histogram = geometry.tissue_histogram();
|
||||
assert!(histogram.get(&TissueType::Liver).unwrap_or(&0) > &0);
|
||||
assert!(histogram.get(&TissueType::Tumor).unwrap_or(&0) > &0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_kidney() {
|
||||
let geometry = GeometryPresets::create("kidney", [32, 32, 32], [1.0, 1.0, 1.0]).unwrap();
|
||||
|
||||
let histogram = geometry.tissue_histogram();
|
||||
assert!(histogram.get(&TissueType::Kidney).unwrap_or(&0) > &0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_brain() {
|
||||
let geometry = GeometryPresets::create("brain", [32, 32, 32], [1.0, 1.0, 1.0]).unwrap();
|
||||
|
||||
let histogram = geometry.tissue_histogram();
|
||||
assert!(histogram.get(&TissueType::BrainGrayMatter).unwrap_or(&0) > &0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unknown_preset() {
|
||||
let result = GeometryPresets::create("unknown", [32, 32, 32], [1.0, 1.0, 1.0]);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user