1use std::{fs::File, io::BufWriter, path::Path};
2
3use anyhow::Result;
4use byteorder::WriteBytesExt;
5use nalgebra::Vector4;
6
7use crate::{
8 affine::get_affine_and_translation,
9 tractogram::{Point, RefTractogramItem, Tractogram, TractogramItem},
10 Affine, Affine4, CHeader, Header, Spacing, Translation, TrkEndianness,
11};
12
13macro_rules! write_streamline {
14 ($writer:ident, $streamline:expr, $scalars:expr, $properties:expr) => {
15 if $writer.nb_scalars == 0 {
16 $streamline.write($writer);
17 } else {
18 $writer.writer.write_i32::<TrkEndianness>($streamline.len() as i32).unwrap();
19 $writer.real_n_count += 1;
20
21 let scalars = $scalars.chunks($writer.nb_scalars);
22 for (p, scalars) in $streamline.into_iter().zip(scalars) {
23 $writer.write_point(&p);
24 $writer.write_f32s(scalars);
25 }
26 }
27
28 $writer.write_f32s($properties);
29 };
30 ($writer:ident, $streamline:expr, $nb_points:expr) => {
32 $writer.writer.write_i32::<TrkEndianness>($nb_points as i32).unwrap();
33 for p in $streamline {
34 $writer.write_point(&p);
35 }
36 $writer.real_n_count += 1;
37 };
38}
39
40pub struct Writer {
41 writer: BufWriter<File>,
42 pub affine4: Affine4,
43 affine: Affine,
44 translation: Translation,
45 nb_scalars: usize,
46
47 real_n_count: i32,
48 raw: bool,
49 voxel_space: bool,
50}
51
52pub trait Writable {
53 fn write(self, w: &mut Writer);
54}
55
56impl Writable for Tractogram {
57 fn write(self, w: &mut Writer) {
58 for item in &self {
59 item.write(w);
60 }
61 }
62}
63
64impl Writable for TractogramItem {
65 fn write(self, writer: &mut Writer) {
66 let (streamline, scalars, properties) = self;
67 write_streamline!(writer, streamline, scalars.data.as_slice(), &properties);
68 }
69}
70
71impl<'data> Writable for RefTractogramItem<'data> {
72 fn write(self, writer: &mut Writer) {
73 let (streamline, scalars, properties) = self;
74 write_streamline!(writer, streamline, scalars, properties);
75 }
76}
77
78impl<'data> Writable for &'data [Point] {
79 fn write(self, writer: &mut Writer) {
80 write_streamline!(writer, self, self.len());
81 }
82}
83
84impl Writer {
85 pub fn new<P: AsRef<Path>>(path: P, reference: Option<&Header>) -> Result<Writer> {
86 let f = File::create(path).expect("Can't create new trk file.");
87 let mut writer = BufWriter::new(f);
88
89 let (affine4, nb_scalars) = match reference {
90 Some(header) => {
91 header.write(&mut writer)?;
92 let affine4 = header
93 .affine4_to_rasmm
94 .try_inverse()
95 .expect("Unable to inverse 4x4 affine matrix");
96 (affine4, header.scalars_name.len())
97 }
98 None => {
99 Header::default().write(&mut writer)?;
100 (Affine4::identity(), 0)
101 }
102 };
103 let (affine, translation) = get_affine_and_translation(&affine4);
104
105 Ok(Writer {
106 writer,
107 affine4,
108 affine,
109 translation,
110 real_n_count: 0,
111 nb_scalars,
112 raw: false,
113 voxel_space: false,
114 })
115 }
116
117 pub fn from_voxel_space(mut self, spacing: Spacing) -> Self {
126 if self.raw {
127 panic!("Can't use raw + voxel space reading");
128 }
129
130 self.voxel_space = true;
131 self.affine = Affine::from_diagonal(&spacing);
132 self.affine4 = Affine4::from_diagonal(&Vector4::new(spacing.x, spacing.y, spacing.z, 1.0));
133 self.translation = Translation::zeros();
134 self
135 }
136
137 pub fn raw(mut self) -> Self {
145 if self.voxel_space {
146 panic!("Can't use voxel space + raw reading");
147 }
148
149 self.raw = true;
150 self
151 }
152
153 pub fn reset_affine(&mut self) {
157 self.affine4 = Affine4::identity();
158 self.affine = Affine::identity();
159 self.translation = Translation::zeros();
160 }
161
162 pub fn apply_affine(&mut self, affine: &Affine4) {
166 self.affine4 = self.affine4 * affine;
167 let (affine, translation) = get_affine_and_translation(&self.affine4);
168 self.affine = affine;
169 self.translation = translation;
170 }
171
172 pub fn write<T: Writable>(&mut self, data: T) {
173 data.write(self);
174 }
175
176 pub fn write_from_iter<I>(&mut self, streamline: I, len: usize)
177 where
178 I: IntoIterator<Item = Point>,
179 {
180 write_streamline!(self, streamline, len);
181 }
182
183 fn write_point(&mut self, p: &Point) {
184 let p = if self.raw { *p } else { self.affine * p + self.translation };
185 self.writer.write_f32::<TrkEndianness>(p.x).unwrap();
186 self.writer.write_f32::<TrkEndianness>(p.y).unwrap();
187 self.writer.write_f32::<TrkEndianness>(p.z).unwrap();
188 }
189
190 fn write_f32s(&mut self, data: &[f32]) {
191 for &d in data {
192 self.writer.write_f32::<TrkEndianness>(d).unwrap();
193 }
194 }
195}
196
197impl Drop for Writer {
199 fn drop(&mut self) {
200 CHeader::seek_n_count_field(&mut self.writer)
201 .expect("Unable to seek to 'n_count' field before closing trk file.");
202 self.writer
203 .write_i32::<TrkEndianness>(self.real_n_count)
204 .expect("Unable to write 'n_count' field before closing trk file.");
205 }
206}