use super::{
ErrorWrapper,
io::{read_mesh, write_mesh},
metrics::write_metrics,
remesh::{MeshRemeshSubcommand, apply_remesh_subcommand},
};
use clap::{Subcommand, ValueEnum};
use conspire::geometry::mesh::{Mesh, Smoothing, Weighting};
use std::time::Instant;
pub const TAUBIN_DEFAULT_ITERS: usize = 20;
pub const TAUBIN_DEFAULT_BAND: f64 = 0.1;
pub const TAUBIN_DEFAULT_SCALE: f64 = 0.6307;
#[derive(Clone, Copy, Debug, ValueEnum)]
#[value(rename_all = "PascalCase")]
pub enum SmoothingMethod {
#[value(alias = "Laplacian")]
Laplace,
Taubin,
}
#[derive(Subcommand, Debug)]
pub enum MeshSmoothCommands {
Smooth {
#[command(subcommand)]
remeshing: Option<MeshRemeshSubcommand>,
#[arg(default_value_t = TAUBIN_DEFAULT_ITERS, long, short = 'n', value_name = "NUM")]
iterations: usize,
#[arg(
default_value_t = SmoothingMethod::Taubin,
ignore_case = true,
long,
short,
value_enum,
value_name = "NAME"
)]
method: SmoothingMethod,
#[arg(default_value_t = TAUBIN_DEFAULT_BAND, long, short = 'k', value_name = "FREQ")]
pass_band: f64,
#[arg(default_value_t = TAUBIN_DEFAULT_SCALE, long, short, value_name = "SCALE")]
scale: f64,
#[arg(action, long, short = 'b')]
hierarchical: bool,
},
}
#[derive(clap::Args)]
pub struct SmoothArgs {
#[command(subcommand)]
pub remeshing: Option<MeshRemeshSubcommand>,
#[arg(long, short, value_name = "FILE")]
pub input: String,
#[arg(long, short, value_name = "FILE")]
pub output: String,
#[arg(default_value_t = TAUBIN_DEFAULT_ITERS, long, short = 'n', value_name = "NUM")]
pub iterations: usize,
#[arg(
default_value_t = SmoothingMethod::Taubin,
ignore_case = true,
long,
short,
value_enum,
value_name = "NAME"
)]
pub method: SmoothingMethod,
#[arg(default_value_t = TAUBIN_DEFAULT_BAND, long, short = 'k', value_name = "FREQ")]
pub pass_band: f64,
#[arg(default_value_t = TAUBIN_DEFAULT_SCALE, long, short, value_name = "SCALE")]
pub scale: f64,
#[arg(action, long, short = 'b')]
pub hierarchical: bool,
#[arg(long, value_name = "FILE")]
pub metrics: Option<String>,
}
pub fn smooth(args: SmoothArgs, quiet: bool) -> Result<(), ErrorWrapper> {
let mut mesh = read_mesh(&args.input, quiet)?;
apply_smoothing_method(
&mut mesh,
args.iterations,
args.method,
args.pass_band,
args.scale,
args.hierarchical,
quiet,
)?;
if let Some(subcommand) = args.remeshing {
mesh = apply_remesh_subcommand(mesh, subcommand, quiet)?;
}
if let Some(file) = args.metrics {
write_metrics(&mesh, &file, quiet)?;
}
write_mesh(&args.output, mesh, quiet)
}
pub fn apply_smoothing_method(
mesh: &mut Mesh<3>,
iterations: usize,
method: SmoothingMethod,
pass_band: f64,
scale: f64,
hierarchical: bool,
quiet: bool,
) -> Result<(), ErrorWrapper> {
let time = Instant::now();
let smoothing = match method {
SmoothingMethod::Laplace => {
crate::echo!(
quiet,
" \x1b[1;96mSmoothing\x1b[0m with {iterations} iterations of Laplace"
);
Smoothing::Laplace {
iterations,
scale,
weighting: Weighting::Uniform,
preserve_boundary: hierarchical,
preserve_interfaces: hierarchical,
}
}
SmoothingMethod::Taubin => {
crate::echo!(
quiet,
" \x1b[1;96mSmoothing\x1b[0m with {iterations} iterations of Taubin"
);
Smoothing::Taubin {
iterations,
pass_band,
scale,
weighting: Weighting::Uniform,
preserve_boundary: hierarchical,
preserve_interfaces: hierarchical,
}
}
};
mesh.smooth(smoothing)?;
crate::echo!(quiet, " \x1b[1;92mDone\x1b[0m {:?}", time.elapsed());
Ok(())
}