1use std::{fs::File, io::BufReader, path::Path};
2
3use anyhow::{Context, Result};
4use byteorder::{BigEndian, ByteOrder, LittleEndian, ReadBytesExt};
5use nalgebra::Vector3;
6
7use crate::{
8 cheader::Endianness,
9 tractogram::{Point, Points, Streamlines, Tractogram, TractogramItem},
10 Affine, ArraySequence, Header, Spacing, Translation, Writer,
11};
12
13pub struct Reader {
14 reader: BufReader<File>,
15 endianness: Endianness,
16 pub header: Header,
17
18 raw: bool,
19 voxel_space: Option<Spacing>,
20
21 floats_per_point: usize,
22 buffer: Vec<f32>,
23}
24
25impl Reader {
26 pub fn new<P: AsRef<Path>>(path: P) -> Result<Reader> {
30 let f = File::open(path.as_ref())
31 .with_context(|| format!("Failed to load {:?}", path.as_ref()))?;
32 let mut reader = BufReader::new(f);
33 let (header, endianness) = Header::read(&mut reader)?;
34 let floats_per_point = 3 + header.scalars_name.len();
35 let buffer = Vec::with_capacity(300);
36
37 let raw = false;
38 let voxel_space = None;
39
40 Ok(Reader { reader, endianness, header, raw, voxel_space, floats_per_point, buffer })
41 }
42
43 pub fn to_voxel_space(mut self, spacing: Spacing) -> Self {
49 if self.raw {
50 panic!("Can't use raw + voxel space reading");
51 }
52
53 self.voxel_space = Some(spacing);
54 self.header.affine_to_rasmm =
55 Affine::from_diagonal(&Vector3::new(1.0 / spacing.x, 1.0 / spacing.y, 1.0 / spacing.z));
56 self.header.translation = Translation::zeros();
57 self
58 }
59
60 pub fn raw(mut self) -> Self {
64 if self.voxel_space.is_some() {
65 panic!("Can't use voxel space + raw reading");
66 }
67
68 self.raw = true;
69 self
70 }
71
72 pub fn build_writer<P: AsRef<Path>>(&self, path: P) -> Result<Writer> {
74 let mut w = Writer::new(path, Some(&self.header))?;
75 if let Some(spacing) = self.voxel_space {
76 w = w.from_voxel_space(spacing);
77 } else if self.raw {
78 w = w.raw();
79 }
80 Ok(w)
81 }
82
83 pub fn into_streamlines_iter(self) -> StreamlinesIter {
85 StreamlinesIter { reader: self }
86 }
87
88 pub fn tractogram(&mut self) -> Tractogram {
90 match self.endianness {
91 Endianness::Little => self.read_tractogram_::<LittleEndian>(),
92 Endianness::Big => self.read_tractogram_::<BigEndian>(),
93 }
94 }
95
96 pub fn streamlines(&mut self) -> Streamlines {
98 match self.endianness {
99 Endianness::Little => self.read_points_::<LittleEndian>(),
100 Endianness::Big => self.read_points_::<BigEndian>(),
101 }
102 }
103
104 fn read_tractogram_<E: ByteOrder>(&mut self) -> Tractogram {
105 let mut lengths = Vec::new();
107 let mut v = Vec::with_capacity(300);
108 let mut scalars = ArraySequence::with_capacity(300);
109 let mut properties = ArraySequence::with_capacity(300);
110 while let Ok(nb_points) = self.reader.read_i32::<E>() {
111 lengths.push(nb_points as usize);
112 self.read_streamline::<E>(&mut v, &mut scalars, nb_points as usize);
113 self.read_properties_to_arr::<E>(&mut properties);
114 }
115
116 self.buffer = vec![];
117 Tractogram::new(Streamlines::new(lengths, v), scalars, properties)
118 }
119
120 fn read_points_<E: ByteOrder>(&mut self) -> Streamlines {
121 let mut lengths = Vec::new();
123 let mut v = Vec::with_capacity(300);
124 while let Ok(nb_points) = self.reader.read_i32::<E>() {
125 lengths.push(nb_points as usize);
126 self.read_streamline_fast::<E>(&mut v, nb_points as usize);
127 }
128
129 self.buffer = vec![];
130 Streamlines::new(lengths, v)
131 }
132
133 fn read_streamline<E: ByteOrder>(
134 &mut self,
135 points: &mut Points,
136 scalars: &mut ArraySequence<f32>,
137 nb_points: usize,
138 ) {
139 self.read_floats::<E>(nb_points);
140 for floats in self.buffer.chunks(self.floats_per_point) {
141 self.add_points(points, floats);
142 for f in &floats[3..] {
143 scalars.push(*f);
144 }
145 }
146 scalars.end_push();
147 }
148
149 fn read_streamline_fast<E: ByteOrder>(&mut self, points: &mut Points, nb_points: usize) {
151 self.read_floats::<E>(nb_points);
152 for floats in self.buffer.chunks(self.floats_per_point) {
153 self.add_points(points, floats);
154 }
156
157 for _ in 0..self.header.properties_name.len() {
159 self.reader.read_f32::<E>().unwrap();
160 }
161 }
162
163 fn read_floats<E: ByteOrder>(&mut self, nb_points: usize) {
167 let nb_floats = nb_points * self.floats_per_point;
170 self.buffer.resize(nb_floats, 0.0);
171 self.reader.read_f32_into::<E>(self.buffer.as_mut_slice()).unwrap();
172 }
173
174 #[inline(always)]
175 fn add_points(&self, points: &mut Points, floats: &[f32]) {
176 let mut p = Point::new(floats[0], floats[1], floats[2]);
177 if !self.raw {
178 p = (self.header.affine_to_rasmm * p) + self.header.translation;
179 }
180 points.push(p);
181 }
182
183 fn read_properties_to_arr<E: ByteOrder>(&mut self, properties: &mut ArraySequence<f32>) {
184 for _ in 0..self.header.properties_name.len() {
185 properties.push(self.reader.read_f32::<E>().unwrap());
186 }
187 properties.end_push();
188 }
189
190 fn read_properties_to_vec<E: ByteOrder>(&mut self, properties: &mut Vec<f32>) {
191 for _ in 0..self.header.properties_name.len() {
192 properties.push(self.reader.read_f32::<E>().unwrap());
193 }
194 }
195
196 fn read_nb_points(&mut self) -> Option<usize> {
197 match self.endianness {
198 Endianness::Little => self.reader.read_i32::<LittleEndian>(),
199 Endianness::Big => self.reader.read_i32::<BigEndian>(),
200 }
201 .ok()
202 .map(|nb_points| nb_points as usize)
203 }
204}
205
206impl Iterator for Reader {
207 type Item = TractogramItem;
208
209 fn next(&mut self) -> Option<TractogramItem> {
210 let nb_points = self.read_nb_points()?;
211 let mut streamline = Vec::with_capacity(nb_points);
212 let mut scalars = ArraySequence::with_capacity(nb_points * self.header.scalars_name.len());
213 let mut properties = Vec::with_capacity(self.header.properties_name.len());
214 match self.endianness {
215 Endianness::Little => {
216 self.read_streamline::<LittleEndian>(&mut streamline, &mut scalars, nb_points);
217 self.read_properties_to_vec::<LittleEndian>(&mut properties);
218 }
219 Endianness::Big => {
220 self.read_streamline::<BigEndian>(&mut streamline, &mut scalars, nb_points);
221 self.read_properties_to_vec::<BigEndian>(&mut properties);
222 }
223 };
224 Some((streamline, scalars, properties))
225 }
226}
227
228pub struct StreamlinesIter {
229 reader: Reader,
230}
231
232impl Iterator for StreamlinesIter {
233 type Item = Points;
234
235 fn next(&mut self) -> Option<Points> {
236 let nb_points = self.reader.read_nb_points()?;
237 let mut streamline = Vec::with_capacity(nb_points);
238 match self.reader.endianness {
239 Endianness::Little => {
240 self.reader.read_streamline_fast::<LittleEndian>(&mut streamline, nb_points);
241 }
242 Endianness::Big => {
243 self.reader.read_streamline_fast::<BigEndian>(&mut streamline, nb_points);
244 }
245 };
246 Some(streamline)
247 }
248}