use crate::io;
use anyhow::Context;
use anyhow::anyhow;
use clap::value_parser;
use log::info;
use splashsurf_lib::mesh::MeshWithData;
use splashsurf_lib::nalgebra::Vector3;
use splashsurf_lib::{Aabb3d, nalgebra, profile};
use std::path::PathBuf;
#[derive(Clone, Debug, clap::Parser)]
pub struct ConvertSubcommandArgs {
#[arg(
long = "particles",
value_parser = value_parser!(PathBuf),
conflicts_with = "input_mesh"
)]
input_particles: Option<PathBuf>,
#[arg(
long = "mesh",
value_parser = value_parser!(PathBuf),
conflicts_with = "input_particles"
)]
input_mesh: Option<PathBuf>,
#[arg(short = 'o', value_parser = value_parser!(PathBuf))]
output_file: PathBuf,
#[arg(long)]
overwrite: bool,
#[arg(
long,
number_of_values = 1,
number_of_values = 3,
value_names = ["X_MIN", "Y_MIN", "Z_MIN"],
allow_negative_numbers = true,
requires = "domain_max"
)]
domain_min: Option<Vec<f64>>,
#[arg(
long,
number_of_values = 3,
value_names = ["X_MIN", "Y_MIN", "Z_MIN"],
allow_negative_numbers = true,
requires = "domain_min"
)]
domain_max: Option<Vec<f64>>,
}
pub fn convert_subcommand(cmd_args: &ConvertSubcommandArgs) -> Result<(), anyhow::Error> {
overwrite_check(cmd_args)?;
match (&cmd_args.input_particles, &cmd_args.input_mesh) {
(Some(_), _) => convert_particles(cmd_args)?,
(_, Some(_)) => convert_mesh(cmd_args)?,
(_, _) => {
return Err(anyhow!(
"Aborting: No input file specified, either a particle or mesh input file has to be specified."
));
}
}
Ok(())
}
fn convert_particles(cmd_args: &ConvertSubcommandArgs) -> Result<(), anyhow::Error> {
profile!("particle file conversion cli");
let io_params = io::FormatParameters::default();
let input_file = cmd_args.input_particles.as_ref().unwrap();
let output_file = &cmd_args.output_file;
let particle_positions: Vec<Vector3<f32>> =
io::read_particle_positions(input_file.as_path(), &io_params.input).with_context(|| {
format!(
"Failed to load particle positions from file \"{}\"",
input_file.as_path().display()
)
})?;
let particle_positions = if let (Some(min), Some(max)) =
(cmd_args.domain_min.clone(), cmd_args.domain_max.clone())
{
let min = nalgebra::convert(Vector3::from_iterator(min));
let max = nalgebra::convert(Vector3::from_iterator(max));
let aabb = Aabb3d::new(min, max);
info!("Filtering out particles outside of {:?}", aabb);
particle_positions
.into_iter()
.filter(|p| aabb.contains_point(p))
.collect()
} else {
particle_positions
};
io::write_particle_positions(
particle_positions.as_slice(),
output_file.as_path(),
&io_params.output,
)?;
Ok(())
}
fn convert_mesh(cmd_args: &ConvertSubcommandArgs) -> Result<(), anyhow::Error> {
profile!("mesh file conversion cli");
let io_params = io::FormatParameters::default();
let input_file = cmd_args.input_mesh.as_ref().unwrap();
let output_file = &cmd_args.output_file;
let mesh: MeshWithData<f32, _> = io::read_surface_mesh(input_file.as_path(), &io_params.input)
.with_context(|| {
format!(
"Failed to load surface mesh from file \"{}\"",
input_file.as_path().display()
)
})?;
io::write_mesh(&mesh, output_file.as_path(), &io_params.output)?;
Ok(())
}
fn overwrite_check(cmd_args: &ConvertSubcommandArgs) -> Result<(), anyhow::Error> {
if !cmd_args.overwrite && cmd_args.output_file.exists() {
return Err(anyhow!(
"Aborting: Output file \"{}\" already exists. Use overwrite flag to ignore this.",
cmd_args.output_file.display()
));
}
Ok(())
}