use std::io::{BufReader, BufWriter};
use imodfile::{read_ascii, write_ascii, write_binary, Imod, ImodError, ImodResult};
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 || args.contains(&"--help".to_string()) || args.contains(&"-h".to_string())
{
print_usage();
return;
}
let input_path = &args[1];
let output_path = args.get(2).filter(|p| !p.starts_with("--"));
let to_ascii = args.contains(&"--ascii".to_string())
|| args.contains(&"--to-ascii".to_string());
let is_ascii_input = args.contains(&"--ascii-in".to_string());
let dump_points = args.contains(&"--points".to_string());
let model = match read_model(input_path, is_ascii_input) {
Ok(m) => m,
Err(e) => {
eprintln!("Error: {}", e);
std::process::exit(1);
}
};
if let Some(out_path) = output_path {
if dump_points {
match std::fs::File::create(out_path) {
Ok(file) => {
let mut writer = BufWriter::new(file);
if let Err(e) = model.write_points(&mut writer) {
eprintln!("Error writing: {}", e);
std::process::exit(1);
}
}
Err(e) => {
eprintln!("Error creating {}: {}", out_path, e);
std::process::exit(1);
}
}
let total: i32 = model.obj.iter().map(|o| o.cont.iter().map(|c| c.psize).sum::<i32>()).sum();
eprintln!("Wrote {} points", total);
} else {
let want_ascii = to_ascii
|| out_path.ends_with(".txt")
|| out_path.ends_with(".ascii");
match write_model(out_path, &model, want_ascii) {
Ok(()) => {
eprintln!(
"Wrote {} with {} objects, {} views",
out_path,
model.obj.len(),
model.view.len()
);
}
Err(e) => {
eprintln!("Error writing: {}", e);
std::process::exit(1);
}
}
}
} else if dump_points {
let pts = model.to_points();
if pts.is_empty() {
eprintln!("(no points)");
} else {
print!("{}", pts);
}
} else {
print_summary(&model);
}
}
fn print_usage() {
eprintln!(
r#"imodfile — IMOD model file decoder/encoder
USAGE:
imodfile <input.mod> # dump model info
imodfile <input.mod> <output.mod> # copy/convert
imodfile <input.mod> --ascii # write as ASCII
imodfile <input.mod> output.txt # write as ASCII (.txt/.ascii)
imodfile <input.mod> output.txt --points # save point coordinates to file
imodfile <input.mod> --ascii-in # read as ASCII (if extension not .txt)
imodfile <input.mod> --points # dump all point coordinates to stdout
FLAGS:
--ascii, --to-ascii write output in ASCII format
--ascii-in force reading input as ASCII
--points print all points (one per line, empty row between contours)
-h, --help print this help
"#
);
}
fn read_model(path: &str, force_ascii: bool) -> ImodResult<Imod> {
if force_ascii {
let file = std::fs::File::open(path).map_err(|e| {
ImodError::Io(std::io::Error::new(e.kind(), format!("cannot open {}: {}", path, e)))
})?;
let mut reader = BufReader::new(file);
return read_ascii(&mut reader);
}
Imod::load(path)
}
fn write_model(path: &str, model: &Imod, ascii: bool) -> ImodResult<()> {
if ascii {
let file = std::fs::File::create(path).map_err(|e| {
ImodError::Io(std::io::Error::new(e.kind(), format!("cannot create {}: {}", path, e)))
})?;
let mut writer = BufWriter::new(file);
write_ascii(&mut writer, model)
} else {
let mut file = std::fs::File::create(path).map_err(|e| {
ImodError::Io(std::io::Error::new(e.kind(), format!("cannot create {}: {}", path, e)))
})?;
write_binary(&mut file, model)
}
}
fn print_summary(model: &Imod) {
println!("IMOD Model: {}", model.name);
println!(" Objects: {}", model.obj.len());
println!(" Views: {}", model.view.len());
println!(
" Size: {} x {} x {}",
model.xmax, model.ymax, model.zmax
);
println!(
" Offset: {} {} {}",
model.xoffset, model.yoffset, model.zoffset
);
println!(
" Scale: {} {} {}",
model.xscale, model.yscale, model.zscale
);
println!(" Pixsize: {}", model.pixsize);
println!(
" Angles: {} {} {}",
model.alpha, model.beta, model.gamma
);
println!(" Flags: 0x{:08x}", model.flags);
println!(" Slicer angles: {}", model.slicer_angles.len());
if let Some(ref ref_img) = model.ref_image {
println!(" Ref image: present");
println!(
" cscale: {} {} {}",
ref_img.cscale.x, ref_img.cscale.y, ref_img.cscale.z
);
}
for (i, obj) in model.obj.iter().enumerate() {
println!();
println!(" Object {}: {}", i, obj.name);
println!(
" Contours: {} Meshes: {} Surfsize: {}",
obj.contsize, obj.meshsize, obj.surfsize
);
println!(
" Color: {} {} {} trans={}",
obj.red, obj.green, obj.blue, obj.trans
);
println!(
" drawmode={} linewidth={} pdrawsize={}",
obj.drawmode, obj.linewidth, obj.pdrawsize
);
println!(" Flags: 0x{:08x}", obj.flags);
let total_pts: i32 = obj.cont.iter().map(|c| c.psize).sum();
println!(" Total points: {}", total_pts);
let show = obj.cont.len().min(5);
for c in &obj.cont[..show] {
let first_z = c.pts.first().map_or(0.0, |p| p.z);
let last_z = c.pts.last().map_or(0.0, |p| p.z);
println!(
" Contour {} pts: flags=0x{:x} time={} surf={} z_range={}..{}",
c.psize, c.flags, c.time, c.surf, first_z, last_z,
);
}
if obj.cont.len() > 5 {
println!(" ... and {} more contours", obj.cont.len() - 5);
}
for (mi, m) in obj.mesh.iter().enumerate() {
println!(
" Mesh {}: {} verts, {} indices, flag=0x{:x}",
mi, m.vsize, m.lsize, m.flag
);
}
}
if !model.view.is_empty() {
println!();
println!(" Views:");
for (i, v) in model.view.iter().enumerate() {
println!(
" View {}: fovy={} label='{}'",
i, v.fovy, v.label
);
}
}
}