use std::io::Cursor;
use imodfile::*;
fn create_test_model() -> Imod {
let mut model = Imod::default();
model.name = "TestModel".to_string();
model.xmax = 100;
model.ymax = 200;
model.zmax = 50;
model.objsize = 1;
let mut obj = Iobj::default();
obj.name = "Object1".to_string();
obj.contsize = 2;
obj.red = 1.0;
obj.green = 0.5;
obj.blue = 0.0;
obj.flags = IMOD_OBJFLAG_DRAW_LABEL | IMOD_OBJFLAG_SCALE_WDTH | IMOD_OBJFLAG_FILL;
let mut cont1 = Icont::default();
cont1.psize = 3;
cont1.pts = vec![
Ipoint { x: 0.0, y: 0.0, z: 0.0 },
Ipoint { x: 10.0, y: 0.0, z: 0.0 },
Ipoint { x: 0.0, y: 10.0, z: 0.0 },
];
cont1.surf = 0;
let mut cont2 = Icont::default();
cont2.psize = 4;
cont2.pts = vec![
Ipoint { x: 0.0, y: 0.0, z: 10.0 },
Ipoint { x: 20.0, y: 0.0, z: 10.0 },
Ipoint { x: 20.0, y: 20.0, z: 10.0 },
Ipoint { x: 0.0, y: 20.0, z: 10.0 },
];
cont2.surf = 0;
cont2.flags = 1;
cont2.time = 5;
obj.cont = vec![cont1, cont2];
let mut mesh = Imesh::default();
mesh.vsize = 4;
mesh.lsize = 5;
mesh.vert = vec![
Ipoint { x: 0.0, y: 0.0, z: 0.0 },
Ipoint { x: 10.0, y: 0.0, z: 0.0 },
Ipoint { x: 10.0, y: 10.0, z: 0.0 },
Ipoint { x: 0.0, y: 10.0, z: 0.0 },
];
mesh.list = vec![0, 1, 2, 3, -1]; mesh.flag = 0;
obj.mesh = vec![mesh];
obj.meshsize = 1;
let mut view = Iview::default();
view.label = "Default View".to_string();
view.fovy = 30.0;
model.obj = vec![obj];
model.view = vec![view];
model.viewsize = 1;
model
}
#[test]
fn test_binary_roundtrip() {
let model = create_test_model();
let mut buf = Vec::new();
let mut cursor = Cursor::new(&mut buf);
write_binary(&mut cursor, &model).expect("write_binary failed");
cursor.set_position(0);
let model2 = read_binary(&mut cursor).expect("read_binary failed");
assert_eq!(model.name, model2.name);
assert_eq!(model.obj.len(), model2.obj.len());
assert_eq!(model.xmax, model2.xmax);
assert_eq!(model.ymax, model2.ymax);
assert_eq!(model.zmax, model2.zmax);
let obj = &model.obj[0];
let obj2 = &model2.obj[0];
assert_eq!(obj.name, obj2.name);
assert_eq!(obj.contsize, obj2.contsize);
assert_eq!(obj.red, obj2.red);
assert_eq!(obj.green, obj2.green);
assert_eq!(obj.blue, obj2.blue);
assert_eq!(obj.flags, obj2.flags);
assert_eq!(obj.meshsize, obj2.meshsize);
for i in 0..obj.contsize as usize {
assert_eq!(obj.cont[i].psize, obj2.cont[i].psize);
assert_eq!(obj.cont[i].surf, obj2.cont[i].surf);
assert_eq!(obj.cont[i].flags, obj2.cont[i].flags);
assert_eq!(obj.cont[i].time, obj2.cont[i].time);
for j in 0..obj.cont[i].psize as usize {
let a = obj.cont[i].pts[j];
let b = obj2.cont[i].pts[j];
assert!((a.x - b.x).abs() < 1e-5, "point {} cont {} x: {} != {}", j, i, a.x, b.x);
assert!((a.y - b.y).abs() < 1e-5);
assert!((a.z - b.z).abs() < 1e-5);
}
}
let mesh = &obj.mesh[0];
let mesh2 = &obj2.mesh[0];
assert_eq!(mesh.vsize, mesh2.vsize);
assert_eq!(mesh.lsize, mesh2.lsize);
assert_eq!(mesh.list, mesh2.list);
for j in 0..mesh.vsize as usize {
assert!(
(mesh.vert[j].x - mesh2.vert[j].x).abs() < 1e-5,
"mesh vert {} x: {} != {}",
j,
mesh.vert[j].x,
mesh2.vert[j].x
);
}
}
#[test]
fn test_ascii_roundtrip() {
let model = create_test_model();
let mut buf = Vec::new();
write_ascii(&mut buf, &model).expect("write_ascii failed");
let ascii_text = String::from_utf8_lossy(&buf);
println!("ASCII output:\n{}", ascii_text);
let model2 = read_ascii(&mut Cursor::new(&buf)).expect("read_ascii failed");
assert_eq!(model.obj.len(), model2.obj.len());
assert_eq!(model.xmax, model2.xmax);
assert_eq!(model.ymax, model2.ymax);
if !model2.obj.is_empty() {
let obj2 = &model2.obj[0];
assert_eq!(obj2.name, "Object1");
assert!((obj2.red - 1.0).abs() < 0.01);
assert!((obj2.green - 0.5).abs() < 0.01);
assert_eq!(obj2.contsize, 2);
assert_eq!(obj2.cont.len(), 2);
if obj2.cont.len() > 0 {
assert_eq!(obj2.cont[0].psize, 3);
assert!(obj2.cont[0].pts.len() >= 3);
}
}
}
#[test]
fn test_binary_to_ascii() {
let model = create_test_model();
let mut bin_buf = Vec::new();
write_binary(&mut Cursor::new(&mut bin_buf), &model).expect("write_binary");
let model2 = read_binary(&mut Cursor::new(&bin_buf)).expect("read_binary");
let mut ascii_buf = Vec::new();
write_ascii(&mut ascii_buf, &model2).expect("write_ascii");
let ascii_text = String::from_utf8_lossy(&ascii_buf);
assert!(ascii_text.contains("imod 1"));
assert!(ascii_text.contains("Object1"));
assert!(ascii_text.contains("contour 0 0 3"));
assert!(ascii_text.contains("contour 1 0 4"));
}
#[test]
fn test_create_empty_model() {
let model = Imod::default();
let mut buf = Vec::new();
write_binary(&mut Cursor::new(&mut buf), &model).expect("write_binary empty");
let model2 = read_binary(&mut Cursor::new(&buf)).expect("read_binary empty");
assert_eq!(model2.objsize, 0);
assert!(model2.obj.is_empty());
}
#[test]
fn test_model_with_labels() {
let mut model = Imod::default();
model.objsize = 1;
let mut obj = Iobj::default();
obj.name = "LabelTest".to_string();
obj.contsize = 1;
let mut cont = Icont::default();
cont.psize = 2;
cont.pts = vec![
Ipoint { x: 0.0, y: 0.0, z: 0.0 },
Ipoint { x: 1.0, y: 1.0, z: 1.0 },
];
let mut label = Ilabel::default();
label.name = Some("ContourLabel".to_string());
label.items.push(IlabelItem {
name: "PointA".to_string(),
index: 0,
});
label.items.push(IlabelItem {
name: "PointB".to_string(),
index: 1,
});
cont.label = Some(label);
obj.cont = vec![cont];
model.obj = vec![obj];
let mut buf = Vec::new();
write_binary(&mut Cursor::new(&mut buf), &model).expect("write_binary with labels");
let model2 = read_binary(&mut Cursor::new(&buf)).expect("read_binary with labels");
let cont2 = &model2.obj[0].cont[0];
assert!(cont2.label.is_some());
let label2 = cont2.label.as_ref().unwrap();
assert_eq!(label2.name.as_deref(), Some("ContourLabel"));
assert_eq!(label2.items.len(), 2);
assert_eq!(label2.items[0].name, "PointA");
assert_eq!(label2.items[1].name, "PointB");
}
#[test]
fn test_model_with_point_sizes() {
let mut model = Imod::default();
model.objsize = 1;
let mut obj = Iobj::default();
obj.name = "SizeTest".to_string();
obj.contsize = 1;
let mut cont = Icont::default();
cont.psize = 3;
cont.pts = vec![
Ipoint { x: 0.0, y: 0.0, z: 0.0 },
Ipoint { x: 1.0, y: 0.0, z: 0.0 },
Ipoint { x: 0.0, y: 1.0, z: 0.0 },
];
cont.sizes = Some(vec![1.5, 2.0, 0.5]);
obj.cont = vec![cont];
model.obj = vec![obj];
let mut buf = Vec::new();
write_binary(&mut Cursor::new(&mut buf), &model).expect("write_binary with sizes");
let model2 = read_binary(&mut Cursor::new(&buf)).expect("read_binary with sizes");
let cont2 = &model2.obj[0].cont[0];
assert!(cont2.sizes.is_some());
let sizes = cont2.sizes.as_ref().unwrap();
assert_eq!(sizes.len(), 3);
assert!((sizes[0] - 1.5).abs() < 1e-5);
assert!((sizes[1] - 2.0).abs() < 1e-5);
assert!((sizes[2] - 0.5).abs() < 1e-5);
}
#[test]
fn test_clip_planes() {
let mut model = Imod::default();
model.objsize = 1;
let mut obj = Iobj::default();
obj.name = "ClipTest".to_string();
obj.contsize = 1;
obj.clips.count = 1;
obj.clips.flags = 1; obj.clips.normal[0] = Ipoint { x: 0.0, y: 0.0, z: 1.0 };
obj.clips.point[0] = Ipoint { x: 0.0, y: 0.0, z: 50.0 };
let mut cont = Icont::default();
cont.psize = 1;
cont.pts = vec![Ipoint { x: 10.0, y: 10.0, z: 10.0 }];
obj.cont = vec![cont];
model.obj = vec![obj];
let mut buf = Vec::new();
write_binary(&mut Cursor::new(&mut buf), &model).expect("write_binary with clips");
let model2 = read_binary(&mut Cursor::new(&buf)).expect("read_binary with clips");
let obj2 = &model2.obj[0];
assert_eq!(obj2.clips.count, 1);
assert!((obj2.clips.normal[0].x - 0.0).abs() < 1e-5);
assert!((obj2.clips.normal[0].z - 1.0).abs() < 1e-5);
assert!((obj2.clips.point[0].z - 50.0).abs() < 1e-5);
}
#[test]
fn test_file_read_write() {
let tmp = std::env::temp_dir().join("test_imod_roundtrip.mod");
let model = create_test_model();
{
let file = std::fs::File::create(&tmp).expect("create temp file");
let mut writer = std::io::BufWriter::new(file);
write_binary(&mut writer, &model).expect("write to file");
}
let file = std::fs::File::open(&tmp).expect("open temp file");
let mut reader = std::io::BufReader::new(file);
let model2 = read_binary(&mut reader).expect("read from file");
assert_eq!(model2.obj.len(), 1);
let _ = std::fs::remove_file(&tmp);
}