use std::fmt;
use std::str::FromStr;
use std::collections::HashMap;
use super::error::{Error, Result};
use super::primitive::*;
#[derive(Debug, Clone)]
pub struct VolumeData {
pub id: u32,
pub color: Option<Colour>,
pub composite: Option<Composite>,
pub properties: Vec<Property>,
}
impl VolumeData {
pub fn new(id: u32) -> Self {
Self {
id,
color: None,
composite: None,
properties: Vec::new(),
}
}
pub fn set_color(&mut self, color: Colour) -> &mut Self {
self.color = Some(color);
self
}
pub fn set_composite(&mut self, composite: Composite) -> &mut Self {
self.composite = Some(composite);
self
}
pub fn add_property(&mut self, property: Property) -> &mut Self {
self.properties.push(property);
self
}
}
#[derive(Debug, Clone)]
pub struct Colour {
pub function_id: u32,
pub channel: String,
pub transform: Option<Matrix3D>,
pub min_feature_size: f64,
pub fallback_value: f64,
}
impl Colour {
pub fn new(function_id: u32, channel: impl Into<String>) -> Self {
Self {
function_id,
channel: channel.into(),
transform: None,
min_feature_size: 0.0,
fallback_value: 0.0,
}
}
}
#[derive(Debug, Clone)]
pub struct Composite {
pub base_material_id: u32,
pub material_mappings: Vec<MaterialMapping>,
}
impl Composite {
pub fn new(base_material_id: u32) -> Self {
Self {
base_material_id,
material_mappings: Vec::new(),
}
}
pub fn add_mapping(&mut self, mapping: MaterialMapping) -> &mut Self {
self.material_mappings.push(mapping);
self
}
}
#[derive(Debug, Clone)]
pub struct MaterialMapping {
pub function_id: u32,
pub channel: String,
pub transform: Option<Matrix3D>,
pub min_feature_size: f64,
pub fallback_value: f64,
}
impl MaterialMapping {
pub fn new(function_id: u32, channel: impl Into<String>) -> Self {
Self {
function_id,
transform: None,
channel: channel.into(),
min_feature_size: 0.0,
fallback_value: 0.0,
}
}
}
#[derive(Debug, Clone)]
pub struct Property {
pub function_id: u32,
pub channel: String,
pub transform: Option<Matrix3D>,
pub name: String,
pub required: bool,
pub min_feature_size: f64,
pub fallback_value: f64,
}
impl Property {
pub fn new(function_id: u32, channel: impl Into<String>, name: impl Into<String>) -> Self {
Self {
function_id,
transform: None,
channel: channel.into(),
name: name.into(),
required: false,
min_feature_size: 0.0,
fallback_value: 0.0,
}
}
}
#[derive(Debug, Clone)]
pub struct Image3D {
pub id: u32,
pub name: Option<String>,
pub content: Image3DContent,
}
impl Image3D {
pub fn new(id: u32, content: Image3DContent) -> Self {
Self {
id,
name: None,
content,
}
}
pub fn set_name(&mut self, name: impl Into<String>) -> &mut Self {
self.name = Some(name.into());
self
}
}
#[derive(Debug, Clone)]
pub enum Image3DContent {
ImageStack(ImageStack),
}
#[derive(Debug, Clone)]
pub struct ImageStack {
pub row_count: u32,
pub column_count: u32,
pub sheet_count: u32,
pub image_sheets: Vec<ImageSheet>,
}
impl ImageStack {
pub fn new(row_count: u32, column_count: u32, sheet_count: u32) -> Self {
Self {
row_count,
column_count,
sheet_count,
image_sheets: Vec::new(),
}
}
pub fn add_sheet(&mut self, path: impl Into<String>) -> &mut Self {
self.image_sheets.push(ImageSheet { path: path.into() });
self
}
pub fn validate(&self) -> Result<()> {
if self.row_count > 1024 {
return Err(Error::InvalidStructure(format!(
"rowcount {} exceeds maximum of 1024",
self.row_count
)));
}
if self.column_count > 1024 {
return Err(Error::InvalidStructure(format!(
"columncount {} exceeds maximum of 1024",
self.column_count
)));
}
if self.sheet_count > 1024 {
return Err(Error::InvalidStructure(format!(
"sheetcount {} exceeds maximum of 1024",
self.sheet_count
)));
}
let total_voxels = self.row_count as u64 * self.column_count as u64 * self.sheet_count as u64;
if total_voxels > 1024u64.pow(5) {
return Err(Error::InvalidStructure(format!(
"total voxels {} exceeds maximum of 1024^5",
total_voxels
)));
}
if self.image_sheets.len() != self.sheet_count as usize {
return Err(Error::InvalidStructure(format!(
"expected {} sheets, got {}",
self.sheet_count,
self.image_sheets.len()
)));
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct ImageSheet {
pub path: String,
}
#[derive(Debug, Clone)]
pub struct Function {
pub id: u32,
pub display_name: Option<String>,
pub r#type: FunctionType,
}
impl Function {
pub fn new(id: u32, r#type: FunctionType) -> Self {
Self {
id,
display_name: None,
r#type,
}
}
pub fn set_display_name(&mut self, name: impl Into<String>) -> &mut Self {
self.display_name = Some(name.into());
self
}
}
#[derive(Debug, Clone)]
pub enum FunctionType {
FunctionFromImage3D(FunctionFromImage3D),
PrivateExtensionFunction(PrivateExtensionFunction),
}
#[derive(Debug, Clone)]
pub struct FunctionFromImage3D {
pub image_3d_id: u32,
pub filter: Filter,
pub value_offset: f64,
pub value_scale: f64,
pub tile_style_u: TileStyle,
pub tile_style_v: TileStyle,
pub tile_style_w: TileStyle,
}
impl FunctionFromImage3D {
pub fn new(image3d_id: u32) -> Self {
Self {
image_3d_id: image3d_id,
value_offset: 0.0,
value_scale: 1.0,
filter: Filter::Linear,
tile_style_u: TileStyle::Wrap,
tile_style_v: TileStyle::Wrap,
tile_style_w: TileStyle::Wrap,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Filter {
Linear,
Nearest,
}
impl Default for Filter {
fn default() -> Self {
Filter::Linear
}
}
impl fmt::Display for Filter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Filter::Linear => write!(f, "linear"),
Filter::Nearest => write!(f, "nearest"),
}
}
}
impl FromStr for Filter {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_lowercase().as_str() {
"linear" => Ok(Filter::Linear),
"nearest" => Ok(Filter::Nearest),
_ => Err(Error::InvalidAttribute {
name: "filter".to_string(),
message: format!("unknown filter type: {}", s),
}),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum TileStyle {
#[default]
Wrap,
Mirror,
Clamp,
}
impl fmt::Display for TileStyle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TileStyle::Wrap => write!(f, "wrap"),
TileStyle::Mirror => write!(f, "mirror"),
TileStyle::Clamp => write!(f, "clamp"),
}
}
}
impl FromStr for TileStyle {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_lowercase().as_str() {
"wrap" => Ok(TileStyle::Wrap),
"mirror" => Ok(TileStyle::Mirror),
"clamp" => Ok(TileStyle::Clamp),
_ => Err(Error::InvalidAttribute {
name: "tilestyleu/tilestylev/tilestylew".to_string(),
message: format!("unknown tile style: {}", s),
}),
}
}
}
#[derive(Debug, Clone)]
pub struct PrivateExtensionFunction {
pub attributes: HashMap<String, String>,
pub content: Option<String>,
}
impl PrivateExtensionFunction {
pub fn new() -> Self {
Self {
attributes: Default::default(),
content: None,
}
}
}
#[derive(Debug, Clone)]
pub struct LevelSet {
pub function_id: u32,
pub channel: String,
pub transform: Option<Matrix3D>,
pub min_feature_size: f64,
pub mesh_bbox_only: bool,
pub fallback_value: f64,
pub mesh_id: u32,
pub volume_id: Option<u32>,
}
impl LevelSet {
pub fn new(function_id: u32, channel: impl Into<String>, mesh_id: u32) -> Self {
Self {
function_id,
channel: channel.into(),
transform: None,
min_feature_size: 0.0,
mesh_id,
mesh_bbox_only: false,
fallback_value: 0.0,
volume_id: None,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct VolumetricResources {
pub volume_datas: Vec<VolumeData>,
pub image_3ds: Vec<Image3D>,
pub functions: Vec<Function>,
}
impl VolumetricResources {
pub fn new() -> Self {
Self::default()
}
pub fn add_function(&mut self, function: Function) -> u32 {
let id = function.id;
self.functions.push(function);
id
}
pub fn add_image(&mut self, image3d: Image3D) -> u32 {
let id = image3d.id;
self.image_3ds.push(image3d);
id
}
pub fn add_volume(&mut self, volumedata: VolumeData) -> u32 {
let id = volumedata.id;
self.volume_datas.push(volumedata);
id
}
pub fn get_function(&self, id: u32) -> Option<&Function> {
self.functions.iter().find(|f| f.id == id)
}
pub fn get_image(&self, id: u32) -> Option<&Image3D> {
self.image_3ds.iter().find(|i| i.id == id)
}
pub fn get_volume(&self, id: u32) -> Option<&VolumeData> {
self.volume_datas.iter().find(|v| v.id == id)
}
}