mod coils;
mod compound;
mod cut;
mod magnet;
mod plasma;
mod validate;
mod vessel;
mod vmec;
use clap::{Parser, Subcommand};
use std::path::PathBuf;
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
#[derive(Parser, Debug)]
#[command(about = "alphastell — VMEC 由来の CAD 生成と検証")]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand, Debug)]
enum Command {
Vessel {
#[arg(long)]
input: PathBuf,
#[arg(long)]
output: PathBuf,
#[arg(long, default_value_t = 1.08)]
wall_s: f64,
#[arg(long, default_value_t = 100.0)]
scale: f64,
},
#[command(group(
clap::ArgGroup::new("op")
.required(true)
.multiple(false)
.args(["cut", "union"])
))]
Cut {
#[arg(short = 'i', long)]
input: PathBuf,
#[arg(short = 'o', long)]
output: PathBuf,
#[arg(short = 's', long, default_value = "0", value_parser = cut::parse_tau_fraction, allow_hyphen_values = true)]
start: f64,
#[arg(short = 'e', long, value_parser = cut::parse_tau_fraction, allow_hyphen_values = true)]
end: f64,
#[arg(short = 'c', long)]
cut: bool,
#[arg(short = 'u', long)]
union: bool,
},
Magnet {
#[arg(long)]
input: PathBuf,
#[arg(long)]
output: PathBuf,
#[arg(long, default_value_t = 0.4)]
width: f64,
#[arg(long, default_value_t = 0.5)]
thickness: f64,
#[arg(long, default_value_t = 360.0)]
toroidal_extent: f64,
},
Plasma {
#[arg(long)]
input: PathBuf,
#[arg(long)]
output: PathBuf,
#[arg(long, default_value_t = 1.0)]
scale: f64,
},
Compound {
#[arg(short = 'i', long = "input")]
inputs: Vec<PathBuf>,
#[arg(short = 'm', long = "input-magnet")]
input_magnet: Option<PathBuf>,
#[arg(short = 'o', long)]
output: PathBuf,
},
Validate {
a: PathBuf,
b: PathBuf,
#[arg(long, default_value_t = 4)]
max_ratio: u32,
#[arg(long, default_value_t = 0.01)]
tol: f64,
#[arg(long, default_value_t = false)]
union: bool,
},
}
fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Command::Vessel {
input,
output,
wall_s,
scale,
} => vessel::run(&input, &output, wall_s, scale),
Command::Cut {
input,
output,
start,
end,
cut,
union,
} => {
let mode = if cut {
cut::Mode::Intersect
} else {
debug_assert!(union, "clap ArgGroup guarantees exactly one of --cut / --union");
cut::Mode::Subtract
};
cut::run(&input, &output, start, end, mode)
}
Command::Magnet {
input,
output,
width,
thickness,
toroidal_extent,
} => magnet::run(&input, &output, width, thickness, toroidal_extent),
Command::Plasma {
input,
output,
scale,
} => plasma::run(&input, &output, scale),
Command::Compound {
inputs,
input_magnet,
output,
} => {
let mut extras: Vec<(String, Vec<cadrum::Solid>)> = Vec::new();
if let Some(coil_path) = input_magnet {
use cadrum::DVec3;
let remove_half_span_tau = 1.0f64 / 6.0;
let solids =
magnet::build_sector(&coil_path, 0.4, 0.5, remove_half_span_tau)?;
let scaled: Vec<cadrum::Solid> = solids
.into_iter()
.map(|s| s.scale(DVec3::ZERO, 100.0))
.collect();
extras.push((
format!("{} (outside ±1/6 τ, ×100)", coil_path.display()),
scaled,
));
}
compound::run(&inputs, extras, &output)
}
Command::Validate {
a,
b,
max_ratio,
tol,
union,
} => validate::run(&a, &b, max_ratio, tol, union),
}
}