use std::io::Seek;
use crate::chunk_ids::*;
#[derive(Clone)]
pub struct ImodPointsIter<'a> {
obj_idx: usize,
cont_idx: usize,
pt_idx: usize,
model: &'a Imod,
}
impl<'a> Iterator for ImodPointsIter<'a> {
type Item = (usize, usize, usize, &'a Ipoint);
fn next(&mut self) -> Option<Self::Item> {
while self.obj_idx < self.model.obj.len() {
let obj = &self.model.obj[self.obj_idx];
while self.cont_idx < obj.cont.len() {
let cont = &obj.cont[self.cont_idx];
if self.pt_idx < cont.pts.len() {
let item = (self.obj_idx, self.cont_idx, self.pt_idx, &cont.pts[self.pt_idx]);
self.pt_idx += 1;
return Some(item);
}
self.pt_idx = 0;
self.cont_idx += 1;
}
self.cont_idx = 0;
self.obj_idx += 1;
}
None
}
}
#[derive(Clone)]
pub struct IobjPointsIter<'a> {
cont_idx: usize,
pt_idx: usize,
obj: &'a Iobj,
}
impl<'a> Iterator for IobjPointsIter<'a> {
type Item = (usize, usize, &'a Ipoint);
fn next(&mut self) -> Option<Self::Item> {
while self.cont_idx < self.obj.cont.len() {
let cont = &self.obj.cont[self.cont_idx];
if self.pt_idx < cont.pts.len() {
let item = (self.cont_idx, self.pt_idx, &cont.pts[self.pt_idx]);
self.pt_idx += 1;
return Some(item);
}
self.pt_idx = 0;
self.cont_idx += 1;
}
None
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Ipoint {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Default for Ipoint {
fn default() -> Self {
Ipoint {
x: 0.0,
y: 0.0,
z: 0.0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Iindex {
pub object: i32,
pub contour: i32,
pub point: i32,
}
impl Default for Iindex {
fn default() -> Self {
Iindex {
object: -1,
contour: -1,
point: -1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Iplane {
pub a: f32,
pub b: f32,
pub c: f32,
pub d: f32,
}
impl Default for Iplane {
fn default() -> Self {
Iplane {
a: 0.0,
b: 0.0,
c: -1.0,
d: 0.0,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IclipPlanes {
pub count: i32,
pub flags: u32,
pub trans: u32,
pub plane: u32,
pub normal: Vec<Ipoint>,
pub point: Vec<Ipoint>,
}
impl Default for IclipPlanes {
fn default() -> Self {
let mut normal = Vec::with_capacity(IMOD_CLIPSIZE);
let mut point = Vec::with_capacity(IMOD_CLIPSIZE);
for _ in 0..IMOD_CLIPSIZE {
normal.push(Ipoint {
x: 0.0,
y: 0.0,
z: -1.0,
});
point.push(Ipoint::default());
}
IclipPlanes {
count: 0,
flags: 0,
trans: 0,
plane: 0,
normal,
point,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IlabelItem {
pub name: String,
pub index: i32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Ilabel {
pub name: Option<String>,
pub items: Vec<IlabelItem>,
}
impl Default for Ilabel {
fn default() -> Self {
Ilabel {
name: None,
items: Vec::new(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IstoreItem {
pub flags: u32,
pub index: i32,
pub value_f: f32,
pub value_i: i32,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Istore {
pub items: Vec<IstoreItem>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ImeshParams {
pub flags: [i32; 9],
pub overlap: [f32; 10],
pub cap_skip_zlist: Vec<i32>,
}
impl Default for ImeshParams {
fn default() -> Self {
ImeshParams {
flags: [0; 9],
overlap: [0.0; 10],
cap_skip_zlist: Vec::new(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Imesh {
pub vert: Vec<Ipoint>,
pub list: Vec<i32>,
pub vsize: i32,
pub lsize: i32,
pub flag: u32,
pub time: u16,
pub surf: u16,
pub store: Istore,
}
impl Default for Imesh {
fn default() -> Self {
Imesh {
vert: Vec::new(),
list: Vec::new(),
vsize: 0,
lsize: 0,
flag: 0,
time: 0,
surf: 0,
store: Istore::default(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Icont {
pub pts: Vec<Ipoint>,
pub psize: i32,
pub flags: u32,
pub time: i32,
pub surf: i32,
pub sizes: Option<Vec<f32>>,
pub label: Option<Ilabel>,
pub store: Istore,
}
impl Default for Icont {
fn default() -> Self {
Icont {
pts: Vec::new(),
psize: 0,
flags: 0,
time: 0,
surf: 0,
sizes: None,
label: None,
store: Istore::default(),
}
}
}
pub const IMOD_OBJFLAG_OPEN: u32 = 0x001;
pub const IMOD_OBJFLAG_FILL: u32 = 0x002;
pub const IMOD_OBJFLAG_SCAT: u32 = 0x004;
pub const IMOD_OBJFLAG_OUT: u32 = 0x008;
pub const IMOD_OBJFLAG_MESH: u32 = 0x010;
pub const IMOD_OBJFLAG_NOLINE: u32 = 0x020;
pub const IMOD_OBJFLAG_TWO_SIDE: u32 = 0x040;
pub const IMOD_OBJFLAG_FCOLOR: u32 = 0x080;
pub const IMOD_OBJFLAG_OFF: u32 = 0x100;
pub const IMOD_OBJFLAG_DRAW_LABEL: u32 = 0x200;
pub const IMOD_OBJFLAG_SCALE_WDTH: u32 = 0x400;
pub const IMOD_OBJFLAG_TIME: u32 = 0x800;
pub const IMOD_OBJFLAG_USE_VALUE: u32 = 0x1000;
pub const IMOD_OBJFLAG_MCOLOR: u32 = 0x2000;
pub const IMOD_OBJFLAG_FCOLOR_PNT: u32 = 0x4000;
pub const IMOD_OBJFLAG_PNT_ON_SEC: u32 = 0x8000;
pub const IMOD_OBJFLAG_ANTI_ALIAS: u32 = 0x10000;
#[derive(Debug, Clone, PartialEq)]
pub struct Iobj {
pub name: String,
pub extra: Vec<i32>,
pub cont: Vec<Icont>,
pub mesh: Vec<Imesh>,
pub contsize: i32,
pub meshsize: i32,
pub surfsize: i32,
pub flags: u32,
pub axis: i32,
pub drawmode: i32,
pub red: f32,
pub green: f32,
pub blue: f32,
pub pdrawsize: i32,
pub symbol: u8,
pub symsize: u8,
pub linewidth2: u8,
pub linewidth: u8,
pub linesty: u8,
pub symflags: u8,
pub sympad: u8,
pub trans: u8,
pub ambient: u8,
pub diffuse: u8,
pub specular: u8,
pub shininess: u8,
pub fillred: u8,
pub fillgreen: u8,
pub fillblue: u8,
pub quality: u8,
pub mat2: i32,
pub valblack: u8,
pub valwhite: u8,
pub matflags2: i32,
pub mesh_thickness: u8,
pub clips: IclipPlanes,
pub label: Option<Ilabel>,
pub mesh_param: Option<ImeshParams>,
pub store: Istore,
}
impl Iobj {
pub fn points(&self) -> IobjPointsIter<'_> {
IobjPointsIter {
cont_idx: 0,
pt_idx: 0,
obj: self,
}
}
}
impl Default for Iobj {
fn default() -> Self {
Iobj {
name: String::new(),
extra: vec![0; IOBJ_EXSIZE],
cont: Vec::new(),
mesh: Vec::new(),
contsize: 0,
meshsize: 0,
surfsize: 0,
flags: IMOD_OBJFLAG_DRAW_LABEL | IMOD_OBJFLAG_SCALE_WDTH,
axis: 0,
drawmode: 1,
red: 0.5,
green: 0.5,
blue: 0.5,
pdrawsize: 0,
symbol: 0,
symsize: 3,
linewidth2: 1,
linewidth: 1,
linesty: 0,
symflags: 0,
sympad: 0,
trans: 0,
ambient: 102,
diffuse: 255,
specular: 127,
shininess: 4,
fillred: 0,
fillgreen: 0,
fillblue: 0,
quality: 0,
mat2: 0,
valblack: 0,
valwhite: 255,
matflags2: 0,
mesh_thickness: 0,
clips: IclipPlanes::default(),
label: None,
mesh_param: None,
store: Istore::default(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SlicerAngles {
pub time: i32,
pub angles: [f32; 3],
pub center: Ipoint,
pub label: String,
}
impl Default for SlicerAngles {
fn default() -> Self {
SlicerAngles {
time: 0,
angles: [0.0; 3],
center: Ipoint::default(),
label: String::new(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IrefImage {
pub cscale: Ipoint,
pub ctrans: Ipoint,
pub crot: Ipoint,
pub otrans: Ipoint,
pub orot: Ipoint,
}
impl Default for IrefImage {
fn default() -> Self {
IrefImage {
cscale: Ipoint {
x: 1.0,
y: 1.0,
z: 1.0,
},
ctrans: Ipoint::default(),
crot: Ipoint::default(),
otrans: Ipoint::default(),
orot: Ipoint::default(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Iview {
pub fovy: f32,
pub rad: f32,
pub aspect: f32,
pub cnear: f32,
pub cfar: f32,
pub rot: Ipoint,
pub trans: Ipoint,
pub scale: Ipoint,
pub world: u32,
pub mat: [f32; 16],
pub label: String,
pub lightx: f32,
pub lighty: f32,
pub plax: f32,
pub dcstart: f32,
pub dcend: f32,
pub clips: IclipPlanes,
pub objview: Vec<u8>,
pub objvsize: i32,
}
impl Default for Iview {
fn default() -> Self {
let mut mat = [0.0f32; 16];
mat[0] = 1.0;
mat[5] = 1.0;
mat[10] = 1.0;
mat[15] = 1.0;
Iview {
fovy: 0.0,
rad: 1.0,
aspect: 1.0,
cnear: 0.0,
cfar: 1.0,
rot: Ipoint::default(),
trans: Ipoint::default(),
scale: Ipoint {
x: 1.0,
y: 1.0,
z: 1.0,
},
world: 0x0010, mat,
label: String::new(),
lightx: 0.0,
lighty: 0.0,
plax: 5.0,
dcstart: 0.0,
dcend: 1.0,
clips: IclipPlanes::default(),
objview: Vec::new(),
objvsize: 0,
}
}
}
pub const IMODF_FLIPYZ: u32 = 0x0001;
pub const IMODF_NEW_TO_3DMOD: u32 = 0x0002;
pub const IMODF_TILTOK: u32 = 0x0004;
pub const IMODF_OTRANS_ORIGIN: u32 = 0x0008;
pub const IMODF_Z_FROM_MINUSPT5: u32 = 0x0010;
pub const IMODF_MAT1_IS_BYTES: u32 = 0x0020;
pub const IMODF_MULTIPLE_CLIP: u32 = 0x0040;
pub const IMODF_HAS_MESH_THICK: u32 = 0x0080;
pub const IMODF_ROT90X: u32 = 0x0100;
#[derive(Debug, Clone, PartialEq)]
pub struct Imod {
pub name: String,
pub obj: Vec<Iobj>,
pub objsize: i32,
pub flags: u32,
pub drawmode: i32,
pub mousemode: i32,
pub blacklevel: i32,
pub whitelevel: i32,
pub xmax: i32,
pub ymax: i32,
pub zmax: i32,
pub xoffset: f32,
pub yoffset: f32,
pub zoffset: f32,
pub xscale: f32,
pub yscale: f32,
pub zscale: f32,
pub cindex: Iindex,
pub ctime: i32,
pub tmax: i32,
pub pixsize: f32,
pub units: i32,
pub csum: u32,
pub alpha: f32,
pub beta: f32,
pub gamma: f32,
pub view: Vec<Iview>,
pub cview: i32,
pub viewsize: i32,
pub cur_obj_group: i32,
pub cur_mesh_surf: i32,
pub xybin: i32,
pub zbin: i32,
pub ref_image: Option<IrefImage>,
pub slicer_angles: Vec<SlicerAngles>,
pub store: Istore,
}
impl Default for Imod {
fn default() -> Self {
let mut name = [0u8; 14];
name[..13].copy_from_slice(b"IMOD-NewModel");
Imod {
name: "IMOD-NewModel".to_string(),
obj: Vec::new(),
objsize: 0,
flags: IMODF_NEW_TO_3DMOD | IMODF_Z_FROM_MINUSPT5,
drawmode: 1,
mousemode: 0, blacklevel: 0,
whitelevel: 255,
xmax: 1,
ymax: 1,
zmax: 1,
xoffset: 0.0,
yoffset: 0.0,
zoffset: 0.0,
xscale: 1.0,
yscale: 1.0,
zscale: 1.0,
cindex: Iindex::default(),
ctime: 0,
tmax: 0,
pixsize: 1.0,
units: 0,
csum: 0,
alpha: 0.0,
beta: 0.0,
gamma: 0.0,
view: Vec::new(),
cview: 0,
viewsize: 0,
cur_obj_group: -1,
cur_mesh_surf: -1,
xybin: 1,
zbin: 1,
ref_image: None,
slicer_angles: Vec::new(),
store: Istore::default(),
}
}
}
impl Imod {
pub fn points(&self) -> ImodPointsIter<'_> {
ImodPointsIter {
obj_idx: 0,
cont_idx: 0,
pt_idx: 0,
model: self,
}
}
pub fn load(path: &str) -> crate::error::ImodResult<Self> {
let file = std::fs::File::open(path).map_err(|e| {
crate::error::ImodError::Io(std::io::Error::new(
e.kind(),
format!("cannot open {}: {}", path, e),
))
})?;
let mut reader = std::io::BufReader::new(file);
let result = crate::binary::read_binary(&mut reader);
match result {
Ok(model) => Ok(model),
Err(crate::error::ImodError::InvalidMagic) => {
reader.rewind().map_err(crate::error::ImodError::Io)?;
crate::ascii::read_ascii(&mut reader)
}
Err(e) => Err(e),
}
}
pub fn save(&self, path: &str) -> crate::error::ImodResult<()> {
let file = std::fs::File::create(path).map_err(|e| {
crate::error::ImodError::Io(std::io::Error::new(
e.kind(),
format!("cannot create {}: {}", path, e),
))
})?;
let want_ascii = path.ends_with(".txt") || path.ends_with(".ascii");
if want_ascii {
let mut writer = std::io::BufWriter::new(file);
crate::ascii::write_ascii(&mut writer, self)
} else {
let mut writer = std::io::BufWriter::new(file);
crate::binary::write_binary(&mut writer, self)
}
}
pub fn save_points(&self, path: &str) -> std::io::Result<()> {
let file = std::fs::File::create(path)?;
let mut writer = std::io::BufWriter::new(file);
self.write_points(&mut writer)
}
pub fn to_points(&self) -> String {
let mut out = String::new();
for obj in &self.obj {
for (ci, cont) in obj.cont.iter().enumerate() {
if ci > 0 {
out.push('\n');
}
for pt in &cont.pts {
out.push_str(&format!("{} {} {}\n", pt.x, pt.y, pt.z));
}
}
}
out
}
pub fn write_points<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
for obj in &self.obj {
for (ci, cont) in obj.cont.iter().enumerate() {
if ci > 0 {
writeln!(writer)?;
}
for pt in &cont.pts {
writeln!(writer, "{} {} {}", pt.x, pt.y, pt.z)?;
}
}
}
Ok(())
}
}