use std::io::Cursor;
use imodfile::*;
fn create_test_model() -> Imod {
let cont1 = Icont {
psize: 3,
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 },
],
surf: 0,
..Icont::default()
};
let cont2 = Icont {
psize: 4,
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 },
],
surf: 0,
flags: 1,
time: 5,
..Icont::default()
};
let mesh = Imesh {
vsize: 4,
lsize: 5,
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 },
],
list: vec![0, 1, 2, 3, -1], flag: 0,
..Imesh::default()
};
let obj = Iobj {
name: "Object1".to_string(),
contsize: 2,
red: 1.0,
green: 0.5,
blue: 0.0,
flags: IMOD_OBJFLAG_DRAW_LABEL | IMOD_OBJFLAG_SCALE_WDTH | IMOD_OBJFLAG_FILL,
cont: vec![cont1, cont2],
mesh: vec![mesh],
meshsize: 1,
..Iobj::default()
};
let view = Iview {
label: "Default View".to_string(),
fovy: 30.0,
..Iview::default()
};
Imod {
name: "TestModel".to_string(),
xmax: 100,
ymax: 200,
zmax: 50,
objsize: 1,
obj: vec![obj],
view: vec![view],
viewsize: 1,
..Imod::default()
}
}
#[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
);
}
assert_eq!(model.view.len(), model2.view.len());
if !model.view.is_empty() {
let v = &model.view[0];
let v2 = &model2.view[0];
assert!((v.fovy - v2.fovy).abs() < 1e-5, "fovy: {} != {}", v.fovy, v2.fovy);
assert!((v.rad - v2.rad).abs() < 1e-5, "rad");
assert!((v.aspect - v2.aspect).abs() < 1e-5, "aspect");
assert!((v.cnear - v2.cnear).abs() < 1e-5, "cnear");
assert!((v.cfar - v2.cfar).abs() < 1e-5, "cfar");
assert!((v.rot.x - v2.rot.x).abs() < 1e-5, "rot.x");
assert!((v.trans.x - v2.trans.x).abs() < 1e-5, "trans.x");
assert!((v.scale.x - v2.scale.x).abs() < 1e-5, "scale.x");
assert!((v.mat[0] - v2.mat[0]).abs() < 1e-5, "mat[0]");
assert!((v.mat[15] - v2.mat[15]).abs() < 1e-5, "mat[15]");
assert_eq!(v.world, v2.world, "world");
assert_eq!(v.label, v2.label, "label");
assert!((v.lightx - v2.lightx).abs() < 1e-5, "lightx");
assert!((v.lighty - v2.lighty).abs() < 1e-5, "lighty");
assert!((v.plax - v2.plax).abs() < 1e-5, "plax");
assert!((v.dcstart - v2.dcstart).abs() < 1e-5, "dcstart");
assert!((v.dcend - v2.dcend).abs() < 1e-5, "dcend");
}
}
#[test]
fn test_view_roundtrip() {
let mut view1 = Iview {
fovy: 30.0,
rad: 1.5,
aspect: 1.33,
cnear: 0.1,
cfar: 1000.0,
rot: Ipoint { x: 10.0, y: 20.0, z: 30.0 },
trans: Ipoint { x: 1.0, y: 2.0, z: 3.0 },
scale: Ipoint { x: 0.5, y: 0.5, z: 1.0 },
world: 0x0010,
label: "Front View".to_string(),
lightx: 1.0,
lighty: 0.5,
plax: 3.0,
dcstart: 0.0,
dcend: 1.0,
..Iview::default()
};
view1.clips = IclipPlanes {
count: 1,
flags: 1,
normal: vec![Ipoint { x: 0.0, y: 0.0, z: 1.0 }],
point: vec![Ipoint { x: 0.0, y: 0.0, z: 50.0 }],
..IclipPlanes::default()
};
let view2 = Iview {
fovy: 45.0,
rad: 2.0,
aspect: 1.0,
cnear: 1.0,
cfar: 500.0,
rot: Ipoint { x: 90.0, y: 0.0, z: 0.0 },
trans: Ipoint { x: 0.0, y: 0.0, z: -10.0 },
scale: Ipoint { x: 1.0, y: 1.0, z: 1.0 },
world: 0x0010,
label: "Side View".to_string(),
..Iview::default()
};
let model = Imod {
name: "ViewTest".to_string(),
objsize: 0,
view: vec![view1, view2],
viewsize: 2,
cview: 0,
..Imod::default()
};
let mut buf = Vec::new();
write_binary(&mut Cursor::new(&mut buf), &model).expect("write_binary");
let model2 = read_binary(&mut Cursor::new(&buf)).expect("read_binary");
assert_eq!(model2.view.len(), 2);
assert_eq!(model2.cview, 0);
let v = &model.view[0];
let v2 = &model2.view[0];
assert!((v.fovy - v2.fovy).abs() < 1e-5, "fovy");
assert!((v.rad - v2.rad).abs() < 1e-5, "rad");
assert!((v.aspect - v2.aspect).abs() < 1e-5, "aspect");
assert!((v.cnear - v2.cnear).abs() < 1e-5, "cnear");
assert!((v.cfar - v2.cfar).abs() < 1e-5, "cfar");
assert!((v.rot.x - v2.rot.x).abs() < 1e-5, "rot.x");
assert!((v.rot.y - v2.rot.y).abs() < 1e-5, "rot.y");
assert!((v.rot.z - v2.rot.z).abs() < 1e-5, "rot.z");
assert!((v.trans.x - v2.trans.x).abs() < 1e-5, "trans.x");
assert!((v.trans.y - v2.trans.y).abs() < 1e-5, "trans.y");
assert!((v.trans.z - v2.trans.z).abs() < 1e-5, "trans.z");
assert!((v.scale.x - v2.scale.x).abs() < 1e-5, "scale.x");
assert!((v.scale.y - v2.scale.y).abs() < 1e-5, "scale.y");
assert!((v.scale.z - v2.scale.z).abs() < 1e-5, "scale.z");
assert!((v.mat[0] - v2.mat[0]).abs() < 1e-5, "mat[0]");
assert!((v.mat[15] - v2.mat[15]).abs() < 1e-5, "mat[15]");
assert_eq!(v.world, v2.world, "world");
assert_eq!(v.label, v2.label, "label");
assert!((v.lightx - v2.lightx).abs() < 1e-5, "lightx");
assert!((v.lighty - v2.lighty).abs() < 1e-5, "lighty");
assert!((v.plax - v2.plax).abs() < 1e-5, "plax");
assert!((v.dcstart - v2.dcstart).abs() < 1e-5, "dcstart");
assert!((v.dcend - v2.dcend).abs() < 1e-5, "dcend");
assert_eq!(v.clips.count, v2.clips.count, "clips count");
if v2.clips.count > 0 {
assert!((v.clips.normal[0].x - v2.clips.normal[0].x).abs() < 1e-5, "clip nx");
assert!((v.clips.normal[0].z - v2.clips.normal[0].z).abs() < 1e-5, "clip nz");
assert!((v.clips.point[0].z - v2.clips.point[0].z).abs() < 1e-5, "clip pz");
}
let v = &model.view[1];
let v2 = &model2.view[1];
assert!((v.fovy - v2.fovy).abs() < 1e-5, "v2 fovy");
assert_eq!(v.label, v2.label, "v2 label");
assert_eq!(v2.clips.count, 0, "v2 clips should be 0");
}
#[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.is_empty() {
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 label = Ilabel {
name: Some("ContourLabel".to_string()),
items: vec![
IlabelItem {
name: "PointA".to_string(),
index: 0,
},
IlabelItem {
name: "PointB".to_string(),
index: 1,
},
],
};
let cont = Icont {
psize: 2,
pts: vec![
Ipoint { x: 0.0, y: 0.0, z: 0.0 },
Ipoint { x: 1.0, y: 1.0, z: 1.0 },
],
label: Some(label),
..Icont::default()
};
let obj = Iobj {
name: "LabelTest".to_string(),
contsize: 1,
cont: vec![cont],
..Iobj::default()
};
let model = Imod {
objsize: 1,
obj: vec![obj],
..Imod::default()
};
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 cont = Icont {
psize: 3,
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 },
],
sizes: Some(vec![1.5, 2.0, 0.5]),
..Icont::default()
};
let obj = Iobj {
name: "SizeTest".to_string(),
contsize: 1,
cont: vec![cont],
..Iobj::default()
};
let model = Imod {
objsize: 1,
obj: vec![obj],
..Imod::default()
};
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 cont = Icont {
psize: 1,
pts: vec![Ipoint { x: 10.0, y: 10.0, z: 10.0 }],
..Icont::default()
};
let mut obj = Iobj {
name: "ClipTest".to_string(),
contsize: 1,
cont: vec![cont],
..Iobj::default()
};
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 model = Imod {
objsize: 1,
obj: vec![obj],
..Imod::default()
};
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);
}
#[test]
fn test_binary_writer_raw_bytes() {
let model = Imod {
objsize: 1,
obj: vec![Iobj {
name: "Test".to_string(),
contsize: 1,
cont: vec![Icont {
psize: 2,
pts: vec![Ipoint { x: 1.0, y: 2.0, z: 3.0 }, Ipoint { x: 4.0, y: 5.0, z: 6.0 }],
..Icont::default()
}],
..Iobj::default()
}],
..Imod::default()
};
let mut buf = Vec::new();
write_binary(&mut Cursor::new(&mut buf), &model).expect("write_binary");
assert_eq!(&buf[0..4], b"IMOD", "magic");
assert_eq!(&buf[4..8], b"V1.2", "version");
let obj_tag_pos = 240; assert_eq!(&buf[obj_tag_pos..obj_tag_pos + 4], b"OBJT", "OBJT tag");
let cont_tag_pos = obj_tag_pos + 4 + 176;
assert_eq!(&buf[cont_tag_pos..cont_tag_pos + 4], b"CONT", "CONT tag");
let psize = i32::from_be_bytes([buf[cont_tag_pos + 4], buf[cont_tag_pos + 5],
buf[cont_tag_pos + 6], buf[cont_tag_pos + 7]]);
assert_eq!(psize, 2, "contour psize");
let x1 = f32::from_be_bytes([buf[cont_tag_pos + 20], buf[cont_tag_pos + 21],
buf[cont_tag_pos + 22], buf[cont_tag_pos + 23]]);
assert!((x1 - 1.0).abs() < 1e-5, "first x = {}", x1);
let imat_tag_pos = cont_tag_pos + 4 + 16 + 24; assert_eq!(&buf[imat_tag_pos..imat_tag_pos + 4], b"IMAT", "IMAT tag");
let imat_size = i32::from_be_bytes([buf[imat_tag_pos + 4], buf[imat_tag_pos + 5],
buf[imat_tag_pos + 6], buf[imat_tag_pos + 7]]);
assert_eq!(imat_size, 16, "IMAT size");
assert_eq!(&buf[buf.len() - 4..], b"IEOF", "IEOF");
}
#[test]
fn test_ascii_writer_format() {
let model = Imod {
objsize: 1,
xmax: 100,
ymax: 200,
zmax: 50,
obj: vec![Iobj {
name: "TestObj".to_string(),
contsize: 2,
red: 1.0,
green: 0.5,
blue: 0.0,
cont: vec![
Icont { psize: 2, pts: vec![Ipoint { x: 0.0, y: 0.0, z: 0.0 }, Ipoint { x: 1.0, y: 0.0, z: 0.0 }], ..Icont::default() },
Icont { psize: 3, pts: vec![Ipoint { x: 0.0, y: 0.0, z: 1.0 }, Ipoint { x: 1.0, y: 0.0, z: 1.0 }, Ipoint { x: 0.0, y: 1.0, z: 1.0 }], ..Icont::default() },
],
..Iobj::default()
}],
..Imod::default()
};
let mut buf = Vec::new();
write_ascii(&mut buf, &model).expect("write_ascii");
let text = String::from_utf8(buf).expect("valid UTF-8");
assert!(text.starts_with("# imod ascii file version 2.0\n"), "ASCII header");
assert!(text.contains("imod 1\n"), "object count");
assert!(text.contains("max 100 200 50\n"), "dimensions");
assert!(text.contains("object 0 2 0\n"), "object header");
assert!(text.contains("name TestObj\n"), "object name");
assert!(text.contains("color 1 0.5 0 0\n"), "color");
assert!(text.contains("contour 0 0 2\n"), "first contour");
assert!(text.contains("contour 1 0 3\n"), "second contour");
assert!(text.contains("0 0 0\n"), "point 0");
assert!(text.contains("1 0 0\n"), "point 1");
assert!(text.ends_with("# end of IMOD model\n"), "footer");
}
#[test]
fn test_real_file_roundtrip() {
let original = read_binary(&mut Cursor::new(&std::fs::read("tests/test_001.mod").unwrap()))
.expect("read real file");
let mut buf = Vec::new();
write_binary(&mut Cursor::new(&mut buf), &original).expect("write real file");
let reloaded = read_binary(&mut Cursor::new(&buf)).expect("re-read real file");
assert_eq!(original.obj.len(), reloaded.obj.len());
assert_eq!(original.xmax, reloaded.xmax);
assert_eq!(original.ymax, reloaded.ymax);
assert_eq!(original.zmax, reloaded.zmax);
assert_eq!(original.name, reloaded.name);
for (oi, (o1, o2)) in original.obj.iter().zip(reloaded.obj.iter()).enumerate() {
assert_eq!(o1.name, o2.name, "obj {oi} name");
assert_eq!(o1.contsize, o2.contsize, "obj {oi} contsize");
assert_eq!(o1.cont.len(), o2.cont.len(), "obj {oi} cont len");
for (ci, (c1, c2)) in o1.cont.iter().zip(o2.cont.iter()).enumerate() {
assert_eq!(c1.psize, c2.psize, "obj {oi} cont {ci} psize");
assert_eq!(c1.flags, c2.flags, "obj {oi} cont {ci} flags");
assert_eq!(c1.time, c2.time, "obj {oi} cont {ci} time");
assert_eq!(c1.surf, c2.surf, "obj {oi} cont {ci} surf");
for (pi, (p1, p2)) in c1.pts.iter().zip(c2.pts.iter()).enumerate() {
assert!((p1.x - p2.x).abs() < 1e-4, "obj {oi} cont {ci} pt {pi} x");
assert!((p1.y - p2.y).abs() < 1e-4, "obj {oi} cont {ci} pt {pi} y");
assert!((p1.z - p2.z).abs() < 1e-4, "obj {oi} cont {ci} pt {pi} z");
}
}
}
}
#[test]
fn test_write_then_read_identical_model() {
let mut model = Imod {
objsize: 3,
name: "RoundTrip".to_string(),
xmax: 512,
ymax: 512,
zmax: 100,
..Imod::default()
};
model.obj.push(Iobj::default());
let mut obj1 = Iobj {
name: "empty-cont".to_string(),
contsize: 1,
..Iobj::default()
};
obj1.cont.push(Icont::default());
model.obj.push(obj1);
let mut obj2 = Iobj {
name: "with-pts".to_string(),
contsize: 1,
red: 0.0,
green: 1.0,
blue: 0.0,
..Iobj::default()
};
obj2.cont.push(Icont {
psize: 3,
pts: vec![
Ipoint { x: 100.0, y: 200.0, z: 300.0 },
Ipoint { x: 400.0, y: 500.0, z: 600.0 },
Ipoint { x: 700.0, y: 800.0, z: 900.0 },
],
..Icont::default()
});
model.obj.push(obj2);
let mut buf = Vec::new();
write_binary(&mut Cursor::new(&mut buf), &model).expect("write");
let model2 = read_binary(&mut Cursor::new(&buf)).expect("read");
assert_eq!(model2.objsize, 3);
assert_eq!(model2.obj.len(), 3);
assert_eq!(model2.obj[0].contsize, 0);
assert_eq!(model2.obj[1].contsize, 1);
assert_eq!(model2.obj[1].cont.len(), 1);
assert_eq!(model2.obj[1].cont[0].psize, 0);
assert_eq!(model2.obj[2].cont[0].psize, 3);
assert!((model2.obj[2].cont[0].pts[2].z - 900.0).abs() < 1e-5);
assert!((model2.obj[2].green - 1.0).abs() < 1e-5);
}
fn build_v01_binary(objects: &[(i32, &[&[Ipoint]])]) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(b"IMOD");
buf.extend_from_slice(b"V0.1");
let mut name = [0u8; 128];
name[..10].copy_from_slice(b"V01-Model\0");
buf.extend_from_slice(&name);
let objsize = objects.len() as i32;
for &v in &[100i32, 200, 50, objsize, 0, 1, 0, 0, 255] {
buf.extend_from_slice(&v.to_be_bytes());
}
for &v in &[0.0f32, 0.0, 0.0] {
buf.extend_from_slice(&v.to_be_bytes());
}
for &(contsize, contours) in objects {
buf.extend_from_slice(b"OBJT");
let mut oname = [0u8; 128];
oname[..7].copy_from_slice(b"Obj-V01");
buf.extend_from_slice(&oname);
for &v in &[contsize, 0i32, 0, 1] {
buf.extend_from_slice(&v.to_be_bytes());
}
for &v in &[0.0f32, 1.0, 0.0] {
buf.extend_from_slice(&v.to_be_bytes());
}
for &v in &[3i32, 1, 0, 0] {
buf.extend_from_slice(&v.to_be_bytes());
}
for pts in contours {
buf.extend_from_slice(b"CONT");
let psize = pts.len() as i32;
for &v in &[psize, 0i32, 0, 0] {
buf.extend_from_slice(&v.to_be_bytes());
}
buf.extend_from_slice(b"PNTS");
for pt in *pts {
buf.extend_from_slice(&pt.x.to_be_bytes());
buf.extend_from_slice(&pt.y.to_be_bytes());
buf.extend_from_slice(&pt.z.to_be_bytes());
}
}
}
buf
}
#[test]
fn test_v01_binary_read() {
let pts0 = [Ipoint { x: 1.0, y: 2.0, z: 3.0 }, Ipoint { x: 4.0, y: 5.0, z: 6.0 }];
let pts1 = [Ipoint { x: 10.0, y: 20.0, z: 30.0 }];
let contours: &[&[Ipoint]] = &[&pts0, &pts1];
let objects = &[(2i32, contours)];
let data = build_v01_binary(objects);
let model = read_binary(&mut Cursor::new(&data)).expect("read V0.1");
assert_eq!(model.name, "V01-Model");
assert_eq!(model.xmax, 100);
assert_eq!(model.ymax, 200);
assert_eq!(model.zmax, 50);
assert_eq!(model.objsize, 1);
assert_eq!(model.obj.len(), 1);
let obj = &model.obj[0];
assert_eq!(obj.name, "Obj-V01");
assert_eq!(obj.contsize, 2);
assert_eq!(obj.cont.len(), 2);
assert!((obj.green - 1.0).abs() < 1e-5);
let c0 = &obj.cont[0];
assert_eq!(c0.psize, 2);
assert_eq!(c0.pts.len(), 2);
assert!((c0.pts[0].x - 1.0).abs() < 1e-5);
assert!((c0.pts[0].y - 2.0).abs() < 1e-5);
assert!((c0.pts[0].z - 3.0).abs() < 1e-5);
assert!((c0.pts[1].x - 4.0).abs() < 1e-5);
assert!((c0.pts[1].y - 5.0).abs() < 1e-5);
assert!((c0.pts[1].z - 6.0).abs() < 1e-5);
let c1 = &obj.cont[1];
assert_eq!(c1.psize, 1);
assert_eq!(c1.pts.len(), 1);
assert!((c1.pts[0].x - 10.0).abs() < 1e-5);
assert!((c1.pts[0].y - 20.0).abs() < 1e-5);
assert!((c1.pts[0].z - 30.0).abs() < 1e-5);
}