1use crate::algo::merge::Merge;
2use crate::attrib::{Attrib, AttribDict, AttribIndex, Attribute, AttributeValue};
3use crate::mesh::topology::*;
4use crate::mesh::{CellType, Mesh, PointCloud, PolyMesh, TetMesh, VertexPositions};
5use flatk::{
6 consts::{U10, U11, U12, U13, U14, U15, U16, U2, U3, U4, U5, U6, U7, U8, U9},
7 U,
8};
9
10use super::MeshExtractor;
11use super::Real;
12use super::{NORMAL_ATTRIB_NAME, UV_ATTRIB_NAME};
13
14pub use vtkio::Error as VtkError;
15pub use vtkio::*;
16
17pub use super::Error;
18
19const FACE_VERTEX_ATTRIBUTES_FIELD: &str = "face_vertex_attributes";
23
24fn special_field_attributes() -> &'static [&'static str] {
30 &[FACE_VERTEX_ATTRIBUTES_FIELD]
31}
32
33pub enum VTKPolyExportStyle {
40 PolyData,
42 UnstructuredGrid,
44}
45
46pub fn convert_mesh_to_vtk_format<T: Real>(mesh: &Mesh<T>) -> Result<model::Vtk, Error> {
47 let points: Vec<T> = mesh
48 .vertex_positions()
49 .iter()
50 .flat_map(|x| x.iter().cloned())
51 .collect();
52 let mut vertices = Vec::new();
53 for cell in mesh.cell_iter() {
54 vertices.push(cell.len() as u32);
55 for &vtx in cell.iter() {
56 vertices.push(vtx as u32);
57 }
58 }
59
60 let cell_types: Vec<_> = mesh
61 .cell_type_iter()
62 .map(|cell_type| match cell_type {
63 CellType::Tetrahedron => model::CellType::Tetra,
64 CellType::Triangle => model::CellType::Triangle,
65 })
66 .collect();
67
68 let point_attribs = mesh
69 .attrib_dict::<VertexIndex>()
70 .iter()
71 .filter_map(|(name, attrib)| mesh_to_vtk_named_attrib(name, attrib))
72 .collect();
73
74 let cell_attribs = mesh
75 .attrib_dict::<CellIndex>()
76 .iter()
77 .filter_map(|(name, attrib)| mesh_to_vtk_named_attrib(name, attrib))
78 .collect();
79
80 Ok(model::Vtk {
81 version: model::Version::new((0, 1)),
82 title: String::from("Unstructured Mesh"),
83 byte_order: model::ByteOrder::BigEndian,
84 file_path: None,
85 data: model::DataSet::inline(model::UnstructuredGridPiece {
86 points: points.into(),
87 cells: model::Cells {
88 cell_verts: model::VertexNumbers::Legacy {
89 num_cells: mesh.num_cells() as u32,
90 vertices,
91 },
92 types: cell_types,
93 },
94 data: model::Attributes {
95 point: point_attribs,
96 cell: cell_attribs,
97 },
98 }),
99 })
100}
101
102pub fn convert_polymesh_to_vtk_format<T: Real>(
103 mesh: &PolyMesh<T>,
104 style: VTKPolyExportStyle,
105) -> Result<model::Vtk, Error> {
106 let points: Vec<T> = mesh
107 .vertex_positions()
108 .iter()
109 .flat_map(|x| x.iter().cloned())
110 .collect();
111 let mut vertices = Vec::new();
112 for face in mesh.face_iter() {
113 vertices.push(face.len() as u32);
114 for &vtx in face.iter() {
115 vertices.push(vtx as u32);
116 }
117 }
118
119 let point_attribs = mesh
120 .attrib_dict::<VertexIndex>()
121 .iter()
122 .filter_map(|(name, attrib)| mesh_to_vtk_named_attrib(name, attrib))
123 .collect();
124
125 let face_attribs = mesh
126 .attrib_dict::<FaceIndex>()
127 .iter()
128 .filter_map(|(name, attrib)| mesh_to_vtk_named_attrib(name, attrib))
129 .chain(
130 mesh_to_vtk_named_field_attribs(
133 FACE_VERTEX_ATTRIBUTES_FIELD,
134 mesh.attrib_dict::<FaceVertexIndex>(),
135 ),
136 )
137 .collect();
138
139 Ok(model::Vtk {
140 version: model::Version::new((0, 1)),
141 title: String::from("Polygonal Mesh"),
142 byte_order: model::ByteOrder::BigEndian,
143 file_path: None,
144 data: match style {
145 VTKPolyExportStyle::UnstructuredGrid => {
146 model::DataSet::inline(model::UnstructuredGridPiece {
147 points: IOBuffer::new(points),
148 cells: model::Cells {
149 cell_verts: model::VertexNumbers::Legacy {
150 num_cells: mesh.num_faces() as u32,
151 vertices,
152 },
153 types: vec![model::CellType::Polygon; mesh.num_faces()],
154 },
155 data: model::Attributes {
156 point: point_attribs,
157 cell: face_attribs,
158 },
159 })
160 }
161 VTKPolyExportStyle::PolyData => model::DataSet::inline(model::PolyDataPiece {
162 points: IOBuffer::new(points),
163 polys: Some(model::VertexNumbers::Legacy {
164 num_cells: mesh.num_faces() as u32,
165 vertices,
166 }),
167 data: model::Attributes {
168 point: point_attribs,
169 cell: face_attribs,
170 },
171 ..Default::default()
172 }),
173 },
174 })
175}
176
177pub fn convert_tetmesh_to_vtk_format<T: Real>(tetmesh: &TetMesh<T>) -> Result<model::Vtk, Error> {
178 let points: Vec<T> = tetmesh
179 .vertex_positions()
180 .iter()
181 .flat_map(|x| x.iter().cloned())
182 .collect();
183 let mut vertices = Vec::new();
184 for cell in tetmesh.cell_iter() {
185 vertices.push(cell.len() as u32);
186 for &vtx in cell.iter() {
187 vertices.push(vtx as u32);
188 }
189 }
190
191 let point_attribs = tetmesh
192 .attrib_dict::<VertexIndex>()
193 .iter()
194 .filter_map(|(name, attrib)| mesh_to_vtk_named_attrib(name, attrib))
195 .collect();
196
197 let cell_attribs = tetmesh
198 .attrib_dict::<CellIndex>()
199 .iter()
200 .filter_map(|(name, attrib)| mesh_to_vtk_named_attrib(name, attrib))
201 .collect();
202
203 Ok(model::Vtk {
204 version: model::Version::new((0, 1)),
205 title: String::from("Tetrahedral Mesh"),
206 byte_order: model::ByteOrder::BigEndian,
207 file_path: None,
208 data: model::DataSet::inline(model::UnstructuredGridPiece {
209 points: points.into(),
210 cells: model::Cells {
211 cell_verts: model::VertexNumbers::Legacy {
212 num_cells: tetmesh.num_cells() as u32,
213 vertices,
214 },
215 types: vec![model::CellType::Tetra; tetmesh.num_cells()],
216 },
217 data: model::Attributes {
218 point: point_attribs,
219 cell: cell_attribs,
220 },
221 }),
222 })
223}
224
225pub fn convert_pointcloud_to_vtk_format<T: Real>(
226 ptcloud: &PointCloud<T>,
227 style: VTKPolyExportStyle,
228) -> Result<model::Vtk, Error> {
229 let num_verts = ptcloud.num_vertices() as u32;
230 let points: Vec<T> = ptcloud
231 .vertex_positions()
232 .iter()
233 .flat_map(|x| x.iter().cloned())
234 .collect();
235
236 let point_attribs = ptcloud
237 .attrib_dict::<VertexIndex>()
238 .iter()
239 .filter_map(|(name, attrib)| mesh_to_vtk_named_attrib(name, attrib))
240 .collect();
241
242 Ok(model::Vtk {
243 version: model::Version::new((0, 1)),
244 title: String::from("Point Cloud"),
245 byte_order: model::ByteOrder::BigEndian,
246 file_path: None,
247 data: match style {
248 VTKPolyExportStyle::PolyData => {
249 model::DataSet::inline(model::PolyDataPiece {
250 points: IOBuffer::new(points),
251 verts: Some(model::VertexNumbers::Legacy {
253 num_cells: 1,
254 vertices: std::iter::once(num_verts)
255 .chain(0..num_verts)
256 .collect::<Vec<_>>(),
257 }),
258 data: model::Attributes {
259 point: point_attribs,
260 cell: Vec::new(),
261 },
262 ..Default::default()
263 })
264 }
265 VTKPolyExportStyle::UnstructuredGrid => {
266 model::DataSet::inline(model::UnstructuredGridPiece {
267 points: IOBuffer::new(points),
268 cells: model::Cells {
269 cell_verts: model::VertexNumbers::Legacy {
270 num_cells: 1,
271 vertices: std::iter::once(num_verts)
272 .chain(0..num_verts)
273 .collect::<Vec<_>>(),
274 },
275 types: vec![model::CellType::Vertex; ptcloud.num_vertices()],
276 },
277 data: model::Attributes {
278 point: point_attribs,
279 cell: Vec::new(),
280 },
281 })
282 }
283 },
284 })
285}
286
287impl<T: Real> MeshExtractor<T> for model::Vtk {
289 fn extract_mesh(&self) -> Result<Mesh<T>, Error> {
293 let model::Vtk {
294 file_path, data, ..
295 } = &self;
296 match data {
297 model::DataSet::UnstructuredGrid { pieces, .. } => {
298 Ok(Mesh::merge_iter(pieces.iter().filter_map(|piece| {
299 let model::UnstructuredGridPiece {
300 points,
301 cells: model::Cells { cell_verts, types },
302 data,
303 } = piece
304 .load_piece_data(file_path.as_ref().map(AsRef::as_ref))
305 .ok()?;
306 let pt_coords: Vec<T> = points.cast_into()?;
308 let mut pts = Vec::with_capacity(pt_coords.len() / 3);
309 for coords in pt_coords.chunks_exact(3) {
310 pts.push([coords[0], coords[1], coords[2]]);
311 }
312
313 let num_cells = cell_verts.num_cells();
314 let (connectivity, offsets) = cell_verts.into_xml();
315
316 let mut orig_cell_idx = Vec::with_capacity(num_cells);
319
320 let mut begin = 0usize;
322 let mut indices = Vec::new();
323 let mut counts = Vec::new();
324 let mut cell_types = Vec::new();
325 for (c, &end) in offsets.iter().enumerate() {
326 let n = end as usize - begin;
327 let cell_type = match types[c] {
328 model::CellType::Triangle if n == 3 => CellType::Triangle,
329 model::CellType::Tetra if n == 4 => CellType::Tetrahedron,
330 _ => {
331 begin = end as usize;
333 continue;
334 }
335 };
336
337 if cell_types.is_empty() || *cell_types.last().unwrap() != cell_type {
338 cell_types.push(cell_type);
340 counts.push(1);
341 } else if let Some(last) = counts.last_mut() {
342 *last += 1;
343 } else {
344 return None;
346 }
347
348 orig_cell_idx.push(c);
349 for i in 0..n {
350 indices.push(connectivity[begin + i] as usize);
351 }
352 begin = end as usize;
353 }
354
355 let mut mesh =
356 Mesh::from_cells_counts_and_types(pts, indices, counts, cell_types);
357
358 if mesh.num_vertices() > 0 {
363 vtk_to_mesh_attrib::<_, VertexIndex>(data.point, &mut mesh, None);
365 }
366
367 if mesh.num_cells() > 0 {
368 vtk_to_mesh_attrib::<_, CellIndex>(
370 data.cell,
371 &mut mesh,
372 Some(orig_cell_idx.as_slice()),
373 );
374 }
375
376 Some(mesh)
377 })))
378 }
379 _ => Err(Error::UnsupportedDataFormat),
380 }
381 }
382 fn extract_polymesh(&self) -> Result<PolyMesh<T>, Error> {
386 let model::Vtk {
387 file_path, data, ..
388 } = &self;
389 match data {
390 model::DataSet::UnstructuredGrid { pieces, .. } => {
391 let mesh = PolyMesh::merge_iter(pieces.iter().filter_map(|piece| {
392 let model::UnstructuredGridPiece {
393 points,
394 cells: model::Cells { cell_verts, types },
395 data,
396 } = piece
397 .load_piece_data(file_path.as_ref().map(AsRef::as_ref))
398 .ok()?;
399 let pt_coords: Vec<T> = points.cast_into()?; let mut pts = Vec::with_capacity(pt_coords.len() / 3);
402 for coords in pt_coords.chunks_exact(3) {
403 pts.push([coords[0], coords[1], coords[2]]);
404 }
405
406 let mut count_non_polymesh_faces = 0;
409
410 let num_cells = cell_verts.num_cells();
411 let (connectivity, offsets) = cell_verts.into_xml();
413
414 let mut orig_cell_idx = Vec::with_capacity(num_cells);
416 let mut orig_cell_vtx_idx = Vec::with_capacity(connectivity.len());
417
418 let mut begin = 0usize;
419 let mut faces = Vec::new();
420 for c in 0..num_cells {
421 let end = offsets[c] as usize;
422 let n = end - begin;
423
424 let skip = match types[c] {
426 model::CellType::Line => n != 1,
427 model::CellType::Triangle => n != 3,
428 model::CellType::Quad => n != 4,
429 model::CellType::Polygon | model::CellType::PolyLine => false,
430 _ => true,
431 };
432 if skip {
433 count_non_polymesh_faces += 1;
434 begin = end;
435 continue;
436 }
437
438 if types[c] == model::CellType::PolyLine {
439 for i in begin..end - 1 {
441 orig_cell_idx.push(c);
442 orig_cell_vtx_idx.push(i - begin);
443 orig_cell_vtx_idx.push(i + 1 - begin);
444 faces.push(2);
445 faces.push(connectivity[i] as usize);
446 faces.push(connectivity[i + 1] as usize);
447 }
448 } else {
449 orig_cell_idx.push(c);
450 faces.push(n);
451 orig_cell_vtx_idx.extend(0..end - begin);
452 faces.extend(connectivity[begin..end].iter().map(|&i| i as usize));
453 }
454
455 begin = end;
456 }
457
458 if faces.is_empty() && count_non_polymesh_faces > 0 {
459 return None;
463 } let mut polymesh = PolyMesh::new(pts, &faces);
466
467 vtk_to_mesh_attrib::<_, VertexIndex>(data.point, &mut polymesh, None);
469
470 let remainder = vtk_to_mesh_attrib::<_, FaceIndex>(
472 data.cell,
473 &mut polymesh,
474 Some(orig_cell_idx.as_slice()),
475 );
476
477 vtk_field_to_mesh_attrib(
479 remainder,
480 &mut polymesh,
481 Some(orig_cell_vtx_idx.as_slice()),
482 );
483
484 Some(polymesh)
485 }));
486
487 if mesh.num_faces() == 0 {
492 Err(Error::MeshTypeMismatch)
493 } else {
494 Ok(mesh)
495 }
496 }
497 model::DataSet::PolyData { pieces, .. } => {
498 Ok(PolyMesh::merge_iter(pieces.iter().filter_map(|piece| {
499 let model::PolyDataPiece {
500 points,
501 lines,
502 polys,
503 strips,
504 data,
506 ..
507 } = piece
508 .load_piece_data(file_path.as_ref().map(AsRef::as_ref))
509 .ok()?;
510 let pt_coords: Vec<T> = points.cast_into()?; let mut pts = Vec::with_capacity(pt_coords.len() / 3);
513 for coords in pt_coords.chunks_exact(3) {
514 pts.push([coords[0], coords[1], coords[2]]);
515 }
516
517 let mut cell_idx_map = Vec::new();
519 let mut cell_vtx_idx_map = Vec::new();
520
521 let mut num_faces = 0;
522
523 let mut faces = Vec::new();
524
525 let mut append_topo = |topo: model::VertexNumbers| {
526 cell_idx_map.extend(num_faces..num_faces + topo.num_cells());
527 num_faces += topo.num_cells();
528 let (connectivity, offsets) = topo.into_xml();
529 let mut begin = 0;
530 for &offset in offsets.iter() {
531 let end = offset as usize;
532 cell_vtx_idx_map.extend(begin..end);
533 faces.push(end - begin);
534 faces.extend(connectivity[begin..end].iter().map(|&i| i as usize));
535 begin = end;
536 }
537 };
538
539 polys.map(&mut append_topo);
540 strips.map(&mut append_topo);
541
542 if let Some(topo) = lines {
543 let (connectivity, offsets) = topo.into_xml();
544 let mut begin = 0;
545 for (orig_face_idx, &offset) in offsets.iter().enumerate() {
546 for i in begin..offset as usize - 1 {
548 cell_idx_map.push(num_faces + orig_face_idx);
549 cell_vtx_idx_map.push(i - begin);
550 cell_vtx_idx_map.push(i + 1 - begin);
551 faces.push(2);
552 faces.push(connectivity[i] as usize);
553 faces.push(connectivity[i + 1] as usize);
554 }
555 begin = offset as usize;
556 }
557 }
558
559 let mut polymesh = PolyMesh::new(pts, &faces);
560
561 vtk_to_mesh_attrib::<_, VertexIndex>(data.point, &mut polymesh, None);
563
564 let remainder = vtk_to_mesh_attrib::<_, FaceIndex>(
566 data.cell,
567 &mut polymesh,
568 Some(cell_idx_map.as_slice()),
569 );
570
571 vtk_field_to_mesh_attrib(
573 remainder,
574 &mut polymesh,
575 Some(cell_vtx_idx_map.as_slice()),
576 );
577
578 Some(polymesh)
579 })))
580 }
581 _ => Err(Error::UnsupportedDataFormat),
582 }
583 }
584
585 fn extract_tetmesh(&self) -> Result<TetMesh<T>, Error> {
589 let model::Vtk {
590 file_path, data, ..
591 } = &self;
592 match data {
593 model::DataSet::UnstructuredGrid { pieces, .. } => {
594 Ok(TetMesh::merge_iter(pieces.iter().filter_map(|piece| {
595 let model::UnstructuredGridPiece {
596 points,
597 cells: model::Cells { cell_verts, types },
598 data,
599 } = piece
600 .load_piece_data(file_path.as_ref().map(AsRef::as_ref))
601 .ok()?;
602 let pt_coords: Vec<T> = points.cast_into()?;
604 let mut pts = Vec::with_capacity(pt_coords.len() / 3);
605 for coords in pt_coords.chunks_exact(3) {
606 pts.push([coords[0], coords[1], coords[2]]);
607 }
608
609 let num_cells = cell_verts.num_cells();
610 let (connectivity, offsets) = cell_verts.into_xml();
611
612 let mut orig_cell_idx = Vec::with_capacity(num_cells);
614
615 let mut begin = 0usize;
617 let mut indices = Vec::new();
618 for (c, &end) in offsets.iter().enumerate() {
619 let n = end as usize - begin;
620
621 if n != 4 || types[c] != model::CellType::Tetra {
622 begin = end as usize;
624 continue;
625 }
626
627 orig_cell_idx.push(c);
628 indices.push([
629 connectivity[begin] as usize,
630 connectivity[begin + 1] as usize,
631 connectivity[begin + 2] as usize,
632 connectivity[begin + 3] as usize,
633 ]);
634 begin = end as usize;
635 }
636
637 let mut tetmesh = TetMesh::new(pts, indices);
638
639 if tetmesh.num_vertices() > 0 {
644 vtk_to_mesh_attrib::<_, VertexIndex>(data.point, &mut tetmesh, None);
646 }
647
648 if tetmesh.num_cells() > 0 {
649 vtk_to_mesh_attrib::<_, CellIndex>(
651 data.cell,
652 &mut tetmesh,
653 Some(orig_cell_idx.as_slice()),
654 );
655 }
656
657 Some(tetmesh)
658 })))
659 }
660 _ => Err(Error::UnsupportedDataFormat),
661 }
662 }
663
664 fn extract_pointcloud(&self) -> Result<PointCloud<T>, Error> {
668 let model::Vtk {
669 file_path, data, ..
670 } = &self;
671 let mut pts = Vec::new();
672 let mut vertices = Vec::new();
673 match data {
674 model::DataSet::UnstructuredGrid { pieces, .. } => {
675 let ptcloud = PointCloud::merge_iter(pieces.iter().filter_map(|piece| {
676 let model::UnstructuredGridPiece {
677 points,
678 cells: model::Cells { cell_verts, types },
679 data,
680 } = piece
681 .load_piece_data(file_path.as_ref().map(AsRef::as_ref))
682 .ok()?;
683
684 pts.clear();
685 vertices.clear();
686
687 let pt_coords: Vec<T> = points.cast_into()?;
689 pts.reserve(pt_coords.len() / 3);
690 for coords in pt_coords.chunks_exact(3) {
691 pts.push([coords[0], coords[1], coords[2]]);
692 }
693
694 let mut count_non_vertex_cells = 0;
697
698 let (num_cells, cell_vertices) = cell_verts.into_legacy();
699
700 let mut i = 0usize;
701 for c in 0..num_cells {
702 if i >= cell_vertices.len() {
703 break;
704 }
705
706 let n = cell_vertices[i] as usize;
707 if types[c as usize] == model::CellType::Vertex {
709 if n != 1 {
710 i += n + 1;
711 count_non_vertex_cells += 1;
712 continue;
713 }
714 } else if types[c as usize] != model::CellType::PolyVertex {
715 i += n + 1;
716 count_non_vertex_cells += 1;
717 continue;
718 }
719
720 i += 1; for _ in 0..=n {
723 vertices.push(cell_vertices[i] as usize);
724 i += 1;
725 }
726 }
727
728 if vertices.is_empty() && count_non_vertex_cells > 0 {
729 return None;
733 } let referenced_points: Vec<_> = vertices.iter().map(|&vtx| pts[vtx]).collect();
735 let mut pointcloud = PointCloud::new(referenced_points);
736
737 vtk_to_mesh_attrib::<_, VertexIndex>(data.point, &mut pointcloud, None);
739
740 Some(pointcloud)
741 }));
742
743 if ptcloud.num_vertices() == 0 {
748 Err(Error::MeshTypeMismatch)
749 } else {
750 Ok(ptcloud)
751 }
752 }
753 model::DataSet::PolyData { pieces, .. } => {
754 Ok(PointCloud::merge_iter(pieces.iter().filter_map(|piece| {
755 let model::PolyDataPiece {
756 points,
757 verts,
758 data,
759 ..
760 } = piece.load_piece_data(None).ok()?;
761 pts.clear();
762 vertices.clear();
763
764 let pt_coords: Vec<T> = points.cast_into()?;
766 pts.reserve(pt_coords.len() / 3);
767 for coords in pt_coords.chunks_exact(3) {
768 pts.push([coords[0], coords[1], coords[2]]);
769 }
770
771 let mut ptcloud = if let Some(topo) = verts {
773 let (_, cell_vertices) = topo.into_legacy();
774 vertices.extend(cell_vertices.into_iter().skip(1).map(|x| x as usize));
775 let referenced_points: Vec<_> =
776 vertices.iter().map(|&vtx| pts[vtx]).collect();
777 PointCloud::new(referenced_points)
778 } else {
779 PointCloud::new(pts.clone())
780 };
781
782 vtk_to_mesh_attrib::<_, VertexIndex>(data.point, &mut ptcloud, None);
784
785 Some(ptcloud)
786 })))
787 }
788 _ => Err(Error::UnsupportedDataFormat),
789 }
790 }
791}
792
793fn flatten2<T: Clone>(vec: Vec<[T; 2]>) -> Vec<T> {
794 vec.iter().flat_map(|x| x.iter().cloned()).collect()
795}
796fn flatten3<T: Clone>(vec: Vec<[T; 3]>) -> Vec<T> {
797 vec.iter().flat_map(|x| x.iter().cloned()).collect()
798}
799fn flatten4<T: Clone>(vec: Vec<[T; 4]>) -> Vec<T> {
800 vec.iter().flat_map(|x| x.iter().cloned()).collect()
801}
802fn flatten33<T: Clone>(vec: Vec<[[T; 3]; 3]>) -> Vec<T> {
803 vec.iter()
804 .flat_map(|x| x.iter().flat_map(|y| y.iter().cloned()))
805 .collect()
806}
807
808fn into_vtk_attrib_uv<I>(name: &str, attrib: &Attribute<I>) -> Option<model::Attribute> {
810 let mut maybe_iobuf = attrib
812 .direct_clone_into_vec::<[f32; 2]>()
813 .map(|y| IOBuffer::from(flatten2(y)));
814 if maybe_iobuf.is_err() {
815 maybe_iobuf = attrib
817 .direct_clone_into_vec::<[f64; 2]>()
818 .map(|y| IOBuffer::from(flatten2(y)));
819 }
820
821 if let Ok(data) = maybe_iobuf {
822 return Some(model::Attribute::tcoords(name, 2).with_data(data));
823 }
824
825 maybe_iobuf = attrib
827 .direct_clone_into_vec::<[f32; 3]>()
828 .map(|y| flatten3(y).into());
829 if maybe_iobuf.is_err() {
830 maybe_iobuf = attrib
832 .direct_clone_into_vec::<[f64; 3]>()
833 .map(|y| flatten3(y).into());
834 }
835
836 if let Ok(data) = maybe_iobuf {
837 return Some(model::Attribute::tcoords(name, 3).with_data(data));
838 }
839
840 None
841}
842
843macro_rules! try_interpret_attrib {
844 (@direct $attrib:ident, $t:ident, $f:expr) => {
845 $attrib.direct_clone_into_vec::<$t>().map($f)
846 };
847 (@build $attrib:ident, ($t:ident $($ts:ident)*), $f:expr) => {
848 $attrib.direct_clone_into_vec::<$t>().map($f)
849 $(
850 .or_else(|_| try_interpret_attrib!(@direct $attrib, $ts, $f))
851 )*
852 };
853 (@direct $attrib:ident, $n:expr, $t:ident, $f:expr) => {
854 $attrib.direct_clone_into_vec::<[$t; $n]>().map($f)
855 };
856 (@build $attrib:ident, $n:expr, ($t:ident $($ts:ident)*), $f:expr) => {
857 $attrib.direct_clone_into_vec::<[$t; $n]>().map($f)
858 $(
859 .or_else(|_| try_interpret_attrib!(@direct $attrib, $n, $ts, $f))
860 )*
861 };
862 (@direct $attrib:ident, $n:expr, $m:expr, $t:ident, $f:expr) => {
863 $attrib.direct_clone_into_vec::<[[$t; $n]; $m]>().map($f)
864 };
865 (@build $attrib:ident, $n:expr, $m:expr, ($t:ident $($ts:ident)*), $f:expr) => {
866 $attrib.direct_clone_into_vec::<[[$t; $n]; $m]>().map($f)
867 $(
868 .or_else(|_| try_interpret_attrib!(@direct $attrib, $n, $m, $ts, $f))
869 )*
870 };
871 ($attrib:ident, $f:expr) => {
872 {
873 try_interpret_attrib!(@build $attrib, (u8 i8 u16 i16 u32 i32 u64 i64 f32 f64), $f)
874 }
875 };
876 ($attrib:ident, $n:expr, $f:expr) => {
877 {
878 try_interpret_attrib!(@build $attrib, $n, (u8 i8 u16 i16 u32 i32 u64 i64 f32 f64), $f)
879 }
880 };
881 ($attrib:ident, $n:expr, $m:expr, $f:expr) => {
882 {
883 try_interpret_attrib!(@build $attrib, $n, $m, (u8 i8 u16 i16 u32 i32 u64 i64 f32 f64), $f)
884 }
885 }
886}
887
888macro_rules! try_interpret_generic_attrib {
889 ($attrib:ident, $name:ident $(,$n:expr)*) => {
890 {
891 $(
892 if let Ok(data) = try_interpret_attrib!($attrib, $n, |x| IOBuffer::from(
893 x.iter().flat_map(|x| x.iter().cloned()).collect::<Vec<_>>()
894 )) {
895 return Some(model::Attribute::generic($name, $n).with_data(data));
896 }
897 )*
898 }
899 }
900}
901
902fn mesh_to_vtk_attrib_impl<I>(name: &str, attrib: &Attribute<I>) -> Option<model::Attribute> {
903 if let Ok(data) = try_interpret_attrib!(attrib, IOBuffer::from) {
905 return Some(model::Attribute::scalars(name, 1).with_data(data));
906 }
907
908 if let Ok(data) = try_interpret_attrib!(attrib, 2, |x| IOBuffer::from(flatten2(x))) {
910 return Some(model::Attribute::scalars(name, 2).with_data(data));
911 }
912
913 if let Ok(data) = try_interpret_attrib!(attrib, 3, |x| IOBuffer::from(flatten3(x))) {
915 return Some(model::Attribute::vectors(name).with_data(data));
916 }
917
918 if let Ok(data) = try_interpret_attrib!(attrib, 4, |x| IOBuffer::from(flatten4(x))) {
920 return Some(model::Attribute::scalars(name, 4).with_data(data));
921 }
922
923 if let Ok(data) = try_interpret_attrib!(attrib, 3, 3, |x| IOBuffer::from(flatten33(x))) {
925 return Some(model::Attribute::tensors(name).with_data(data));
926 }
927
928 try_interpret_generic_attrib!(attrib, name, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
930 None
931}
932
933fn mesh_to_vtk_named_attrib<I>(name: &str, attrib: &Attribute<I>) -> Option<model::Attribute> {
934 if name == UV_ATTRIB_NAME {
936 let attrib = into_vtk_attrib_uv(name, attrib);
937 if attrib.is_some() {
938 return attrib;
939 }
940 } else if name == NORMAL_ATTRIB_NAME {
941 let mut maybe_iobuf: Result<IOBuffer, _> = attrib
942 .direct_clone_into_vec::<[f32; 3]>()
943 .map(|y| flatten3(y).into());
944 if maybe_iobuf.is_err() {
945 maybe_iobuf = attrib
947 .direct_clone_into_vec::<[f64; 3]>()
948 .map(|y| flatten3(y).into());
949 }
950
951 if let Ok(data) = maybe_iobuf {
952 return Some(model::Attribute::normals(name).with_data(data));
953 }
954 }
955
956 mesh_to_vtk_attrib_impl(name, attrib)
958}
959
960fn mesh_to_vtk_named_field_attribs<I>(
964 field_data_name: &str,
965 attrib_dict: &AttribDict<I>,
966) -> Option<model::Attribute> {
967 let data_array: Vec<_> = attrib_dict
968 .iter()
969 .filter_map(|(name, attrib)| {
970 if let Ok(data) = try_interpret_attrib!(attrib, IOBuffer::from) {
972 return Some(model::FieldArray::new(name, 1).with_data(data));
973 }
974
975 if let Ok(data) = try_interpret_attrib!(attrib, 2, |x| IOBuffer::from(flatten2(x))) {
977 return Some(model::FieldArray::new(name, 2).with_data(data));
978 }
979
980 if let Ok(data) = try_interpret_attrib!(attrib, 3, |x| IOBuffer::from(flatten3(x))) {
982 return Some(model::FieldArray::new(name, 3).with_data(data));
983 }
984
985 None
986 })
987 .collect();
988
989 if !data_array.is_empty() {
990 Some(model::Attribute::field(field_data_name).with_field_data(data_array))
991 } else {
992 None
993 }
994}
995
996fn insert_2d_array_attrib<'a, T, M, I>(
997 buf: &[T],
998 name: &'a str,
999 mesh: &mut M,
1000 remap: Option<&[usize]>,
1001) -> Result<(), Error>
1002where
1003 T: AttributeValue + Copy + Default,
1004 I: AttribIndex<M>,
1005 M: Attrib,
1006{
1007 let n = 9;
1008 let mut vecs = Vec::with_capacity(buf.len() / n);
1009 let mut count_comp = 0;
1010 let mut cur = [[T::default(); 3]; 3];
1011 let mut push_val = |val| {
1012 cur[count_comp / 3][count_comp % 3] = val; count_comp += 1;
1014 if count_comp == n {
1015 vecs.push(cur);
1016 count_comp = 0;
1017 }
1018 };
1019 if let Some(remap) = remap {
1020 remap.iter().for_each(|&i| push_val(buf[i]));
1021 } else {
1022 buf.iter().cloned().for_each(push_val);
1023 }
1024 mesh.insert_attrib_data::<_, I>(name, vecs)?;
1025 Ok(())
1026}
1027
1028fn insert_array_attrib<'a, T, M, I>(
1029 buf: &[T],
1030 name: &'a str,
1031 mesh: &mut M,
1032 remap: Option<&[usize]>,
1033) -> Result<(), Error>
1034where
1035 T: AttributeValue + Default,
1036 I: AttribIndex<M>,
1037 M: Attrib,
1038{
1039 let remapped_buf = if let Some(remap) = remap {
1040 remap.iter().map(|&i| buf[i].clone()).collect()
1041 } else {
1042 buf.to_vec()
1043 };
1044 mesh.insert_attrib_data::<_, I>(name, remapped_buf)?;
1045 Ok(())
1046}
1047
1048fn insert_array_attrib_n<'a, T, M, I: AttribIndex<M>, N>(
1049 buf: &[T],
1050 name: &'a str,
1051 mesh: &mut M,
1052 remap: Option<&[usize]>,
1053) -> Result<(), Error>
1054where
1055 T: bytemuck::Pod + AttributeValue + Default,
1056 M: Attrib,
1057 N: flatk::Unsigned + Default + flatk::Array<T>,
1058 <N as flatk::Array<T>>::Array: Default + PartialEq + std::fmt::Debug + Send + Sync,
1059{
1060 let remapped_buf = if let Some(remap) = remap {
1061 remap
1062 .iter()
1063 .flat_map(|&i| (0..N::to_usize()).map(move |j| buf[N::to_usize() * i + j]))
1064 .collect()
1065 } else {
1066 buf.to_vec()
1067 };
1068 let chunked = flatk::UniChunked::<_, U<N>>::from_flat(remapped_buf);
1069 mesh.insert_attrib_data::<_, I>(name, chunked.into_arrays())?;
1070 Ok(())
1071}
1072
1073#[allow(clippy::cognitive_complexity)]
1079fn vtk_to_mesh_attrib<M, I>(
1080 attribs: Vec<model::Attribute>,
1081 mesh: &mut M,
1082 orig_map: Option<&[usize]>,
1083) -> Vec<model::Attribute>
1084where
1085 M: Attrib,
1086 I: AttribIndex<M>,
1087{
1088 let mut remainder = Vec::with_capacity(attribs.len());
1090
1091 for attrib in attribs {
1092 match attrib {
1093 model::Attribute::DataArray(model::DataArray { name, elem, data }) => {
1094 let name = name.as_str();
1095 match elem {
1096 model::ElementType::Scalars { num_comp: dim, .. } | model::ElementType::TCoords(dim) => {
1097 match dim {
1098 1 => match_buf!( &data, v => insert_array_attrib::<_,M,I>(v, name, mesh, orig_map) ),
1101 2 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U2>(v, name, mesh, orig_map) ),
1102 3 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U3>(v, name, mesh, orig_map) ),
1103 4 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U4>(v, name, mesh, orig_map) ),
1104 _ => continue,
1107 }
1108 }
1109 model::ElementType::Vectors | model::ElementType::Normals => {
1110 match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U3>(v, name, mesh, orig_map) )
1111 }
1112 model::ElementType::Tensors => {
1113 match_buf!( &data, v => insert_2d_array_attrib::<_,M,I>(v, name, mesh, orig_map) )
1114 }
1115 model::ElementType::Generic(dim) => {
1116 match dim {
1117 1 => match_buf!( &data, v => insert_array_attrib::<_,M,I>(v, name, mesh, orig_map) ),
1118 2 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U2>(v, name, mesh, orig_map) ),
1119 3 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U3>(v, name, mesh, orig_map) ),
1120 4 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U4>(v, name, mesh, orig_map) ),
1121 5 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U5>(v, name, mesh, orig_map) ),
1122 6 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U6>(v, name, mesh, orig_map) ),
1123 7 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U7>(v, name, mesh, orig_map) ),
1124 8 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U8>(v, name, mesh, orig_map) ),
1125 9 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U9>(v, name, mesh, orig_map) ),
1126 10 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U10>(v, name, mesh, orig_map) ),
1127 11 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U11>(v, name, mesh, orig_map) ),
1128 12 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U12>(v, name, mesh, orig_map) ),
1129 13 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U13>(v, name, mesh, orig_map) ),
1130 14 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U14>(v, name, mesh, orig_map) ),
1131 15 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U15>(v, name, mesh, orig_map) ),
1132 16 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U16>(v, name, mesh, orig_map) ),
1133 _ => continue,
1134 }
1135 }
1136 _ => continue, }
1138 }
1139 model::Attribute::Field { data_array, name } => {
1140 if special_field_attributes().contains(&name.as_str()) {
1141 remainder.push(model::Attribute::Field { name, data_array });
1142 continue;
1143 }
1144 for model::FieldArray {
1145 name,
1146 elem,
1147 data,
1148 } in data_array
1149 {
1150 let name = name.as_str();
1151 match elem {
1153 1 => match_buf!( &data, v => insert_array_attrib::<_,M,I>(v, name, mesh, orig_map) ),
1156 2 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U2>(v, name, mesh, orig_map) ),
1157 3 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U3>(v, name, mesh, orig_map) ),
1158 4 => match_buf!( &data, v => insert_array_attrib_n::<_,M,I,U4>(v, name, mesh, orig_map) ),
1159 _ => continue,
1160 }
1161 .unwrap_or_else(|err| eprintln!("WARNING: Field attribute transfer error: {}", err));
1162 }
1163 continue;
1164 }
1165 }
1166 .unwrap_or_else(|err| {
1169 #[cfg(feature = "unstable")]
1170 {
1171 eprintln!("WARNING: Attribute transfer error at {}: {}", std::intrinsics::type_name::<I>(), err)
1172 }
1173 #[cfg(not(feature = "unstable"))]
1174 {
1175 eprintln!("WARNING: Attribute transfer error: {}", err)
1176 }
1177 })
1178 }
1179 remainder
1180}
1181
1182#[allow(clippy::cognitive_complexity)]
1184fn vtk_field_to_mesh_attrib<M>(
1185 attribs: Vec<model::Attribute>,
1186 mesh: &mut M,
1187 orig_map: Option<&[usize]>,
1188) where
1189 M: Attrib + FaceVertex,
1190 FaceVertexIndex: AttribIndex<M>,
1191{
1192 for attrib in attribs {
1193 if !special_field_attributes().contains(&attrib.name()) {
1194 continue;
1195 }
1196 if let model::Attribute::Field { data_array, .. } = attrib {
1197 for model::FieldArray { name, elem, data } in data_array {
1198 let name = name.as_str();
1199 match elem {
1200 1 => match_buf!( &data, v => insert_array_attrib::<_, _, FaceVertexIndex>(v, name, mesh, orig_map) ),
1203 2 => match_buf!( &data, v => insert_array_attrib_n::<_, _, FaceVertexIndex,U2>(v, name, mesh, orig_map) ),
1204 3 => match_buf!( &data, v => insert_array_attrib_n::<_, _, FaceVertexIndex,U3>(v, name, mesh, orig_map) ),
1205 4 => match_buf!( &data, v => insert_array_attrib_n::<_, _, FaceVertexIndex,U4>(v, name, mesh, orig_map) ),
1206 _ => continue,
1207 }
1208 .unwrap_or_else(|err| eprintln!("WARNING: Face Vertex Attribute transfer error for \"{}\": {}", name, err))
1209 }
1210 } }
1212}
1213
1214#[cfg(test)]
1215mod tests {
1216 use super::*;
1217
1218 #[test]
1219 fn basic_test() {
1220 let vtk = model::Vtk {
1221 version: model::Version::new((0, 1)),
1222 title: String::from("Tetrahedral Mesh"),
1223 byte_order: model::ByteOrder::BigEndian,
1224 file_path: None,
1225 data: model::DataSet::inline(model::UnstructuredGridPiece {
1226 points: vec![
1227 2., 1., 0., 0., 0., 1., 0., 1., 1., 1., 1., 1., 2., 1., 1., 0., 1., 2., 2., 1.,
1228 2., 1., 1., 4., 2., 1., 4., 1., 1., 5., 2., 1., 5.,
1229 ]
1230 .into(),
1231 cells: model::Cells {
1232 cell_verts: model::VertexNumbers::Legacy {
1233 num_cells: 3,
1234 vertices: vec![4, 1, 3, 2, 5, 4, 0, 4, 3, 6, 4, 9, 10, 8, 7],
1235 },
1236 types: vec![model::CellType::Tetra; 3],
1237 },
1238 data: model::Attributes {
1239 point: vec![
1240 model::Attribute::scalars("scalars", 1).with_data(vec![
1241 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0f32,
1242 ]),
1243 model::Attribute::vectors("vectors").with_data(vec![
1244 1., 0., 0., 1., 1., 0., 0., 2., 0., 1., 0., 0., 1., 1., 0., 0., 2., 0.,
1245 1., 0., 0., 1., 1., 0., 0., 2., 0., 0., 0., 1., 0., 0., 1.,
1246 ]),
1247 model::Attribute::tensors("tensors").with_data(vec![
1248 1., 0., 0., 1., 1., 0., 0., 2., 0., 1., 0., 0., 1., 1., 0., 0., 2., 0.,
1249 1., 0., 0., 1., 1., 0., 0., 2., 0., 0., 0., 1., 0., 0., 1., 1., 0., 0.,
1250 1., 1., 0., 0., 2., 0., 1., 0., 0., 1., 1., 0., 0., 2., 0., 1., 0., 0.,
1251 1., 1., 0., 0., 2., 0., 0., 0., 1., 0., 0., 1., 1., 0., 0., 1., 1., 0.,
1252 0., 2., 0., 1., 0., 0., 1., 1., 0., 0., 2., 0., 1., 0., 0., 1., 1., 0.,
1253 0., 2., 0., 0., 0., 1., 0., 0., 1.,
1254 ]),
1255 ],
1256 cell: vec![],
1257 },
1258 }),
1259 };
1260
1261 let vtktetmesh = vtk.extract_tetmesh().unwrap();
1262
1263 let pts = vec![
1264 [2., 1., 0.],
1265 [0., 0., 1.],
1266 [0., 1., 1.],
1267 [1., 1., 1.],
1268 [2., 1., 1.],
1269 [0., 1., 2.],
1270 [2., 1., 2.],
1271 [1., 1., 4.],
1272 [2., 1., 4.],
1273 [1., 1., 5.],
1274 [2., 1., 5.],
1275 ];
1276 let indices = vec![[1, 3, 2, 5], [0, 4, 3, 6], [9, 10, 8, 7]];
1277 let mut tetmesh = TetMesh::new(pts, indices);
1278
1279 let scalars = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0f32];
1280 let vectors = vec![
1281 [1., 0., 0.],
1282 [1., 1., 0.],
1283 [0., 2., 0.],
1284 [1., 0., 0.],
1285 [1., 1., 0.],
1286 [0., 2., 0.],
1287 [1., 0., 0.],
1288 [1., 1., 0.],
1289 [0., 2., 0.],
1290 [0., 0., 1.],
1291 [0., 0., 1.],
1292 ];
1293 let tensors = vec![
1294 [[1., 0., 0.], [1., 1., 0.], [0., 2., 0.]],
1295 [[1., 0., 0.], [1., 1., 0.], [0., 2., 0.]],
1296 [[1., 0., 0.], [1., 1., 0.], [0., 2., 0.]],
1297 [[0., 0., 1.], [0., 0., 1.], [1., 0., 0.]],
1298 [[1., 1., 0.], [0., 2., 0.], [1., 0., 0.]],
1299 [[1., 1., 0.], [0., 2., 0.], [1., 0., 0.]],
1300 [[1., 1., 0.], [0., 2., 0.], [0., 0., 1.]],
1301 [[0., 0., 1.], [1., 0., 0.], [1., 1., 0.]],
1302 [[0., 2., 0.], [1., 0., 0.], [1., 1., 0.]],
1303 [[0., 2., 0.], [1., 0., 0.], [1., 1., 0.]],
1304 [[0., 2., 0.], [0., 0., 1.], [0., 0., 1.]],
1305 ];
1306
1307 tetmesh
1308 .insert_attrib_data::<_, VertexIndex>("scalars", scalars)
1309 .ok()
1310 .unwrap();
1311
1312 tetmesh
1313 .insert_attrib_data::<_, VertexIndex>("tensors", tensors)
1314 .ok()
1315 .unwrap();
1316 tetmesh
1317 .insert_attrib_data::<_, VertexIndex>("vectors", vectors)
1318 .ok()
1319 .unwrap();
1320
1321 assert_eq!(vtktetmesh, tetmesh);
1323
1324 let tetmeshvtk = convert_tetmesh_to_vtk_format(&tetmesh).unwrap();
1326 let vtktetmesh = tetmeshvtk.extract_tetmesh().unwrap();
1327 assert_eq!(vtktetmesh, tetmesh);
1328 }
1329
1330 fn vtk_polymesh_example_data() -> (IOBuffer, model::VertexNumbers, model::Attributes) {
1331 let (buf, mut attrib) = vtk_pointcloud_example_data();
1332 let cell_verts = model::VertexNumbers::Legacy {
1333 num_cells: 3,
1334 vertices: vec![4, 1, 3, 2, 5, 4, 0, 4, 3, 6, 4, 9, 10, 8, 7],
1335 };
1336 attrib.cell = vec![model::Attribute::scalars("scalars", 1).with_data(vec![0.0, 1.0, 2.0])];
1337
1338 (buf, cell_verts, attrib)
1339 }
1340 fn vtk_polymesh_example_mixed_polydata() -> (
1341 IOBuffer,
1342 model::VertexNumbers,
1343 model::VertexNumbers,
1344 model::Attributes,
1345 ) {
1346 let (buf, mut attrib) = vtk_pointcloud_example_data();
1347 let polys = model::VertexNumbers::Legacy {
1348 num_cells: 2,
1349 vertices: vec![4, 1, 3, 2, 5, 4, 0, 4, 3, 6],
1350 };
1351 let lines = model::VertexNumbers::Legacy {
1352 num_cells: 1,
1353 vertices: vec![4, 9, 10, 8, 7],
1354 };
1355 attrib.cell =
1356 vec![model::Attribute::scalars("scalars", 1).with_data(vec![0.0, 1.0, 2.0, 2.0, 2.0])];
1357
1358 (buf, polys, lines, attrib)
1359 }
1360
1361 fn polymesh_example() -> PolyMesh<f64> {
1364 let pts = vec![
1365 [2., 1., 0.],
1366 [0., 0., 1.],
1367 [0., 1., 1.],
1368 [1., 1., 1.],
1369 [2., 1., 1.],
1370 [0., 1., 2.],
1371 [2., 1., 2.],
1372 [1., 1., 4.],
1373 [2., 1., 4.],
1374 [1., 1., 5.],
1375 [2., 1., 5.],
1376 ];
1377 let faces: Vec<usize> = vec![4, 1, 3, 2, 5, 4, 0, 4, 3, 6, 4, 9, 10, 8, 7];
1378 let mut polymesh = PolyMesh::new(pts, &faces);
1379
1380 let face_scalars = vec![0.0, 1.0, 2.0];
1381 let scalars = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0f32];
1382 let vectors = vec![
1383 [1., 0., 0.],
1384 [1., 1., 0.],
1385 [0., 2., 0.],
1386 [1., 0., 0.],
1387 [1., 1., 0.],
1388 [0., 2., 0.],
1389 [1., 0., 0.],
1390 [1., 1., 0.],
1391 [0., 2., 0.],
1392 [0., 0., 1.],
1393 [0., 0., 1.],
1394 ];
1395 let tensors = vec![
1396 [[1., 0., 0.], [1., 1., 0.], [0., 2., 0.]],
1397 [[1., 0., 0.], [1., 1., 0.], [0., 2., 0.]],
1398 [[1., 0., 0.], [1., 1., 0.], [0., 2., 0.]],
1399 [[0., 0., 1.], [0., 0., 1.], [1., 0., 0.]],
1400 [[1., 1., 0.], [0., 2., 0.], [1., 0., 0.]],
1401 [[1., 1., 0.], [0., 2., 0.], [1., 0., 0.]],
1402 [[1., 1., 0.], [0., 2., 0.], [0., 0., 1.]],
1403 [[0., 0., 1.], [1., 0., 0.], [1., 1., 0.]],
1404 [[0., 2., 0.], [1., 0., 0.], [1., 1., 0.]],
1405 [[0., 2., 0.], [1., 0., 0.], [1., 1., 0.]],
1406 [[0., 2., 0.], [0., 0., 1.], [0., 0., 1.]],
1407 ];
1408
1409 polymesh
1410 .insert_attrib_data::<_, FaceIndex>("scalars", face_scalars)
1411 .unwrap();
1412
1413 polymesh
1414 .insert_attrib_data::<_, VertexIndex>("scalars", scalars)
1415 .unwrap();
1416
1417 polymesh
1418 .insert_attrib_data::<_, VertexIndex>("tensors", tensors)
1419 .unwrap();
1420
1421 polymesh
1422 .insert_attrib_data::<_, VertexIndex>("vectors", vectors)
1423 .unwrap();
1424
1425 polymesh
1426 }
1427
1428 #[test]
1429 fn unstructured_data_polymesh_test() {
1430 let (points, cell_verts, data) = vtk_polymesh_example_data();
1431 let vtk = model::Vtk {
1432 version: model::Version::new((0, 1)),
1433 title: String::from("Polygonal Mesh"),
1434 byte_order: model::ByteOrder::BigEndian,
1435 file_path: None,
1436 data: model::DataSet::inline(model::UnstructuredGridPiece {
1437 points,
1438 cells: model::Cells {
1439 cell_verts: cell_verts.clone(),
1440 types: vec![model::CellType::Polygon; cell_verts.num_cells() as usize],
1441 },
1442 data,
1443 }),
1444 };
1445
1446 let vtkpolymesh = vtk.extract_polymesh().unwrap();
1447 let polymesh = polymesh_example();
1448
1449 assert_eq!(vtkpolymesh, polymesh);
1451
1452 let polymeshvtk =
1454 convert_polymesh_to_vtk_format(&polymesh, VTKPolyExportStyle::UnstructuredGrid)
1455 .unwrap();
1456 let vtkpolymesh = polymeshvtk.extract_polymesh().unwrap();
1457 assert_eq!(vtkpolymesh, polymesh);
1458 }
1459
1460 fn polymesh_example_with_polyline() -> PolyMesh<f64> {
1463 let pts = vec![
1464 [2., 1., 0.],
1465 [0., 0., 1.],
1466 [0., 1., 1.],
1467 [1., 1., 1.],
1468 [2., 1., 1.],
1469 [0., 1., 2.],
1470 [2., 1., 2.],
1471 [1., 1., 4.],
1472 [2., 1., 4.],
1473 [1., 1., 5.],
1474 [2., 1., 5.],
1475 ];
1476 let faces: Vec<usize> = vec![4, 1, 3, 2, 5, 4, 0, 4, 3, 6, 2, 9, 10, 2, 10, 8, 2, 8, 7];
1477 let mut polymesh = PolyMesh::new(pts, &faces);
1478
1479 let face_scalars = vec![0.0, 1.0, 2.0, 2.0, 2.0];
1480 let scalars = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0f32];
1481 let vectors = vec![
1482 [1., 0., 0.],
1483 [1., 1., 0.],
1484 [0., 2., 0.],
1485 [1., 0., 0.],
1486 [1., 1., 0.],
1487 [0., 2., 0.],
1488 [1., 0., 0.],
1489 [1., 1., 0.],
1490 [0., 2., 0.],
1491 [0., 0., 1.],
1492 [0., 0., 1.],
1493 ];
1494 let tensors = vec![
1495 [[1., 0., 0.], [1., 1., 0.], [0., 2., 0.]],
1496 [[1., 0., 0.], [1., 1., 0.], [0., 2., 0.]],
1497 [[1., 0., 0.], [1., 1., 0.], [0., 2., 0.]],
1498 [[0., 0., 1.], [0., 0., 1.], [1., 0., 0.]],
1499 [[1., 1., 0.], [0., 2., 0.], [1., 0., 0.]],
1500 [[1., 1., 0.], [0., 2., 0.], [1., 0., 0.]],
1501 [[1., 1., 0.], [0., 2., 0.], [0., 0., 1.]],
1502 [[0., 0., 1.], [1., 0., 0.], [1., 1., 0.]],
1503 [[0., 2., 0.], [1., 0., 0.], [1., 1., 0.]],
1504 [[0., 2., 0.], [1., 0., 0.], [1., 1., 0.]],
1505 [[0., 2., 0.], [0., 0., 1.], [0., 0., 1.]],
1506 ];
1507
1508 polymesh
1509 .insert_attrib_data::<_, FaceIndex>("scalars", face_scalars)
1510 .unwrap();
1511
1512 polymesh
1513 .insert_attrib_data::<_, VertexIndex>("scalars", scalars)
1514 .unwrap();
1515
1516 polymesh
1517 .insert_attrib_data::<_, VertexIndex>("tensors", tensors)
1518 .unwrap();
1519
1520 polymesh
1521 .insert_attrib_data::<_, VertexIndex>("vectors", vectors)
1522 .unwrap();
1523
1524 polymesh
1525 }
1526
1527 #[test]
1528 fn mixed_unstructured_data_polymesh_test() {
1529 use model::CellType;
1530 let (points, cell_verts, data) = vtk_polymesh_example_data();
1531 let vtk = model::Vtk {
1532 version: model::Version::new((0, 1)),
1533 title: String::from("Polygonal Mesh"),
1534 byte_order: model::ByteOrder::BigEndian,
1535 file_path: None,
1536 data: model::DataSet::inline(model::UnstructuredGridPiece {
1537 points,
1538 cells: model::Cells {
1539 cell_verts: cell_verts.clone(),
1540 types: vec![CellType::Polygon, CellType::Polygon, CellType::PolyLine],
1541 },
1542 data: model::Attributes {
1543 point: data.point,
1544 cell: vec![model::Attribute::scalars("scalars", 1)
1546 .with_data(vec![0.0, 1.0, 2.0, 2.0, 2.0])],
1547 },
1548 }),
1549 };
1550
1551 let vtkpolymesh = vtk.extract_polymesh().unwrap();
1552 let polymesh = polymesh_example_with_polyline();
1553
1554 assert_eq!(vtkpolymesh, polymesh);
1556
1557 let polymeshvtk =
1559 convert_polymesh_to_vtk_format(&polymesh, VTKPolyExportStyle::UnstructuredGrid)
1560 .unwrap();
1561 let vtkpolymesh = polymeshvtk.extract_polymesh().unwrap();
1562 assert_eq!(vtkpolymesh, polymesh);
1563 }
1564
1565 #[test]
1566 fn poly_data_polymesh_test() {
1567 let (points, polys, data) = vtk_polymesh_example_data();
1568 let vtk = model::Vtk {
1569 version: model::Version::new((0, 1)),
1570 title: String::from("Polygonal Mesh"),
1571 byte_order: model::ByteOrder::BigEndian,
1572 file_path: None,
1573 data: model::DataSet::inline(model::PolyDataPiece {
1574 points,
1575 polys: Some(polys),
1576 data,
1577 ..Default::default()
1578 }),
1579 };
1580
1581 let vtkpolymesh = vtk.extract_polymesh().unwrap();
1582 let polymesh = polymesh_example();
1583
1584 assert_eq!(vtkpolymesh, polymesh);
1586
1587 let polymeshvtk =
1589 convert_polymesh_to_vtk_format(&polymesh, VTKPolyExportStyle::PolyData).unwrap();
1590 let vtkpolymesh = polymeshvtk.extract_polymesh().unwrap();
1591 assert_eq!(vtkpolymesh, polymesh);
1592 }
1593
1594 #[test]
1595 fn mixed_poly_data_polymesh_test() {
1596 let (points, polys, lines, data) = vtk_polymesh_example_mixed_polydata();
1597 let vtk = model::Vtk {
1598 version: model::Version::new((0, 1)),
1599 title: String::from("Polygonal Mesh"),
1600 byte_order: model::ByteOrder::BigEndian,
1601 file_path: None,
1602 data: model::DataSet::inline(model::PolyDataPiece {
1603 points,
1604 polys: Some(polys),
1605 lines: Some(lines),
1606 data,
1607 ..Default::default()
1608 }),
1609 };
1610
1611 let vtkpolymesh = vtk.extract_polymesh().unwrap();
1612 let polymesh = polymesh_example_with_polyline();
1613
1614 assert_eq!(vtkpolymesh, polymesh);
1616
1617 let polymeshvtk =
1619 convert_polymesh_to_vtk_format(&polymesh, VTKPolyExportStyle::PolyData).unwrap();
1620 let vtkpolymesh = polymeshvtk.extract_polymesh().unwrap();
1621 assert_eq!(vtkpolymesh, polymesh);
1622 }
1623
1624 fn vtk_pointcloud_example_data() -> (IOBuffer, model::Attributes) {
1625 (
1626 vec![
1627 2., 1., 0., 0., 0., 1., 0., 1., 1., 1., 1., 1., 2., 1., 1., 0., 1., 2., 2., 1., 2.,
1628 1., 1., 4., 2., 1., 4., 1., 1., 5., 2., 1., 5.,
1629 ]
1630 .into(),
1631 model::Attributes {
1632 point: vec![
1633 model::Attribute::scalars("scalars", 1).with_data(vec![
1634 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0f32,
1635 ]),
1636 model::Attribute::vectors("vectors").with_data(vec![
1637 1., 0., 0., 1., 1., 0., 0., 2., 0., 1., 0., 0., 1., 1., 0., 0., 2., 0., 1.,
1638 0., 0., 1., 1., 0., 0., 2., 0., 0., 0., 1., 0., 0., 1.,
1639 ]),
1640 model::Attribute::tensors("tensors").with_data(vec![
1641 1., 0., 0., 1., 1., 0., 0., 2., 0., 1., 0., 0., 1., 1., 0., 0., 2., 0., 1.,
1642 0., 0., 1., 1., 0., 0., 2., 0., 0., 0., 1., 0., 0., 1., 1., 0., 0., 1., 1.,
1643 0., 0., 2., 0., 1., 0., 0., 1., 1., 0., 0., 2., 0., 1., 0., 0., 1., 1., 0.,
1644 0., 2., 0., 0., 0., 1., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 2., 0., 1.,
1645 0., 0., 1., 1., 0., 0., 2., 0., 1., 0., 0., 1., 1., 0., 0., 2., 0., 0., 0.,
1646 1., 0., 0., 1.,
1647 ]),
1648 ],
1649 cell: vec![],
1650 },
1651 )
1652 }
1653
1654 fn pointcloud_example() -> PointCloud<f64> {
1657 let pts = vec![
1658 [2., 1., 0.],
1659 [0., 0., 1.],
1660 [0., 1., 1.],
1661 [1., 1., 1.],
1662 [2., 1., 1.],
1663 [0., 1., 2.],
1664 [2., 1., 2.],
1665 [1., 1., 4.],
1666 [2., 1., 4.],
1667 [1., 1., 5.],
1668 [2., 1., 5.],
1669 ];
1670 let mut pointcloud = PointCloud::new(pts);
1671
1672 let scalars = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0f32];
1673 let vectors = vec![
1674 [1., 0., 0.],
1675 [1., 1., 0.],
1676 [0., 2., 0.],
1677 [1., 0., 0.],
1678 [1., 1., 0.],
1679 [0., 2., 0.],
1680 [1., 0., 0.],
1681 [1., 1., 0.],
1682 [0., 2., 0.],
1683 [0., 0., 1.],
1684 [0., 0., 1.],
1685 ];
1686 let tensors = vec![
1687 [[1., 0., 0.], [1., 1., 0.], [0., 2., 0.]],
1688 [[1., 0., 0.], [1., 1., 0.], [0., 2., 0.]],
1689 [[1., 0., 0.], [1., 1., 0.], [0., 2., 0.]],
1690 [[0., 0., 1.], [0., 0., 1.], [1., 0., 0.]],
1691 [[1., 1., 0.], [0., 2., 0.], [1., 0., 0.]],
1692 [[1., 1., 0.], [0., 2., 0.], [1., 0., 0.]],
1693 [[1., 1., 0.], [0., 2., 0.], [0., 0., 1.]],
1694 [[0., 0., 1.], [1., 0., 0.], [1., 1., 0.]],
1695 [[0., 2., 0.], [1., 0., 0.], [1., 1., 0.]],
1696 [[0., 2., 0.], [1., 0., 0.], [1., 1., 0.]],
1697 [[0., 2., 0.], [0., 0., 1.], [0., 0., 1.]],
1698 ];
1699
1700 pointcloud
1701 .insert_attrib_data::<_, VertexIndex>("scalars", scalars)
1702 .unwrap();
1703
1704 pointcloud
1705 .insert_attrib_data::<_, VertexIndex>("tensors", tensors)
1706 .unwrap();
1707 pointcloud
1708 .insert_attrib_data::<_, VertexIndex>("vectors", vectors)
1709 .unwrap();
1710
1711 pointcloud
1712 }
1713
1714 #[test]
1715 fn poly_data_pointcloud_test() {
1716 let (points, data) = vtk_pointcloud_example_data();
1717 let num_vertices = (points.len() / 3) as u32;
1718 let verts = Some(model::VertexNumbers::Legacy {
1719 num_cells: 1,
1720 vertices: std::iter::once(num_vertices)
1721 .chain(0..num_vertices)
1722 .collect(),
1723 });
1724
1725 let vtk = model::Vtk {
1726 version: model::Version::new((0, 1)),
1727 title: String::from("Point Cloud"),
1728 byte_order: model::ByteOrder::BigEndian,
1729 file_path: None,
1730 data: model::DataSet::inline(model::PolyDataPiece {
1731 points,
1732 verts,
1733 data,
1734 ..Default::default()
1735 }),
1736 };
1737
1738 let vtkpointcloud = vtk.extract_pointcloud().unwrap();
1739 let pointcloud = pointcloud_example();
1740
1741 assert_eq!(vtkpointcloud, pointcloud);
1743
1744 let pointcloudvtk =
1746 convert_pointcloud_to_vtk_format(&pointcloud, VTKPolyExportStyle::PolyData).unwrap();
1747 let vtkpointcloud = pointcloudvtk.extract_pointcloud().unwrap();
1748 assert_eq!(vtkpointcloud, pointcloud);
1749 }
1750}