1#![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
17named!(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
84pub struct VtkParser<BO: ByteOrder>(PhantomData<BO>);
88
89#[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 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 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 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 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 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 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 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 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 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 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 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 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 #[allow(unused_comparisons)] 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 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 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 byte_order: ByteOrderTag::new::<BO>(),
778 title: h.1,
779 data: d,
780 file_path: None,
781 })
782 ))
783 )
784 }
785}
786
787pub fn parse_ne(input: &[u8]) -> IResult<&[u8], Vtk> {
789 VtkParser::<NativeEndian>::vtk(input)
790}
791
792pub fn parse_le(input: &[u8]) -> IResult<&[u8], Vtk> {
794 VtkParser::<LittleEndian>::vtk(input)
795}
796
797pub 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 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 test!(cell_attributes("\n", FileType::ASCII) => Vec::new());
940 test!(point_attributes("", FileType::ASCII) => Vec::new());
942 test!(attributes("\n", FileType::ASCII) => Attributes::new());
944 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 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}