#[cfg(test)]
mod test;
use super::unstructured::ReadVtkUnstructured;
use crate::{
geometry::mesh::Mesh,
io::{
invalid,
read::{attribute, data_arrays, encoding, find_data_array, integers, region, tag},
},
};
use std::{
fs::read_to_string,
io::Result,
path::{Path, PathBuf},
};
pub(crate) trait ReadVtkMultiBlock<P>
where
P: AsRef<Path>,
Self: Sized,
{
fn read_vtk_multi_block(input: P) -> Result<Self>;
}
impl<const D: usize, P> ReadVtkMultiBlock<P> for Mesh<D>
where
P: AsRef<Path>,
{
fn read_vtk_multi_block(input: P) -> Result<Self> {
let path = input.as_ref();
let dir = path
.parent()
.filter(|parent| !parent.as_os_str().is_empty());
let join = |name: &str| -> PathBuf {
match dir {
Some(dir) => dir.join(name),
None => PathBuf::from(name),
}
};
let text = read_to_string(path)?;
let header = tag(&text, "<VTKFile")?;
if attribute(header, "type") != Some("vtkMultiBlockDataSet") {
return Err(invalid("file is not a vtkMultiBlockDataSet".into()));
}
let entries = data_sets(&text)?;
let volume_file = entries
.iter()
.find_map(|(name, file)| (name == "volume").then_some(file))
.ok_or_else(|| invalid("vtkMultiBlockDataSet has no \"volume\" block".into()))?;
let mut mesh = Mesh::<D>::read_vtk_unstructured(join(volume_file))?;
let mut side_sets = Vec::new();
let mut side_set_labels = Vec::new();
for (name, file) in &entries {
if let Some(label) = name.strip_prefix("side_set_") {
side_sets.push(read_side_set(&join(file))?);
side_set_labels.push(label);
}
}
if !side_sets.is_empty() {
let numbers: Option<Vec<usize>> = side_set_labels
.iter()
.map(|label| label.parse().ok())
.collect();
mesh.set_side_sets(match numbers {
Some(numbers) => (side_sets, numbers).into(),
None => side_sets.into(),
});
}
Ok(mesh)
}
}
fn data_sets(text: &str) -> Result<Vec<(String, String)>> {
let mut rest = text;
let mut entries = Vec::new();
while let Some(start) = rest.find("<DataSet") {
let end = rest[start..]
.find('>')
.ok_or_else(|| invalid("unterminated DataSet in vtkMultiBlockDataSet".into()))?
+ start;
let tag = &rest[start..end];
let name = attribute(tag, "name")
.ok_or_else(|| invalid("DataSet without a name".into()))?
.to_string();
let file = attribute(tag, "file")
.ok_or_else(|| invalid("DataSet without a file".into()))?
.to_string();
entries.push((name, file));
rest = &rest[end..];
}
Ok(entries)
}
fn read_side_set(path: &Path) -> Result<Vec<(usize, usize)>> {
let text = read_to_string(path)?;
let header = tag(&text, "<VTKFile")?;
if attribute(header, "type") != Some("PolyData") {
return Err(invalid("side set file is not a PolyData".into()));
}
let encoding = encoding(header)?;
let cell_data = region(&text, "CellData")?;
let cell_arrays = data_arrays(cell_data)?;
let elements = integers(
&find_data_array(&cell_arrays, Some("OriginalElementIds"))?,
&encoding,
)?;
let ordinals = integers(
&find_data_array(&cell_arrays, Some("OriginalFaceIds"))?,
&encoding,
)?;
Ok(elements
.into_iter()
.zip(ordinals)
.map(|(element, ordinal)| (element as usize, ordinal as usize))
.collect())
}