vtkio/lib.rs
1//! Import and export library for Visualization Toolkit (VTK)
2//! [files](https://lorensen.github.io/VTKExamples/site/VTKFileFormats/).
3//!
4//! Legacy `.vtk` files as well as modern XML formats are supported.
5//! Both "serial" and "parallel" XML files are supported with facilities for lazily loading.
6//!
7//! The [`Vtk`] struct exposes the primary IO API.
8//!
9//! # Examples
10//!
11//! Many sample files can be found in the `assets` directory.
12//!
13//! For the following example, we will load a VTK file named `tet.vtk`, modify it and write it back
14//! in Legacy ASCII format.
15//!
16//! ```no_run
17//! use vtkio::model::*; // import model definition of a VTK file
18//! fn main() {
19//! use std::path::PathBuf;
20//! let file_path = PathBuf::from("../assets/tet.vtk");
21//!
22//! let mut vtk_file = Vtk::import(&file_path)
23//! .expect(&format!("Failed to load file: {:?}", file_path));
24//!
25//! vtk_file.version = Version::new((4,2)); // arbitrary change
26//!
27//! vtk_file.export_ascii(&file_path)
28//! .expect(&format!("Failed to save file: {:?}", file_path));
29//! }
30//! ```
31//!
32//! Files are sometimes provided as strings or byte slices, so it is also useful to be able to
33//! parse VTK files and write them back to a string or byte slice.
34//!
35//! ```no_run
36//! use vtkio::model::*; // import model definition of a VTK file
37//! fn main() {
38//! let data: &[u8] = include_str!("../assets/tet.vtk").as_bytes(); // Or just include_bytes!
39//!
40//! let mut vtk_file = Vtk::parse_legacy_be(data).expect(&format!("Failed to parse file"));
41//!
42//! vtk_file.version = Version::new((4,2)); // arbitrary change
43//!
44//! let mut output = String::new();
45//! Vtk::write_legacy_ascii(vtk_file, &mut output).expect(&format!("Failed to write file"));
46//!
47//! println!("{}", output);
48//! }
49//! ```
50#[macro_use]
51extern crate nom;
52
53#[macro_use]
54pub mod basic;
55
56#[macro_use]
57pub mod model;
58pub mod parser;
59pub mod writer;
60#[cfg(feature = "xml")]
61pub mod xml;
62
63#[cfg(feature = "xml")]
64use std::convert::{TryFrom, TryInto};
65use std::fs::File;
66#[cfg(feature = "xml")]
67use std::io::BufRead;
68use std::io::{self, BufWriter, Read, Write};
69use std::path::Path;
70
71use crate::writer::{AsciiWriter, BinaryWriter, WriteVtk};
72
73pub use model::IOBuffer;
74
75/// The primary `vtkio` API is provided through the `Vtk` struct.
76pub use model::Vtk;
77
78/// Error type for Import/Export operations.
79#[derive(Debug)]
80pub enum Error {
81 IO(io::Error),
82 Write(writer::Error),
83 Parse(nom::ErrorKind<u32>),
84 #[cfg(feature = "xml")]
85 XML(xml::Error),
86 UnknownFileExtension(Option<String>),
87 Load(model::Error),
88 Unknown,
89}
90
91impl std::fmt::Display for Error {
92 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
93 match self {
94 Error::IO(source) => write!(f, "IO error: {}", source),
95 Error::Write(source) => write!(f, "Write error: {}", source),
96 Error::Parse(source) => write!(f, "Parse error: {:?}", source),
97 #[cfg(feature = "xml")]
98 Error::XML(source) => write!(f, "XML error: {}", source),
99 Error::UnknownFileExtension(Some(ext)) => {
100 write!(f, "Unknown file extension: {:?}", ext)
101 }
102 Error::UnknownFileExtension(None) => write!(f, "Missing file extension"),
103 Error::Load(source) => write!(f, "Load error: {}", source),
104 Error::Unknown => write!(f, "Unknown error"),
105 }
106 }
107}
108
109impl std::error::Error for Error {
110 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
111 match self {
112 Error::IO(source) => Some(source),
113 Error::Write(source) => Some(source),
114 Error::Parse(_) => None,
115 #[cfg(feature = "xml")]
116 Error::XML(source) => Some(source),
117 Error::UnknownFileExtension(_) => None,
118 Error::Load(source) => Some(source),
119 Error::Unknown => None,
120 }
121 }
122}
123
124/// Convert `std::io` error into `vtkio` error.
125impl From<io::Error> for Error {
126 fn from(e: io::Error) -> Error {
127 Error::IO(e)
128 }
129}
130
131/// Convert [`xml::Error`] error into the top level `vtkio` error.
132///
133/// [`xml::Error`]: xml.enum.Error.html
134#[cfg(feature = "xml")]
135impl From<xml::Error> for Error {
136 fn from(e: xml::Error) -> Error {
137 Error::XML(e)
138 }
139}
140
141/// Convert `vtkio` error into `std::io` error.
142impl From<Error> for io::Error {
143 fn from(err: Error) -> io::Error {
144 match err {
145 Error::IO(e) => e,
146 _ => io::Error::new(io::ErrorKind::Other, format!("{:?}", err)),
147 }
148 }
149}
150
151impl From<writer::Error> for Error {
152 fn from(e: writer::Error) -> Error {
153 Error::Write(e)
154 }
155}
156
157impl Vtk {
158 /// Helper for parsing legacy VTK files.
159 fn parse_vtk<F>(mut reader: impl Read, parse: F, buf: &mut Vec<u8>) -> Result<Vtk, Error>
160 where
161 F: Fn(&[u8]) -> nom::IResult<&[u8], Vtk>,
162 {
163 use nom::IResult;
164 reader.read_to_end(buf)?;
165 match parse(buf) {
166 IResult::Done(_, vtk) => Ok(vtk),
167 IResult::Error(e) => Err(Error::Parse(e.into_error_kind())),
168 IResult::Incomplete(_) => Err(Error::Unknown),
169 }
170 }
171
172 /// Helper for importing legacy VTK files from the given path.
173 fn import_vtk<F>(file_path: &Path, parse: F) -> Result<Vtk, Error>
174 where
175 F: Fn(&[u8]) -> nom::IResult<&[u8], Vtk>,
176 {
177 let file = File::open(file_path)?;
178 Vtk::parse_vtk(file, parse, &mut Vec::new())
179 }
180
181 /// Parse a legacy VTK file from the given reader.
182 ///
183 /// If the file is in binary format, numeric types will be interpreted in big endian format,
184 /// which is the most common among VTK files.
185 /// Note that this function and [`parse_legacy_le`](Vtk::parse_legacy_le) also work equally well for
186 /// parsing VTK files in ASCII format.
187 ///
188 /// # Examples
189 ///
190 /// Parsing an ASCII file:
191 ///
192 /// ```
193 /// use vtkio::model::*; // import the model definition of a VTK file
194 /// let vtk_ascii: &[u8] = b"
195 /// ## vtk DataFile Version 2.0
196 /// Triangle example
197 /// ASCII
198 /// DATASET POLYDATA
199 /// POINTS 3 float
200 /// 0.0 0.0 0.0
201 /// 1.0 0.0 0.0
202 /// 0.0 0.0 -1.0
203 ///
204 /// POLYGONS 1 4
205 /// 3 0 1 2
206 /// ";
207 ///
208 /// let vtk = Vtk::parse_legacy_be(vtk_ascii).expect("Failed to parse vtk file");
209 ///
210 /// assert_eq!(vtk, Vtk {
211 /// version: Version::new((2,0)),
212 /// byte_order: ByteOrder::BigEndian,
213 /// title: String::from("Triangle example"),
214 /// file_path: None,
215 /// data: DataSet::inline(PolyDataPiece {
216 /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(),
217 /// polys: Some(VertexNumbers::Legacy {
218 /// num_cells: 1,
219 /// vertices: vec![3, 0, 1, 2]
220 /// }),
221 /// data: Attributes::new(),
222 /// ..Default::default()
223 /// })
224 /// });
225 /// ```
226 pub fn parse_legacy_be(reader: impl Read) -> Result<Vtk, Error> {
227 Vtk::parse_vtk(reader, parser::parse_be, &mut Vec::new())
228 }
229
230 /// Parse a legacy VTK file from the given reader.
231 ///
232 /// If the file is in binary format, numeric types will be interpreted in little endian format.
233 /// Note that this function and [`parse_legacy_be`](Vtk::parse_legacy_be) also work equally well for
234 /// parsing VTK files in ASCII format.
235 ///
236 /// # Examples
237 ///
238 /// Parsing an ASCII file:
239 ///
240 /// ```
241 /// use vtkio::model::*; // import the model definition of a VTK file
242 /// let vtk_ascii: &[u8] = b"
243 /// ## vtk DataFile Version 2.0
244 /// Triangle example
245 /// ASCII
246 /// DATASET POLYDATA
247 /// POINTS 3 float
248 /// 0.0 0.0 0.0
249 /// 1.0 0.0 0.0
250 /// 0.0 0.0 -1.0
251 ///
252 /// POLYGONS 1 4
253 /// 3 0 1 2
254 /// ";
255 ///
256 /// let vtk = Vtk::parse_legacy_le(vtk_ascii).expect("Failed to parse vtk file");
257 ///
258 /// assert_eq!(vtk, Vtk {
259 /// version: Version::new((2,0)),
260 /// byte_order: ByteOrder::LittleEndian,
261 /// title: String::from("Triangle example"),
262 /// file_path: None,
263 /// data: DataSet::inline(PolyDataPiece {
264 /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(),
265 /// polys: Some(VertexNumbers::Legacy {
266 /// num_cells: 1,
267 /// vertices: vec![3, 0, 1, 2]
268 /// }),
269 /// data: Attributes::new(),
270 /// ..Default::default()
271 /// })
272 /// });
273 /// ```
274 pub fn parse_legacy_le(reader: impl Read) -> Result<Vtk, Error> {
275 Vtk::parse_vtk(reader, parser::parse_le, &mut Vec::new())
276 }
277
278 /// Parse a legacy VTK file in big endian format from the given reader and a buffer.
279 ///
280 /// This is the buffered version of [`Vtk::parse_legacy_be`](Vtk::parse_legacy_be), which allows one to reuse the same
281 /// heap allocated space when reading many files.
282 pub fn parse_legacy_buf_be(reader: impl Read, buf: &mut Vec<u8>) -> Result<Vtk, Error> {
283 Vtk::parse_vtk(reader, parser::parse_be, buf)
284 }
285
286 /// Parse a legacy VTK file in little endian format from the given reader and a buffer.
287 ///
288 /// This is the buffered version of [`parse_legacy_le`](Vtk::parse_legacy_le), which allows one to reuse the same
289 /// heap allocated space when reading many files.
290 pub fn parse_legacy_buf_le(reader: impl Read, buf: &mut Vec<u8>) -> Result<Vtk, Error> {
291 Vtk::parse_vtk(reader, parser::parse_le, buf)
292 }
293
294 /// Parse a modern XML style VTK file from a given reader.
295 ///
296 /// # Examples
297 ///
298 /// Parsing a binary file in big endian format representing a polygon mesh consisting of a single
299 /// triangle:
300 ///
301 /// ```
302 /// use vtkio::model::*; // import the model definition of a VTK file
303 ///
304 /// let input: &[u8] = b"\
305 /// <VTKFile type=\"PolyData\" version=\"2.0\" byte_order=\"BigEndian\">\
306 /// <PolyData>\
307 /// <Piece NumberOfPoints=\"3\" NumberOfLines=\"0\" NumberOfStrips=\"0\" NumberOfPolys=\"1\" NumberOfVerts=\"0\">\
308 /// <PointData/>\
309 /// <CellData/>\
310 /// <Points>\
311 /// <DataArray type=\"Float32\" format=\"binary\" NumberOfComponents=\"3\">\
312 /// AAAAAAAAAAQAAAAAAAAAAAAAAAA/gAAAAAAAAAAAAAAAAAAAAAAAAL+AAAA=\
313 /// </DataArray>\
314 /// </Points>\
315 /// <Polys>\
316 /// <DataArray type=\"UInt64\" Name=\"connectivity\" format=\"binary\" NumberOfComponents=\"1\">\
317 /// AAAAAAAAAAgAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAI=\
318 /// </DataArray>\
319 /// <DataArray type=\"UInt64\" Name=\"offsets\" format=\"binary\" NumberOfComponents=\"1\">\
320 /// AAAAAAAAAAgAAAAAAAAAAw==\
321 /// </DataArray>\
322 /// </Polys>\
323 /// </Piece>\
324 /// </PolyData>\
325 /// </VTKFile>";
326 ///
327 /// let vtk = Vtk::parse_xml(input).expect("Failed to parse XML VTK file");
328 ///
329 /// assert_eq!(vtk, Vtk {
330 /// version: Version::new((2,0)),
331 /// byte_order: ByteOrder::BigEndian, // This is default
332 /// title: String::new(),
333 /// file_path: None,
334 /// data: DataSet::inline(PolyDataPiece {
335 /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(),
336 /// polys: Some(VertexNumbers::XML {
337 /// connectivity: vec![0, 1, 2],
338 /// offsets: vec![3]
339 /// }),
340 /// data: Attributes::new(),
341 /// ..Default::default()
342 /// })
343 /// });
344 /// ```
345 #[cfg(feature = "xml")]
346 pub fn parse_xml(reader: impl BufRead) -> Result<Vtk, Error> {
347 // There is no extension to check with the data is provided directly.
348 // Luckily the xml file contains all the data necessary to determine which data is
349 // being parsed.
350 let vtk_file = xml::parse(reader)?;
351 Ok(vtk_file.try_into()?)
352 }
353
354 #[cfg(feature = "async_blocked")]
355 async fn import_vtk_async<F>(file_path: &Path, parse: F) -> Result<Vtk, Error>
356 where
357 F: Fn(&[u8]) -> nom::IResult<&[u8], Vtk>,
358 {
359 use nom::IResult;
360 use tokio::fs::File;
361 use tokio::io::AsyncReadExt;
362
363 let mut file = File::open(file_path).await?;
364 let mut buf = Vec::new();
365 file.read_to_end(&mut buf).await?;
366 match parse(&buf) {
367 IResult::Done(_, vtk) => Ok(vtk),
368 IResult::Error(e) => Err(Error::Parse(e.into_error_kind())),
369 IResult::Incomplete(_) => Err(Error::Unknown),
370 }
371 }
372
373 /// Import a VTK file at the specified path.
374 ///
375 /// This function determines the vtk file type from the extension as prescribed by the [VTK
376 /// file formats documentation](https://lorensen.github.io/VTKExamples/site/VTKFileFormats/):
377 ///
378 /// - Legacy (`.vtk`) -- Simple legacy file format (Non-XML)
379 /// - Image data (`.vti`) -- Serial vtkImageData (structured)
380 /// - PolyData (`.vtp`) -- Serial vtkPolyData (unstructured)
381 /// - RectilinearGrid (`.vtr`) -- Serial vtkRectilinearGrid (structured)
382 /// - StructuredGrid (`.vts`) -- Serial vtkStructuredGrid (structured)
383 /// - UnstructuredGrid (`.vtu`) -- Serial vtkUnstructuredGrid (unstructured)
384 /// - PImageData (`.pvti`) -- Parallel vtkImageData (structured)
385 /// - PPolyData (`.pvtp`) -- Parallel vtkPolyData (unstructured)
386 /// - PRectilinearGrid (`.pvtr`) -- Parallel vtkRectilinearGrid (structured)
387 /// - PStructuredGrid (`.pvts`) -- Parallel vtkStructuredGrid (structured)
388 /// - PUnstructuredGrid (`.pvtu`) -- Parallel vtkUnstructuredGrid (unstructured)
389 ///
390 /// # Examples
391 ///
392 /// The following example imports a legacy `.vtk` file called `tet.vtk`, and panics with an
393 /// appropriate error message if the file fails to load.
394 ///
395 /// ```should_panic
396 /// use vtkio::Vtk;
397 /// use std::path::PathBuf;
398 ///
399 /// let file_path = PathBuf::from("tet.vtk");
400 ///
401 /// let mut vtk_file = Vtk::import(&file_path)
402 /// .expect(&format!("Failed to load file: {:?}", file_path));
403 /// ```
404 pub fn import(file_path: impl AsRef<Path>) -> Result<Vtk, Error> {
405 Vtk::import_impl(file_path.as_ref())
406 }
407
408 /// A non-generic helper for the `import` function.
409 fn import_impl(path: &Path) -> Result<Vtk, Error> {
410 let ext = path
411 .extension()
412 .and_then(|s| s.to_str())
413 .ok_or(Error::UnknownFileExtension(None))?;
414 match ext {
415 "vtk" => Vtk::import_vtk(path, parser::parse_be),
416 #[cfg(feature = "xml")]
417 ext => {
418 let ft = xml::FileType::try_from_ext(ext)
419 .ok_or(Error::UnknownFileExtension(Some(ext.to_string())))?;
420 let vtk_file = xml::import(path)?;
421 let exp_ft = xml::FileType::from(vtk_file.data_set_type);
422 if ft != exp_ft {
423 Err(Error::XML(xml::Error::TypeExtensionMismatch))
424 } else {
425 let mut vtk: Vtk = vtk_file.try_into()?;
426 vtk.file_path = Some(path.into());
427 Ok(vtk)
428 }
429 }
430 #[cfg(not(feature = "xml"))]
431 _ => Err(Error::UnknownFileExtension(None)),
432 }
433 }
434
435 /// Import a VTK file at the specified path.
436 ///
437 /// This is the async version of [`import`](Vtk::import).
438 ///
439 /// This function determines the vtk file type from the extension as prescribed by the [VTK
440 /// file formats documentation](https://lorensen.github.io/VTKExamples/site/VTKFileFormats/):
441 ///
442 /// - Legacy (`.vtk`) -- Simple legacy file format (Non-XML)
443 /// - Image data (`.vti`) -- Serial vtkImageData (structured)
444 /// - PolyData (`.vtp`) -- Serial vtkPolyData (unstructured)
445 /// - RectilinearGrid (`.vtr`) -- Serial vtkRectilinearGrid (structured)
446 /// - StructuredGrid (`.vts`) -- Serial vtkStructuredGrid (structured)
447 /// - UnstructuredGrid (`.vtu`) -- Serial vtkUnstructuredGrid (unstructured)
448 /// - PImageData (`.pvti`) -- Parallel vtkImageData (structured)
449 /// - PPolyData (`.pvtp`) -- Parallel vtkPolyData (unstructured)
450 /// - PRectilinearGrid (`.pvtr`) -- Parallel vtkRectilinearGrid (structured)
451 /// - PStructuredGrid (`.pvts`) -- Parallel vtkStructuredGrid (structured)
452 /// - PUnstructuredGrid (`.pvtu`) -- Parallel vtkUnstructuredGrid (unstructured)
453 ///
454 /// # Examples
455 ///
456 /// The following example imports a legacy `.vtk` file called `tet.vtk`, and panics with an
457 /// appropriate error message if the file fails to load.
458 ///
459 /// ```should_panic
460 /// use vtkio::Vtk;
461 /// use std::path::PathBuf;
462 ///
463 /// let file_path = PathBuf::from("tet.vtk");
464 ///
465 /// let mut vtk_file = Vtk::import_async(&file_path).await
466 /// .expect(&format!("Failed to load file: {:?}", file_path));
467 /// ```
468 #[cfg(feature = "async_blocked")]
469 pub async fn import_async(file_path: impl AsRef<Path>) -> Result<Vtk, Error> {
470 let path = file_path.as_ref();
471 let ext = path.extension().and_then(|s| s.to_str()).ok_or()?;
472 match ext {
473 "vtk" => import_vtk_async(path, parser::parse_be).await,
474 #[cfg(feature = "xml")]
475 ext => {
476 let ft = xml::FileType::try_from_ext(ext)
477 .ok_or(Error::UnknownFileExtension(Some(ext.to_string())))?;
478 let vtk_file = xml::import_async(path).await?;
479 let exp_ft = xml::FileType::from(vtk_file.data_set_type);
480 if ft != exp_ft {
481 Err(Error::XML(xml::Error::TypeExtensionMismatch))
482 } else {
483 Ok(vtk_file.try_into()?)
484 }
485 }
486 #[cfg(not(feature = "xml"))]
487 _ => Err(Error::UnknownFileExtension(None)),
488 }
489 }
490
491 /// Import an XML VTK file in raw form.
492 ///
493 /// This importer performs a direct translation from the XML string to a Rust representation
494 /// without any decoding or conversion. For a more complete import use [`import`].
495 ///
496 /// [`VTKFile`] is used internally as an intermediate step for constructing the [`Vtk`] model,
497 /// which has built-in facilities for loading pieces referenced in "parallel" XML formats as well
498 /// as representing Legacy VTK formats, which are more compact when serialized.
499 ///
500 /// [`Vtk`]: model/struct.Vtk.html
501 /// [`VTKFile`]: xml/struct.VTKFile.html
502 /// [`import`]: fn.import.html
503 #[cfg(feature = "unstable")]
504 pub fn import_xml(file_path: impl AsRef<Path>) -> Result<xml::VTKFile, Error> {
505 let path = file_path.as_ref();
506 let ext = path
507 .extension()
508 .and_then(|s| s.to_str())
509 .ok_or(Error::UnknownFileExtension(None))?;
510
511 // Check that the file extension is one of the known ones.
512 let _ = xml::FileType::try_from_ext(ext)
513 .ok_or(Error::UnknownFileExtension(Some(ext.to_string())))?;
514
515 Ok(xml::import(path)?)
516 }
517
518 /// Import a legacy VTK file at the specified path.
519 ///
520 /// If the file is in binary format, numeric types will be interpreted in little endian format.
521 /// For the default byte order used by most `.vtk` files use [`import`] or [`import_legacy_be`].
522 ///
523 /// [`import`]: fn.import.html
524 /// [`import_legacy_be`]: fn.import_legacy_be.html
525 pub fn import_legacy_le(file_path: impl AsRef<Path>) -> Result<Vtk, Error> {
526 Vtk::import_vtk(file_path.as_ref(), parser::parse_le)
527 }
528
529 #[deprecated(since = "0.6.2", note = "Please use Vtk::import_legacy_le instead")]
530 pub fn import_le(file_path: impl AsRef<Path>) -> Result<Vtk, Error> {
531 Vtk::import_legacy_le(file_path.as_ref())
532 }
533
534 /// Import a legacy VTK file at the specified path.
535 ///
536 /// If the file is in binary format, numeric types will be interpreted in big endian format.
537 /// This function behaves the same as [`import`], but expects the given file to be strictly in
538 /// legacy `.vtk` format.
539 ///
540 /// [`import`]: fn.import.html
541 pub fn import_legacy_be(file_path: impl AsRef<Path>) -> Result<Vtk, Error> {
542 Vtk::import_vtk(file_path.as_ref(), parser::parse_be)
543 }
544
545 #[deprecated(since = "0.6.2", note = "Please use Vtk::import_legacy_be instead")]
546 pub fn import_be(file_path: impl AsRef<Path>) -> Result<Vtk, Error> {
547 Vtk::import_legacy_be(file_path.as_ref())
548 }
549
550 /// Export given [`Vtk`] file to the specified file.
551 ///
552 /// The type of file exported is determined by the extension in `file_path`.
553 ///
554 /// Files ending in `.vtk` are exported in binary format. For exporting in ASCII, use
555 /// [`export_ascii`].
556 ///
557 /// Endianness is determined by the `byte_order` field of the [`Vtk`] type.
558 ///
559 /// # Examples
560 ///
561 /// ```no_run
562 /// use vtkio::model::*;
563 /// use std::path::PathBuf;
564 /// let vtk = Vtk {
565 /// version: Version::new((4,1)),
566 /// byte_order: ByteOrder::BigEndian,
567 /// title: String::from("Tetrahedron"),
568 /// file_path: Some(PathBuf::from("./test.vtk")),
569 /// data: DataSet::inline(UnstructuredGridPiece {
570 /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0].into(),
571 /// cells: Cells {
572 /// cell_verts: VertexNumbers::Legacy {
573 /// num_cells: 1,
574 /// vertices: vec![4, 0, 1, 2, 3]
575 /// },
576 /// types: vec![CellType::Tetra],
577 /// },
578 /// data: Attributes::new(),
579 /// })
580 /// };
581 /// vtk.export("test.vtk");
582 /// ```
583 ///
584 /// [`Vtk`]: struct.Vtk.html
585 /// [`export_ascii`]: fn.export_ascii.html
586 pub fn export(self, file_path: impl AsRef<Path>) -> Result<(), Error> {
587 self.export_impl(file_path.as_ref())
588 }
589
590 /// A non-generic helper for the export function.
591 fn export_impl(self, path: &Path) -> Result<(), Error> {
592 let ext = path
593 .extension()
594 .and_then(|s| s.to_str())
595 .ok_or(Error::UnknownFileExtension(None))?;
596 match ext {
597 "vtk" => {
598 let file = File::create(path)?;
599 BinaryWriter(BufWriter::new(file)).write_vtk(self)?;
600 Ok(())
601 }
602 #[cfg(feature = "xml")]
603 ext => {
604 let ft = xml::FileType::try_from_ext(ext)
605 .ok_or(Error::UnknownFileExtension(Some(ext.to_string())))?;
606 let vtk_file = xml::VTKFile::try_from(self)?;
607 let exp_ft = xml::FileType::from(vtk_file.data_set_type);
608 if ft != exp_ft {
609 Err(Error::XML(xml::Error::TypeExtensionMismatch))
610 } else {
611 xml::export(&vtk_file, path)?;
612 Ok(())
613 }
614 }
615 #[cfg(not(feature = "xml"))]
616 _ => Err(Error::UnknownFileExtension(None)),
617 }
618 }
619
620 /// Write the given VTK file in binary legacy format to the specified [`Write`](std::io::Write)r.
621 ///
622 /// # Examples
623 ///
624 /// Writing a binary file in big endian format representing a polygon mesh consisting of a single
625 /// triangle:
626 ///
627 /// ```
628 /// use vtkio::model::*; // import model definition of a VTK file
629 ///
630 /// let mut vtk_bytes = Vec::<u8>::new();
631 ///
632 /// Vtk {
633 /// version: Version::new((2,0)),
634 /// byte_order: ByteOrder::BigEndian,
635 /// title: String::from("Triangle example"),
636 /// file_path: None,
637 /// data: DataSet::inline(PolyDataPiece {
638 /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(),
639 /// polys: Some(VertexNumbers::Legacy {
640 /// num_cells: 1,
641 /// vertices: vec![3, 0, 1, 2]
642 /// }),
643 /// data: Attributes::new(),
644 /// ..Default::default()
645 /// })
646 /// }.write_legacy(&mut vtk_bytes);
647 ///
648 /// println!("{}", String::from_utf8_lossy(&vtk_bytes));
649 /// ```
650 pub fn write_legacy(self, writer: impl std::io::Write) -> Result<(), Error> {
651 BinaryWriter(writer).write_vtk(self)?;
652 Ok(())
653 }
654
655 /// Write the given VTK file in binary legacy format to the specified [`Write`](std::fmt::Write)r.
656 ///
657 /// # Examples
658 ///
659 /// Writing an ASCII file representing a polygon mesh consisting of a single triangle:
660 ///
661 /// ```
662 /// use vtkio::model::*; // import model definition of a VTK file
663 ///
664 /// let mut vtk_string = String::new();
665 ///
666 /// Vtk {
667 /// version: Version::new((2,0)),
668 /// byte_order: ByteOrder::BigEndian, // Ignored
669 /// title: String::from("Triangle example"),
670 /// file_path: None,
671 /// data: DataSet::inline(PolyDataPiece {
672 /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(),
673 /// polys: Some(VertexNumbers::Legacy {
674 /// num_cells: 1,
675 /// vertices: vec![3, 0, 1, 2]
676 /// }),
677 /// data: Attributes::new(),
678 /// ..Default::default()
679 /// })
680 /// }.write_legacy_ascii(&mut vtk_string);
681 ///
682 /// assert_eq!(vtk_string.as_str(), "\
683 /// ## vtk DataFile Version 2.0
684 /// Triangle example
685 /// ASCII
686 ///
687 /// DATASET POLYDATA
688 /// POINTS 3 float
689 /// 0 0 0 1 0 0 0 0 -1
690 ///
691 /// POLYGONS 1 4
692 /// 3 0 1 2
693 ///
694 /// POINT_DATA 3
695 ///
696 /// CELL_DATA 1
697 ///
698 /// ");
699 /// ```
700 pub fn write_legacy_ascii(self, writer: impl std::fmt::Write) -> Result<(), Error> {
701 AsciiWriter(writer).write_vtk(self)?;
702 Ok(())
703 }
704
705 /// Write the given VTK file in modern XML format to the specified [`Write`](std::io::Write)r.
706 ///
707 /// # Examples
708 ///
709 /// Writing a binary file in big endian format representing a polygon mesh consisting of a single
710 /// triangle:
711 ///
712 /// ```
713 /// use vtkio::model::*; // import model definition of a VTK file
714 ///
715 /// let mut vtk_bytes = Vec::<u8>::new();
716 ///
717 /// Vtk {
718 /// version: Version::new((2,0)),
719 /// byte_order: ByteOrder::BigEndian,
720 /// title: String::from("Triangle example"),
721 /// file_path: None,
722 /// data: DataSet::inline(PolyDataPiece {
723 /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(),
724 /// polys: Some(VertexNumbers::Legacy {
725 /// num_cells: 1,
726 /// vertices: vec![3, 0, 1, 2]
727 /// }),
728 /// data: Attributes::new(),
729 /// ..Default::default()
730 /// })
731 /// }.write_xml(&mut vtk_bytes);
732 ///
733 /// assert_eq!(String::from_utf8_lossy(&vtk_bytes), "\
734 /// <VTKFile type=\"PolyData\" version=\"2.0\" byte_order=\"BigEndian\" header_type=\"UInt64\">\
735 /// <PolyData>\
736 /// <Piece NumberOfPoints=\"3\" NumberOfLines=\"0\" NumberOfStrips=\"0\" NumberOfPolys=\"1\" NumberOfVerts=\"0\">\
737 /// <PointData/>\
738 /// <CellData/>\
739 /// <Points>\
740 /// <DataArray type=\"Float32\" format=\"binary\" NumberOfComponents=\"3\">\
741 /// AAAAAAAAACQAAAAAAAAAAAAAAAA/gAAAAAAAAAAAAAAAAAAAAAAAAL+AAAA=\
742 /// </DataArray>\
743 /// </Points>\
744 /// <Polys>\
745 /// <DataArray type=\"UInt64\" Name=\"connectivity\" format=\"binary\" NumberOfComponents=\"1\">\
746 /// AAAAAAAAABgAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAI=\
747 /// </DataArray>\
748 /// <DataArray type=\"UInt64\" Name=\"offsets\" format=\"binary\" NumberOfComponents=\"1\">\
749 /// AAAAAAAAAAgAAAAAAAAAAw==\
750 /// </DataArray>\
751 /// </Polys>\
752 /// </Piece>\
753 /// </PolyData>\
754 /// </VTKFile>");
755 /// ```
756 #[cfg(feature = "xml")]
757 pub fn write_xml(self, writer: impl Write) -> Result<(), Error> {
758 let vtk_file = xml::VTKFile::try_from(self)?;
759 xml::write(&vtk_file, writer)?;
760 Ok(())
761 }
762
763 /// Export the VTK data to the specified path in little endian binary format.
764 ///
765 /// This function is used as [`export`] but overrides endiannes.
766 ///
767 /// [`export`]: fn.export.html
768 pub fn export_le(self, file_path: impl AsRef<Path>) -> Result<(), Error> {
769 let file = File::create(file_path.as_ref())?;
770 BinaryWriter(BufWriter::new(file)).write_vtk_le(self)?;
771 Ok(())
772 }
773
774 /// Export the VTK data to the specified path in big endian binary format.
775 ///
776 /// This function is used as [`export`] but overrides endiannes.
777 ///
778 /// [`export`]: fn.export.html
779 pub fn export_be(self, file_path: impl AsRef<Path>) -> Result<(), Error> {
780 let file = File::create(file_path.as_ref())?;
781 BinaryWriter(BufWriter::new(file)).write_vtk_be(self)?;
782 Ok(())
783 }
784
785 /// Export VTK data to the specified file in ASCII format.
786 ///
787 /// # Examples
788 ///
789 /// ```no_run
790 /// use vtkio::model::*;
791 /// use std::path::PathBuf;
792 /// let vtk = Vtk {
793 /// version: Version::new((4,1)),
794 /// title: String::from("Tetrahedron"),
795 /// byte_order: ByteOrder::BigEndian,
796 /// file_path: Some(PathBuf::from("./test.vtk")),
797 /// data: DataSet::inline(UnstructuredGridPiece {
798 /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0].into(),
799 /// cells: Cells {
800 /// cell_verts: VertexNumbers::Legacy {
801 /// num_cells: 1,
802 /// vertices: vec![4, 0, 1, 2, 3]
803 /// },
804 /// types: vec![CellType::Tetra],
805 /// },
806 /// data: Attributes::new(),
807 /// })
808 /// };
809 /// vtk.export_ascii("test.vtk");
810 /// ```
811 pub fn export_ascii(self, file_path: impl AsRef<Path>) -> Result<(), Error> {
812 // Ascii formats are typically used for small files, so it makes sense to make the write
813 // in-memory first.
814 let mut out_str = AsciiWriter(String::new());
815 out_str.write_vtk(self)?;
816 let mut file = File::create(file_path.as_ref())?;
817 file.write_all(out_str.0.as_bytes())?;
818 Ok(())
819 }
820}