vtkio/
parser.rs

1// TODO: migrating to a newer version of nom could reduce the detected "cognitive complexity"
2// caused by macros.
3#![allow(clippy::cognitive_complexity)]
4
5use std::marker::PhantomData;
6use std::str;
7
8use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian};
9use nom::{self, eol, ErrorKind, IResult};
10use num_traits::FromPrimitive;
11
12pub use crate::basic::*;
13use crate::model::*;
14
15use crate::model::ByteOrder as ByteOrderTag;
16
17/*
18 * Parsing routines
19 */
20
21// Parse the file version
22named!(version<&[u8], Version>, sp!(
23 do_parse!(
24     take_until!("#") >>
25     tag!("#") >>
26     tag_no_case!("vtk") >>
27     tag_no_case!("DataFile") >>
28     tag_no_case!("Version") >>
29     ver: separated_pair!(u8_b, tag!("."), u8_b) >>
30     eol >>
31     (Version::new(ver))
32     )
33 )
34);
35
36named!(file_type<&[u8], FileType>,
37       alt!( tag_no_case!("ASCII") => { |_| FileType::ASCII } |
38             tag_no_case!("BINARY") => { |_| FileType::Binary } ) );
39
40named!(title<&[u8], &str>, map_res!(
41  do_parse!(
42      ttl: take_until!("\n") >>
43      eol >>
44      (ttl)),
45      str::from_utf8 )
46);
47
48named!(header<&[u8], (Version, String, FileType)>, sp!(
49     do_parse!(
50         ver: version >>
51         ttl: title >>
52         ft:  file_type >>
53         ((ver, String::from(ttl), ft))
54         )
55    )
56);
57
58named!(data_type< &[u8], ScalarType >, alt!(
59        tag_no_case!("bit")            => { |_| ScalarType::Bit } |
60        tag_no_case!("int")            => { |_| ScalarType::I32 } |
61        tag_no_case!("char")           => { |_| ScalarType::I8 } |
62        tag_no_case!("long")           => { |_| ScalarType::I64 } |
63        tag_no_case!("short")          => { |_| ScalarType::I16 } |
64        tag_no_case!("float")          => { |_| ScalarType::F32 } |
65        tag_no_case!("double")         => { |_| ScalarType::F64 } |
66        tag_no_case!("unsigned_int")   => { |_| ScalarType::U32 } |
67        tag_no_case!("unsigned_char")  => { |_| ScalarType::U8 } |
68        tag_no_case!("unsigned_long")  => { |_| ScalarType::U64 } |
69        tag_no_case!("unsigned_short") => { |_| ScalarType::U16 } ));
70
71named!(pub usize_b<&[u8], usize>, call!(integer) );
72named!(pub u32_b<&[u8], u32>, call!(integer) );
73named!(pub u8_b<&[u8], u8>, call!(integer) );
74named!(pub f32_b<&[u8], f32>, call!(real::<f32>) );
75
76named!(name, take_till!(|x: u8| b" \t\n\r".contains(&x)));
77
78enum Axis {
79    X,
80    Y,
81    Z,
82}
83
84/**
85 * Mesh data parsing.
86 */
87pub struct VtkParser<BO: ByteOrder>(PhantomData<BO>);
88
89/// Helper struct for parsing polygon topology sections.
90#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
91enum PolyDataTopology {
92    Verts = 0,
93    Lines,
94    Polys,
95    Strips,
96}
97
98impl<BO: ByteOrder + 'static> VtkParser<BO> {
99    #[allow(unused_variables)]
100    fn points(input: &[u8], ft: FileType) -> IResult<&[u8], IOBuffer> {
101        do_parse!(
102            input,
103            n: ws!(do_parse!(tag_no_case!("POINTS") >> n: u32_b >> (n)))
104                >> vec: switch!(
105                       do_parse!(
106                           dt: sp!( data_type ) >>
107                           tag!("\n") >>
108                           (dt) ),
109                                ScalarType::F32 => call!( parse_data_buffer::<f32, BO>, 3*n as usize, ft ) |
110                                ScalarType::F64 => call!( parse_data_buffer::<f64, BO>, 3*n as usize, ft ) )
111                >> (vec)
112        )
113    }
114
115    /// Parse a collection of cells. The given tag should be one of
116    ///  * "CELLS"
117    ///  * "VERTICES"
118    ///  * "LINES"
119    ///  * "POLYGONS"
120    ///  * "TRIANGLE_STRIPS"
121    fn cell_verts<'a>(
122        input: &'a [u8],
123        tag: &'static str,
124        ft: FileType,
125    ) -> IResult<&'a [u8], VertexNumbers> {
126        do_parse!(
127            input,
128            n: ws!(do_parse!(tag_no_case!(tag) >> n: u32_b >> (n)))
129                >> size: sp!(u32_b)
130                >> tag!("\n")
131                >> data: call!(parse_data_vec::<u32, BO>, size as usize, ft)
132                >> ({
133                    VertexNumbers::Legacy {
134                        num_cells: n,
135                        vertices: data,
136                    }
137                })
138        )
139    }
140
141    #[allow(unused_variables)]
142    fn coordinates(input: &[u8], axis: Axis, ft: FileType) -> IResult<&[u8], IOBuffer> {
143        let tag = match axis {
144            Axis::X => "X_COORDINATES",
145            Axis::Y => "Y_COORDINATES",
146            Axis::Z => "Z_COORDINATES",
147        };
148        do_parse!(
149            input,
150            n: ws!(do_parse!(tag_no_case!(tag) >> n: u32_b >> (n)))
151                >> vec: switch!(
152                       do_parse!(
153                           dt: sp!( data_type ) >>
154                           tag!("\n") >>
155                           (dt) ),
156                                ScalarType::F32 => call!( parse_data_buffer::<f32, BO>, n as usize, ft ) |
157                                ScalarType::F64 => call!( parse_data_buffer::<f64, BO>, n as usize, ft ) )
158                >> (vec)
159        )
160    }
161
162    /// Recognize and throw away `METADATA` block. Metadata is spearated by an empty line.
163    fn meta(input: &[u8]) -> IResult<&[u8], ()> {
164        complete!(
165            input,
166            ws!(do_parse!(
167                tag_no_case!("METADATA") >> take_until!("\n\n") >> ()
168            ))
169        )
170    }
171
172    /**
173     * Attribute Parsing
174     */
175
176    fn attribute_data(
177        input: &[u8],
178        n: usize,
179        data_type: ScalarType,
180        ft: FileType,
181    ) -> IResult<&[u8], IOBuffer> {
182        match data_type {
183            ScalarType::Bit => parse_data_bit_buffer(input, n, ft),
184            ScalarType::U8 => parse_data_buffer_u8(input, n, ft),
185            ScalarType::I8 => parse_data_buffer_i8(input, n, ft),
186            ScalarType::U16 => parse_data_buffer::<u16, BO>(input, n, ft),
187            ScalarType::I16 => parse_data_buffer::<i16, BO>(input, n, ft),
188            ScalarType::U32 => parse_data_buffer::<u32, BO>(input, n, ft),
189            ScalarType::I32 => parse_data_buffer::<i32, BO>(input, n, ft),
190            ScalarType::U64 => parse_data_buffer::<u64, BO>(input, n, ft),
191            ScalarType::I64 => parse_data_buffer::<i64, BO>(input, n, ft),
192            ScalarType::F32 => parse_data_buffer::<f32, BO>(input, n, ft),
193            ScalarType::F64 => parse_data_buffer::<f64, BO>(input, n, ft),
194        }
195    }
196
197    named!(
198        lookup_table,
199        alt_complete!(
200        sp!( do_parse!( tag_no_case!("LOOKUP_TABLE") >> n: name >> (n) ) ) |
201        eof!() => { |_| &b""[..] } |
202        eol
203        )
204    );
205
206    fn attribute_scalars(
207        input: &[u8],
208        num_elements: usize,
209        ft: FileType,
210    ) -> IResult<&[u8], Attribute> {
211        ws!(
212            input,
213            do_parse!(
214                tag_no_case!("SCALARS")
215                    >> name: map_res!(name, str::from_utf8)
216                    >> dt: data_type
217                    >> num_comp: opt!(u32_b)
218                    >> lookup_tbl_name: opt!(map_res!(Self::lookup_table, str::from_utf8))
219                    >> data: call!(
220                        Self::attribute_data,
221                        num_comp.unwrap_or(1) as usize * num_elements,
222                        dt,
223                        ft
224                    )
225                    >> opt!(Self::meta)
226                    >> (Attribute::DataArray(DataArray {
227                        name: String::from(name),
228                        elem: ElementType::Scalars {
229                            num_comp: num_comp.unwrap_or(1),
230                            lookup_table: lookup_tbl_name.and_then(|x| if x == "default" {
231                                None
232                            } else {
233                                Some(String::from(x))
234                            }),
235                        },
236                        data
237                    }))
238            )
239        )
240    }
241
242    fn attribute_lookup_table(input: &[u8], ft: FileType) -> IResult<&[u8], Attribute> {
243        ws!(
244            input,
245            do_parse!(
246                tag_no_case!("LOOKUP_TABLE")
247                    >> name: map_res!(name, str::from_utf8)
248                    >> num_elements: u32_b
249                    >> data: call!(
250                        Self::attribute_data,
251                        4 * num_elements as usize,
252                        ScalarType::F32,
253                        ft
254                    )
255                    >> opt!(Self::meta)
256                    >> (Attribute::DataArray(DataArray {
257                        name: String::from(name),
258                        elem: ElementType::LookupTable,
259                        data
260                    }))
261            )
262        )
263    }
264
265    /// Helper to `attribute_color_scalars`. This function calls the appropriate data parser for color
266    /// scalars.
267    fn attribute_color_scalars_data(
268        input: &[u8],
269        n: usize,
270        ft: FileType,
271    ) -> IResult<&[u8], IOBuffer> {
272        match ft {
273            FileType::ASCII => Self::attribute_data(input, n, ScalarType::F32, ft),
274            FileType::Binary => Self::attribute_data(input, n, ScalarType::U8, ft),
275        }
276    }
277
278    fn attribute_color_scalars(
279        input: &[u8],
280        num_elements: usize,
281        ft: FileType,
282    ) -> IResult<&[u8], Attribute> {
283        ws!(
284            input,
285            do_parse!(
286                tag_no_case!("COLOR_SCALARS")
287                    >> name: map_res!(name, str::from_utf8)
288                    >> num_comp: u32_b
289                    >> data: call!(
290                        Self::attribute_color_scalars_data,
291                        num_comp as usize * num_elements,
292                        ft
293                    )
294                    >> opt!(Self::meta)
295                    >> (Attribute::DataArray(DataArray {
296                        name: String::from(name),
297                        elem: ElementType::ColorScalars(num_comp),
298                        data
299                    }))
300            )
301        )
302    }
303
304    fn attribute_vectors(
305        input: &[u8],
306        num_elements: usize,
307        ft: FileType,
308    ) -> IResult<&[u8], Attribute> {
309        ws!(
310            input,
311            do_parse!(
312                tag_no_case!("VECTORS")
313                    >> name: map_res!(name, str::from_utf8)
314                    >> dt: data_type
315                    >> data: call!(Self::attribute_data, 3 * num_elements, dt, ft)
316                    >> opt!(Self::meta)
317                    >> (Attribute::DataArray(DataArray {
318                        name: String::from(name),
319                        elem: ElementType::Vectors,
320                        data
321                    }))
322            )
323        )
324    }
325
326    fn attribute_normals(
327        input: &[u8],
328        num_elements: usize,
329        ft: FileType,
330    ) -> IResult<&[u8], Attribute> {
331        ws!(
332            input,
333            do_parse!(
334                tag_no_case!("NORMALS")
335                    >> name: map_res!(name, str::from_utf8)
336                    >> dt: data_type
337                    >> data: call!(Self::attribute_data, 3 * num_elements, dt, ft)
338                    >> opt!(Self::meta)
339                    >> (Attribute::DataArray(DataArray {
340                        name: String::from(name),
341                        elem: ElementType::Normals,
342                        data
343                    }))
344            )
345        )
346    }
347
348    fn attribute_tex_coords(
349        input: &[u8],
350        num_elements: usize,
351        ft: FileType,
352    ) -> IResult<&[u8], Attribute> {
353        ws!(
354            input,
355            do_parse!(
356                tag_no_case!("TEXTURE_COORDINATES")
357                    >> name: map_res!(name, str::from_utf8)
358                    >> dim: u32_b
359                    >> dt: data_type
360                    >> data: call!(Self::attribute_data, dim as usize * num_elements, dt, ft)
361                    >> opt!(Self::meta)
362                    >> (Attribute::DataArray(DataArray {
363                        name: String::from(name),
364                        elem: ElementType::TCoords(dim),
365                        data
366                    }))
367            )
368        )
369    }
370
371    fn attribute_tensors(
372        input: &[u8],
373        num_elements: usize,
374        ft: FileType,
375    ) -> IResult<&[u8], Attribute> {
376        ws!(
377            input,
378            do_parse!(
379                tag_no_case!("TENSORS")
380                    >> name: map_res!(name, str::from_utf8)
381                    >> dt: data_type
382                    >> data: call!(Self::attribute_data, 9 * num_elements, dt, ft)
383                    >> opt!(Self::meta)
384                    >> (Attribute::DataArray(DataArray {
385                        name: String::from(name),
386                        elem: ElementType::Tensors,
387                        data
388                    }))
389            )
390        )
391    }
392
393    fn attribute_field_array(input: &[u8], ft: FileType) -> IResult<&[u8], FieldArray> {
394        ws!(
395            input,
396            do_parse!(
397                name: map_res!(name, str::from_utf8)
398                    >> num_comp: u32_b
399                    >> num_tuples: u32_b
400                    >> dt: data_type
401                    >> data: call!(
402                        Self::attribute_data,
403                        (num_comp * num_tuples) as usize,
404                        dt,
405                        ft
406                    )
407                    >> opt!(Self::meta)
408                    >> (FieldArray {
409                        name: String::from(name),
410                        elem: num_comp,
411                        data
412                    })
413            )
414        )
415    }
416
417    fn attribute_field(input: &[u8], ft: FileType) -> IResult<&[u8], Attribute> {
418        ws!(
419            input,
420            do_parse!(
421                tag_no_case!("FIELD")
422                    >> name: map_res!(name, str::from_utf8)
423                    >> n: u32_b
424                    >> data_array:
425                        many_m_n!(
426                            n as usize,
427                            n as usize,
428                            call!(Self::attribute_field_array, ft)
429                        )
430                    >> (Attribute::Field {
431                        name: String::from(name),
432                        data_array
433                    })
434            )
435        )
436    }
437
438    fn attribute(input: &[u8], num_elements: usize, ft: FileType) -> IResult<&[u8], Attribute> {
439        ws!(
440            input,
441            alt!(
442                call!(Self::attribute_scalars, num_elements, ft)
443                    | call!(Self::attribute_color_scalars, num_elements, ft)
444                    | call!(Self::attribute_lookup_table, ft)
445                    | call!(Self::attribute_vectors, num_elements, ft)
446                    | call!(Self::attribute_normals, num_elements, ft)
447                    | call!(Self::attribute_tex_coords, num_elements, ft)
448                    | call!(Self::attribute_tensors, num_elements, ft)
449                    | call!(Self::attribute_field, ft)
450            )
451        )
452    }
453
454    fn point_attributes(input: &[u8], ft: FileType) -> IResult<&[u8], Vec<Attribute>> {
455        ws!(
456            input,
457            alt_complete!(
458            do_parse!(
459                tag_no_case!("POINT_DATA") >>
460                n: sp!(u32_b) >>
461                vec: many0!( call!( Self::attribute, n as usize, ft ) ) >>
462                (vec)
463                ) |
464            ws!( eof!() ) => { |_| Vec::new() }
465            )
466        )
467    }
468
469    fn cell_attributes(input: &[u8], ft: FileType) -> IResult<&[u8], Vec<Attribute>> {
470        ws!(
471            input,
472            alt_complete!(
473                do_parse!(
474                    ws!( tag_no_case!("CELL_DATA") ) >>
475                    n: sp!( u32_b ) >>
476                    vec: many0!( call!( Self::attribute, n as usize, ft ) ) >>
477                    (vec)
478                ) |
479                ws!( eof!() ) => { |_| Vec::new() }
480            )
481        )
482    }
483
484    fn attributes(input: &[u8], ft: FileType) -> IResult<&[u8], Attributes> {
485        ws!(
486            input,
487            do_parse!(
488                c1: opt!(call!(Self::cell_attributes, ft))
489                    >> p: opt!(call!(Self::point_attributes, ft))
490                    >> c2: opt!(call!(Self::cell_attributes, ft))
491                    >> (Attributes {
492                        point: p.unwrap_or_default(),
493                        cell: if let Some(c) = c1 {
494                            c
495                        } else {
496                            c2.unwrap_or_default()
497                        }
498                    })
499            )
500        )
501    }
502
503    /// Parse structured points dataset.
504    fn structured_points(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> {
505        ws!(
506            input,
507            do_parse!(
508                tag_no_case!("STRUCTURED_POINTS")
509                    >> parms:
510                        permutation!(
511                            do_parse!(
512                                tag_no_case!("DIMENSIONS")
513                                    >> nx: u32_b
514                                    >> ny: u32_b
515                                    >> nz: u32_b
516                                    >> ([nx, ny, nz])
517                            ),
518                            do_parse!(
519                                tag_no_case!("ORIGIN")
520                                    >> x: f32_b
521                                    >> y: f32_b
522                                    >> z: f32_b
523                                    >> ([x, y, z])
524                            ),
525                            do_parse!(
526                                alt_complete!(
527                                    tag_no_case!("SPACING") | tag_no_case!("ASPECT_RATIO")
528                                ) >> sx: f32_b
529                                    >> sy: f32_b
530                                    >> sz: f32_b
531                                    >> ([sx, sy, sz])
532                            )
533                        )
534                    >> data: call!(Self::attributes, ft)
535                    >> (DataSet::ImageData {
536                        extent: Extent::Dims(parms.0),
537                        origin: parms.1,
538                        spacing: parms.2,
539                        meta: None,
540                        pieces: vec![Piece::Inline(Box::new(ImageDataPiece {
541                            extent: Extent::Dims(parms.0),
542                            data
543                        }))]
544                    })
545            )
546        )
547    }
548
549    /// Parse structured grid dataset.
550    fn structured_grid(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> {
551        ws!(
552            input,
553            do_parse!(
554                tag_no_case!("STRUCTURED_GRID")
555                    >> dims: do_parse!(
556                        tag_no_case!("DIMENSIONS")
557                            >> nx: u32_b
558                            >> ny: u32_b
559                            >> nz: u32_b
560                            >> ([nx, ny, nz])
561                    )
562                    >> points: call!(Self::points, ft)
563                    >> opt!(Self::meta)
564                    >> data: call!(Self::attributes, ft)
565                    >> (DataSet::inline(StructuredGridPiece {
566                        extent: Extent::Dims(dims),
567                        points,
568                        data
569                    }))
570            )
571        )
572    }
573
574    /// Parse rectilinear grid dataset.
575    fn rectilinear_grid(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> {
576        ws!(
577            input,
578            do_parse!(
579                tag_no_case!("RECTILINEAR_GRID")
580                    >> dims: do_parse!(
581                        tag_no_case!("DIMENSIONS")
582                            >> nx: u32_b
583                            >> ny: u32_b
584                            >> nz: u32_b
585                            >> ([nx, ny, nz])
586                    )
587                    >> x: call!(Self::coordinates, Axis::X, ft)
588                    >> y: call!(Self::coordinates, Axis::Y, ft)
589                    >> z: call!(Self::coordinates, Axis::Z, ft)
590                    >> data: call!(Self::attributes, ft)
591                    >> opt!(complete!(Self::meta))
592                    >> (DataSet::inline(RectilinearGridPiece {
593                        extent: Extent::Dims(dims),
594                        coords: Coordinates { x, y, z },
595                        data
596                    }))
597            )
598        )
599    }
600
601    /// Parse field dataset.
602    fn field_data(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> {
603        let res = Self::attribute_field(input, ft);
604        match res {
605            IResult::Done(i, o) => {
606                if let Attribute::Field { name, data_array } = o {
607                    IResult::Done(i, DataSet::Field { name, data_array })
608                } else {
609                    IResult::Error(nom::Err::Code(ErrorKind::Custom(1u32)))
610                }
611            }
612            IResult::Incomplete(e) => IResult::Incomplete(e),
613            IResult::Error(e) => IResult::Error(e),
614        }
615    }
616
617    // Parse a single cell type. Essentially a byte converted to` CellType` enum.
618    named!(pub cell_type<&[u8], CellType>,
619    map_opt!( u8_b, |x| CellType::from_u8(x) )
620    );
621
622    named!(pub cell_type_binary<&[u8], CellType>,
623    map_opt!( i32::from_binary::<BO>, |x| CellType::from_u8(x as u8) )
624    );
625
626    fn cell_type_data(input: &[u8], n: usize, ft: FileType) -> IResult<&[u8], Vec<CellType>> {
627        match ft {
628            FileType::ASCII => many_m_n!(input, n, n, ws!(Self::cell_type)),
629            FileType::Binary => many_m_n!(input, n, n, Self::cell_type_binary),
630        }
631    }
632
633    /// Parse cell types for unstructured grids
634    fn cell_types(input: &[u8], ft: FileType) -> IResult<&[u8], Vec<CellType>> {
635        do_parse!(
636            input,
637            ws!(tag_no_case!("CELL_TYPES"))
638                >> n: sp!(usize_b)
639                >> tag!("\n")
640                >> data: dbg!(call!(Self::cell_type_data, n, ft))
641                >> (data)
642        )
643    }
644
645    /// Parse UNSTRUCTURED_GRID type dataset
646    fn unstructured_grid(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> {
647        ws!(
648            input,
649            do_parse!(
650                tag_no_case!("UNSTRUCTURED_GRID")
651                    >> p: call!(Self::points, ft)
652                    >> opt!(Self::meta)
653                    >> cell_verts: call!(Self::cell_verts, "CELLS", ft)
654                    >> types: call!(Self::cell_types, ft)
655                    >> data: call!(Self::attributes, ft)
656                    >> (DataSet::inline(UnstructuredGridPiece {
657                        points: p,
658                        cells: Cells { cell_verts, types },
659                        data
660                    }))
661            )
662        )
663    }
664
665    /// Parse PolyData topology
666    fn poly_data_topo(
667        input: &[u8],
668        ft: FileType,
669    ) -> IResult<&[u8], (PolyDataTopology, VertexNumbers)> {
670        alt_complete!(
671            input,
672            map!(call!(Self::cell_verts, "LINES", ft), |x| {
673                (PolyDataTopology::Lines, x)
674            }) | map!(call!(Self::cell_verts, "POLYGONS", ft), |x| {
675                (PolyDataTopology::Polys, x)
676            }) | map!(call!(Self::cell_verts, "VERTICES", ft), |x| {
677                (PolyDataTopology::Verts, x)
678            }) | map!(call!(Self::cell_verts, "TRIANGLE_STRIPS", ft), |x| {
679                (PolyDataTopology::Strips, x)
680            })
681        )
682    }
683
684    /// Parse POLYDATA type dataset
685    #[allow(unused_comparisons)] // Suppress the warning of using 0 in many_m_n!(..)
686    fn poly_data(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> {
687        do_parse!(
688            input,
689            tag_no_case!("POLYDATA")
690                >> points: call!(Self::points, ft)
691                >> opt!(Self::meta)
692                >> topo1: opt!(call!(Self::poly_data_topo, ft))
693                >> topo2: opt!(call!(Self::poly_data_topo, ft))
694                >> topo3: opt!(call!(Self::poly_data_topo, ft))
695                >> topo4: opt!(call!(Self::poly_data_topo, ft))
696                >> data: call!(Self::attributes, ft)
697                >> ({
698                    // The following algorithm is just to avoid unnecessary cloning.
699                    // There may be a simpler way to do this.
700                    let mut topos = [topo1, topo2, topo3, topo4];
701                    let vertsi = topos
702                        .iter()
703                        .position(|x| x.as_ref().map(|x| x.0) == Some(PolyDataTopology::Verts));
704                    let linesi = topos
705                        .iter()
706                        .position(|x| x.as_ref().map(|x| x.0) == Some(PolyDataTopology::Lines));
707                    let polysi = topos
708                        .iter()
709                        .position(|x| x.as_ref().map(|x| x.0) == Some(PolyDataTopology::Polys));
710                    let stripsi = topos
711                        .iter()
712                        .position(|x| x.as_ref().map(|x| x.0) == Some(PolyDataTopology::Strips));
713                    let mut indices = [0, 1, 2, 3];
714
715                    vertsi.map(|i| {
716                        indices.swap(i, 0);
717                        topos.swap(i, 0)
718                    });
719                    linesi.map(|i| {
720                        let i = indices[i];
721                        indices.swap(i, 1);
722                        topos.swap(i, 1)
723                    });
724                    polysi.map(|i| {
725                        let i = indices[i];
726                        indices.swap(i, 2);
727                        topos.swap(i, 2)
728                    });
729                    stripsi.map(|i| {
730                        let i = indices[i];
731                        indices.swap(i, 3);
732                        topos.swap(i, 3)
733                    });
734
735                    let [verts, lines, polys, strips] = topos;
736
737                    DataSet::inline(PolyDataPiece {
738                        points,
739                        verts: verts.map(|x| x.1),
740                        lines: lines.map(|x| x.1),
741                        polys: polys.map(|x| x.1),
742                        strips: strips.map(|x| x.1),
743                        data,
744                    })
745                })
746        )
747    }
748
749    fn dataset(input: &[u8], file_type: FileType) -> IResult<&[u8], DataSet> {
750        alt_complete!(
751            input,
752            do_parse!(
753                tag_no_case!("DATASET")
754                    >> whitespace
755                    >> tn: alt!(
756                        call!(Self::poly_data, file_type)
757                            | call!(Self::structured_grid, file_type)
758                            | call!(Self::rectilinear_grid, file_type)
759                            | call!(Self::structured_points, file_type)
760                            | call!(Self::unstructured_grid, file_type)
761                    )
762                    >> (tn)
763            ) | call!(Self::field_data, file_type)
764        )
765    }
766
767    /// Parse the entire vtk file
768    fn vtk(input: &[u8]) -> IResult<&[u8], Vtk> {
769        complete!(
770            input,
771            ws!(do_parse!(
772                h: header
773                    >> d: call!(Self::dataset, h.2)
774                    >> (Vtk {
775                        version: h.0,
776                        // This is ignored in Legacy formats
777                        byte_order: ByteOrderTag::new::<BO>(),
778                        title: h.1,
779                        data: d,
780                        file_path: None,
781                    })
782            ))
783        )
784    }
785}
786
787/// Parse the entire VTK file using native endian byte order.
788pub fn parse_ne(input: &[u8]) -> IResult<&[u8], Vtk> {
789    VtkParser::<NativeEndian>::vtk(input)
790}
791
792/// Parse the entire VTK file using little endian byte order.
793pub fn parse_le(input: &[u8]) -> IResult<&[u8], Vtk> {
794    VtkParser::<LittleEndian>::vtk(input)
795}
796
797/// Parse the entire VTK file using big endian byte order.
798///
799/// This is the default VTK byte order. Binary `.vtk` files produced by ParaView are in big endian
800/// form.
801pub fn parse_be(input: &[u8]) -> IResult<&[u8], Vtk> {
802    VtkParser::<BigEndian>::vtk(input)
803}
804
805#[cfg(test)]
806mod tests {
807    use super::*;
808    use nom::IResult;
809
810    #[test]
811    fn file_type_test() {
812        let f = file_type("BINARY".as_bytes());
813        assert_eq!(f, IResult::Done(&b""[..], FileType::Binary));
814        let f = file_type("ASCII".as_bytes());
815        assert_eq!(f, IResult::Done(&b""[..], FileType::ASCII));
816    }
817    #[test]
818    fn version_test() {
819        let f = version("\n\t# vtk DataFile Version 2.0  \ntitle\n".as_bytes());
820        assert_eq!(f, IResult::Done("title\n".as_bytes(), Version::new((2, 0))));
821    }
822    #[test]
823    fn title_test() {
824        let f = title("This is a title\nBINARY".as_bytes());
825        assert_eq!(f, IResult::Done("BINARY".as_bytes(), "This is a title"));
826    }
827    #[test]
828    fn points_test() {
829        let in1 = "POINTS 0 float\n";
830        let in2 = "POINTS 3 float\n2 45 2 3 4 1 46 2 0\nother";
831        let f = VtkParser::<NativeEndian>::points(in1.as_bytes(), FileType::ASCII);
832        assert_eq!(f, IResult::Done("".as_bytes(), Vec::<f32>::new().into()));
833        let f = VtkParser::<NativeEndian>::points(in2.as_bytes(), FileType::ASCII);
834        assert_eq!(
835            f,
836            IResult::Done(
837                "other".as_bytes(),
838                vec![2.0f32, 45., 2., 3., 4., 1., 46., 2., 0.].into()
839            )
840        );
841    }
842    #[test]
843    fn cells_test() {
844        let in1 = "CELLS 0 0\n";
845        let in2 = "CELLS 1 3\n2 1 2\nother";
846
847        let f = VtkParser::<NativeEndian>::cell_verts(in1.as_bytes(), "CELLS", FileType::ASCII);
848        assert_eq!(
849            f,
850            IResult::Done(
851                "".as_bytes(),
852                VertexNumbers::Legacy {
853                    num_cells: 0,
854                    vertices: vec![]
855                }
856            )
857        );
858        let f = VtkParser::<NativeEndian>::cell_verts(in2.as_bytes(), "CELLS", FileType::ASCII);
859        assert_eq!(
860            f,
861            IResult::Done(
862                "other".as_bytes(),
863                VertexNumbers::Legacy {
864                    num_cells: 1,
865                    vertices: vec![2, 1, 2]
866                }
867            )
868        );
869    }
870    #[test]
871    fn cell_type_test() {
872        let f = VtkParser::<NativeEndian>::cell_type("2".as_bytes());
873        assert_eq!(f, IResult::Done("".as_bytes(), CellType::PolyVertex));
874        let f = VtkParser::<NativeEndian>::cell_type("10".as_bytes());
875        assert_eq!(f, IResult::Done("".as_bytes(), CellType::Tetra));
876    }
877
878    macro_rules! test {
879        ($fn:ident ($in:expr, $($args:expr),*) => ($rem:expr, $out:expr)) => {
880            assert_eq!(VtkParser::<NativeEndian>::$fn($in.as_bytes(), $($args),*), IResult::Done($rem.as_bytes(), $out.clone()));
881        };
882        ($fn:ident ($in:expr) => ($rem:expr, $out:expr)) => {
883            assert_eq!(VtkParser::<NativeEndian>::$fn($in.as_bytes()), IResult::Done($rem.as_bytes(), $out.clone()));
884        };
885        ($fn:ident ($in:expr, $($args:expr),*) => $out:expr) => {
886            test!($fn($in, $($args),*) => ("", $out));
887        };
888        ($fn:ident ($in:expr) => $out:expr) => {
889            test!($fn($in) => ("", $out));
890        }
891    }
892
893    #[test]
894    fn cell_types_test() {
895        let in1 = "CELL_TYPES 0\nother";
896        let out1 = Vec::<CellType>::new();
897        let in2 = "CELL_TYPES 3\n2 1 10\nother";
898        let out2 = vec![CellType::PolyVertex, CellType::Vertex, CellType::Tetra];
899        test!(cell_types(in1, FileType::ASCII) => ("other", out1));
900        test!(cell_types(in2, FileType::ASCII) => ("other", out2));
901    }
902
903    #[test]
904    fn unstructured_grid_test() {
905        let in1 = "UNSTRUCTURED_GRID\nPOINTS 4 float\n\
906                   2 45 2 3 4 1 46 2 0 4 32 1\nCELLS 2 10\n4 0 1 2 3\n4 3 2 1 0
907                   CELL_TYPES 2\n 10 10\nother";
908        let out1 = DataSet::inline(UnstructuredGridPiece {
909            points: vec![2.0f32, 45., 2., 3., 4., 1., 46., 2., 0., 4., 32., 1.].into(),
910            cells: Cells {
911                cell_verts: VertexNumbers::Legacy {
912                    num_cells: 2,
913                    vertices: vec![4, 0, 1, 2, 3, 4, 3, 2, 1, 0],
914                },
915                types: vec![CellType::Tetra; 2],
916            },
917            data: Attributes::new(),
918        });
919
920        test!(unstructured_grid(in1, FileType::ASCII) => ("other", out1));
921    }
922    #[test]
923    fn attribute_test() {
924        // scalar attribute
925        let in1 = "SCALARS cell_scalars int 1\n0 1 2 3 4 5";
926        let out1 = Attribute::DataArray(DataArray {
927            name: String::from("cell_scalars"),
928            elem: ElementType::Scalars {
929                num_comp: 1,
930                lookup_table: None,
931            },
932            data: vec![0, 1, 2, 3, 4, 5].into(),
933        });
934        test!(attribute(in1, 6, FileType::ASCII) => ("", out1));
935    }
936    #[test]
937    fn attributes_test() {
938        // empty cell attributes
939        test!(cell_attributes("\n", FileType::ASCII) => Vec::new());
940        // empty point attributes
941        test!(point_attributes("", FileType::ASCII) => Vec::new());
942        // empty
943        test!(attributes("\n", FileType::ASCII) => Attributes::new());
944        // scalar cell attribute
945        let in1 = "CELL_DATA 6\nSCALARS cell_scalars int 1\n0 1 2 3 4 5\n";
946        let scalar_data = DataArray {
947            name: String::new(),
948            elem: ElementType::Scalars {
949                num_comp: 1,
950                lookup_table: None,
951            },
952            data: vec![0, 1, 2, 3, 4, 5].into(),
953        };
954        let out1 = vec![Attribute::DataArray(DataArray {
955            name: String::from("cell_scalars"),
956            ..scalar_data.clone()
957        })];
958        test!(cell_attributes(in1, FileType::ASCII) => out1);
959        // scalar point and cell attributes
960        let in2 = "POINT_DATA 6\n SCALARS point_scalars int 1\n0 1 2 3 4 5\n
961                   CELL_DATA 6\n SCALARS cell_scalars int 1\n0 1 2 3 4 5";
962        let pt_res = vec![Attribute::DataArray(DataArray {
963            name: String::from("point_scalars"),
964            ..scalar_data.clone()
965        })];
966        let cl_res = vec![Attribute::DataArray(DataArray {
967            name: String::from("cell_scalars"),
968            ..scalar_data
969        })];
970        let out2 = Attributes {
971            point: pt_res,
972            cell: cl_res,
973        };
974        test!(attributes(in2, FileType::ASCII) => out2);
975    }
976    #[test]
977    fn dataset_simple_test() {
978        let in1 = "DATASET UNSTRUCTURED_GRID\nPOINTS 0 float\nCELLS 0 0\nCELL_TYPES 0\n";
979        let out1 = DataSet::inline(UnstructuredGridPiece {
980            points: Vec::<f32>::new().into(),
981            cells: Cells {
982                cell_verts: VertexNumbers::Legacy {
983                    num_cells: 0,
984                    vertices: vec![],
985                },
986                types: vec![],
987            },
988            data: Attributes::new(),
989        });
990        test!(dataset(in1, FileType::ASCII) => out1);
991    }
992    #[test]
993    fn dataset_test() {
994        let in1 = "DATASET UNSTRUCTURED_GRID\nPOINTS 3 float\n2 45 2 3 4 1 46 2 0\
995                   CELLS 0 0\nCELL_TYPES 0\n";
996        let out1 = DataSet::inline(UnstructuredGridPiece {
997            points: vec![2.0f32, 45., 2., 3., 4., 1., 46., 2., 0.].into(),
998            cells: Cells {
999                cell_verts: VertexNumbers::Legacy {
1000                    num_cells: 0,
1001                    vertices: vec![],
1002                },
1003                types: vec![],
1004            },
1005            data: Attributes::new(),
1006        });
1007        test!(dataset(in1, FileType::ASCII) => out1);
1008    }
1009}