├── .github └── workflows │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── assets ├── RectilinearGrid.pvtr ├── RectilinearGrid │ └── RectilinearGrid_0.vtr ├── RectilinearGridAppendedBase64.vtr ├── RectilinearGridCompressed.vtr ├── RectilinearGridInlineBinary.vtr ├── RectilinearGridRawBinary.vtr ├── RectilinearGrid_ascii.vtr ├── box.vtu ├── box_para.vtu ├── cube.pvtp ├── cube.vtk ├── cube_complex.vtk ├── cube_complex_topo.vtk ├── dodecagon.vtk ├── dodecagon_ascii.vtk ├── dodecagon_ascii_simple.vtk ├── dodecagon_simple.vtk ├── field.vtk ├── hexahedron.vtu ├── hexahedron_ascii.vtu ├── hexahedron_binary.vtu ├── hexahedron_inline_binary.vtu ├── hexahedron_lz4.vtu ├── hexahedron_lz4_inline_binary.vtu ├── hexahedron_lzma_inline_binary.vtu ├── hexahedron_parallel.pvtu ├── hexahedron_parallel_0.vtu ├── hexahedron_parallel_lzma.pvtu ├── hexahedron_parallel_lzma_0.vtu ├── hexahedron_zlib.vtu ├── hexahedron_zlib_binary.vtu ├── hexahedron_zlib_inline_binary.vtu ├── hexahedron_zlib_para.vtu ├── para_test.vtk ├── para_test_ascii.vtk ├── para_tet.vtk ├── para_tet_ascii.vtk ├── point.vtp ├── point_ascii.vtp ├── point_cloud.vtk ├── point_cloud.vtp ├── polyEx0.vtp ├── pygmsh │ ├── ascii.vtk │ ├── ascii.vtu │ ├── binary.vtk │ ├── gen.py │ ├── lzma.vtu │ ├── no-compression.vtu │ └── zlib.vtu ├── rectilinear_grid.vtk ├── rectilinear_grid_binary.vtk ├── square.vtk ├── structured_grid.vtk ├── tet.vtk ├── tet.vtu ├── tet_test.vtk ├── tet_test_binary.vtk ├── tri.vtk ├── tri_attrib.vtk ├── tri_attrib_binary.vtk ├── triangle_vtkidtype.vtk ├── unstructured_grid_complex.vtk ├── volume_complex.vti └── volume_complex.vtk ├── src ├── basic.rs ├── lib.rs ├── model.rs ├── parser.rs ├── writer.rs ├── xml.rs └── xml │ └── se.rs └── tests ├── legacy.rs ├── pygmsh.rs └── xml.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | - name: Build with no parallelism 24 | run: cargo build --no-default-features --features "xml compression" --verbose 25 | - name: Run tests with no parallelism 26 | run: cargo test --no-default-features --features "xml compression" --verbose 27 | - name: Build with no compression or parallelism 28 | run: cargo build --no-default-features --features "xml" --verbose 29 | - name: Run tests with no compression or parallelism 30 | run: cargo test --no-default-features --features "xml" --verbose 31 | - name: Build with no default features 32 | run: cargo build --no-default-features --verbose 33 | - name: Run tests with no default features 34 | run: cargo test --no-default-features --verbose 35 | 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /target_remote 3 | **/*.rs.bk 4 | Cargo.lock 5 | .vscode 6 | .DS_Store 7 | .idea 8 | /assets/proto 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | This document outlines changes and updates in major releases of `vtkio`. 4 | 5 | # Release 0.6 6 | 7 | This release moves all IO API into the `Vtk` struct, which should make the documentation easier to 8 | browse and adds some consistency to the API (functions taking self export and static functions 9 | construct a Vtk file via import). 10 | 11 | Additionally, API function names are now more consistent throughout the library (more 12 | specifically between parse and write functions). 13 | 14 | - `parse_vtk_{be|le}` is renamed to `parse_legacy_{be|le}`. 15 | - `parse_vtk_buf_{be|le}` is renamed to `parse_legacy_buf_{be|le}`. 16 | - `import_{be|le}` is renamed to `import_legacy_{be|le}`. 17 | 18 | Support for compression and decompression (feature gated by the "compression" feature which is 19 | enabled by default) are also added. 20 | 21 | - LZMA, LZ4 and Zlib compression are now all supported for appended data blobs. 22 | - Compression level is currently ignored on LZ4 until either the `lz4_flex` crate implements 23 | support, or the `lz4` crate supports LZ4 block format. 24 | - Note that solutions to the above problem should only cause a minor version bump. 25 | 26 | A few new API changes have been made: 27 | 28 | - The VTK file was changed to include an optional `file_path`, which encodes the original path to the 29 | VTK file. This allows relative paths when reading in "parallel" XML files. This is how 30 | ParaView deals with "parallel" XML files for instance. Note that the "parallel" files refers to how 31 | they are defined in the VTK documentation; async file loading is not yet supported, but it is planned. 32 | - `load_piece_data` was renamed to `into_loaded_piece_data` to indicate that this function takes a 33 | piece by value and converts it into concrete piece data. 34 | - In contrast `load_piece_in_place` takes a Piece by mutable reference and replaces source pieces by 35 | their loaded counterparts. 36 | - `load_all_pieces` is added as a convenience function to load "parallel" XML files recursively 37 | in-place. For non-parallel files this function is a no-op. 38 | - The error and display traits are finally implemented on writer errors, which fixes 39 | [#6](https://github.com/elrnv/vtkio/issues/6). 40 | - Added `try_into_xml_format` function to `model::Vtk`, which accepts an additional arguments 41 | indicating the compression level. The `TryFrom` trait defaults to no compression as before. 42 | - The XML API is updated to accept an encoding info (which includes byte order and header 43 | type) when encoding/decoding data. 44 | 45 | 46 | # Release 0.5 47 | 48 | This is a small update to v0.4 that simplifies the Polygon data topology fields. This release also 49 | fixes bugs in parsing and writing files in XML format, especially involving Polygon data. 50 | 51 | In particular the following API was changed: 52 | 53 | - Polygon data topology (`PolyDataTopology`) was previously stored as a `Vec` of enums identifying 54 | the topology type (one of Vertices, Lines, Polygons, or Triangle Strips). This is removed in 55 | favour of storing the specific types of topologies in a dedicated field of the new `PolyDataPiece` 56 | struct. This makes it unambiguous that there is at most one of each topology sections in each 57 | piece. 58 | 59 | - A new parse and write API is introduced for easily reading and writing legacy and xml files from 60 | standard byte buffers (and string slices for ASCII files). 61 | Specifically the functions `parse_vtk_{be|le}`, `parse_vtk_buf_{be|le}`, `parse_xml`, 62 | `write_legacy`, `write_legacy_ascii`, and `write_xml` are added. 63 | 64 | 65 | # Release 0.4 66 | 67 | This release most notably adds support for importing and exporting VTK files in the modern XML 68 | format. 69 | 70 | The XML support is provided via an additional `xml::VTKFile` type, which stores xml specific 71 | information which can be directly serialized and deserialized with `serde` and `quick-xml` into the 72 | corresponding VTK XML file type. 73 | 74 | This means that an additional pass is required to convert the XML type into the underlying 75 | `model::Vtk` type used for vtkio, which unifies legacy and xml I/O and facilitates the lazy loading 76 | of parallel XML formats. Performance sensitive applications may chose to work with the `xml::VTKFile` 77 | type directly, however the `model::Vtk` API is much more comprehensive and easier to work with. 78 | Notably, `xml::VTKFile` stores loaded data buffers as encoded and potentially compressed strings 79 | and byte arrays, whereas `model::Vtk` stores the decoded uncompressed data ready for processing. 80 | 81 | In order to facilitate import and export for both legacy and xml formats, the underlying VTK data 82 | model (`model::Vtk`) was updated. The following outlines most of the critical changes that were made 83 | in version 0.4, particularly in the `model` module: 84 | 85 | - `IOBuffer` was simplified into a simple enum of `Vec`s of different predetermined scalar types. 86 | This means that a number of member functions are now unavailable (and many are unnecessary) on 87 | `IOBuffer`s. 88 | 89 | - a `byte_order` field was added to the `Vtk` model to facilitate automatic determination byte order 90 | on import and export. This means that `import` and `export` functions will automatically encode 91 | data in the byte order provided in that field. Byte order can still be overridden 92 | with `export_{le,be}` or by changing the `byte_order` field in the `Vtk` file directly. 93 | 94 | - `Attribute`s have been completely reformulated to distinguish between Field attributes which are 95 | available only in Legacy VTK formats and the rest. 96 | The distinction between the "kind" of attribute (ColorScalars, Scalars, Vectors, etc.) is 97 | offloaded to a separate enum called `ElementType`. Because XML files don't necessarily need a 98 | "kind" for each attribute, `ElementType` can take on a `Generic` variant, which can represent any 99 | number of components per element. 100 | 101 | - `DataArray`s as well as `Attribute`s are equipped with convenience constructors for creating 102 | arrays with a specific type. 103 | 104 | - `call_numeric_buffer_fn` macro has been removed. A new macro that can tersely abstract over the 105 | type of the underlying vector is `match_buf`, which evaluates an expression with a binding to the 106 | underlying vector. As before this is useful for expressions whose return type is the same 107 | regardless of the type of the vector, otherwise the caller must match on the `IOBuffer` variants 108 | explicitly. 109 | 110 | For example, suppose we had a function 111 | 112 | ```rust 113 | fn display(buf: &IOBuffer) { ... } 114 | ``` 115 | 116 | that printed the buffer in some way. Previously we would need to call this function as follows 117 | 118 | ```rust 119 | call_numeric_buffer_fn!( display<_>(&buf) or {} ); 120 | ``` 121 | 122 | With the current changes, we would need to rewrite the `display` function in terms of a `Vec` 123 | (or slice) instead as follows: 124 | ```rust 125 | fn display(slice: &[T]) { ... } 126 | ``` 127 | 128 | and call it with `match_buf` like so: 129 | 130 | ```rust 131 | match_buf!(&buf, v => display(v.as_slice())); 132 | ``` 133 | which translates to 134 | 135 | ```rust 136 | match &buf { 137 | IOBuffer::Bit(v) => display(v.as_slice()), 138 | IOBuffer::U8(v) => display(v.as_slice()), 139 | IOBuffer::I8(v) => display(v.as_slice()), 140 | IOBuffer::U16(v) => display(v.as_slice()), 141 | IOBuffer::I16(v) => display(v.as_slice()), 142 | IOBuffer::U32(v) => display(v.as_slice()), 143 | IOBuffer::I32(v) => display(v.as_slice()), 144 | IOBuffer::U64(v) => display(v.as_slice()), 145 | IOBuffer::I64(v) => display(v.as_slice()), 146 | IOBuffer::F32(v) => display(v.as_slice()), 147 | IOBuffer::F64(v) => display(v.as_slice()), 148 | } 149 | ``` 150 | 151 | - `DataSet` has been decomposed into `Piece`s to be compatible with the XML vtk format. 152 | Use the `load_piece_data` function to retrieve data corresponding to each piece. 153 | Each `DataSet` can now contain multiple pieces. Each piece can be stored either inline alongside the 154 | data set (as before) or they can be loaded lazily from other referenced vtk files as described in 155 | the parallel XML vtk format. Since each loaded piece is itself a DataSet, there are in total 3 156 | variants of a `Piece`: `Inline`, `Source` and `Loaded`. `Inline` pieces contain the actual piece 157 | data. See the documentation for details. 158 | 159 | - An optional `MetaData` type is attached to a data set to store meta information about the pieces 160 | referenced within. This is only useful when the pieces are not loaded eagerly with the data set. 161 | The parallel XML formats provide the additional meta data, which allows users to set up the data 162 | structures without having to load the actual data. 163 | 164 | - `DataType` was renamed to `ScalarType` to be more consistent with modern VTK formats. The names of 165 | the variants were also renamed to resemble `Rust` numeric types rather than C/C++. 166 | 167 | - The `StructuredPoints` data set variant was renamed to `ImageData` to reflect modern vtk formats. 168 | 169 | - A new `Extent` type was introduced to abstract between Legacy and XML formats. This type describes 170 | the extent of a grid-like structure (i.e. `ImageData`, `StructuredGrid` and `RectilinearGrid` 171 | data set types) or piece. See the documentation for details. 172 | 173 | - The coordinates of the `RectilinearGrid` type were split into a separate struct containing the 174 | individual `x`, `y` and `z` coordinate data arrays. 175 | 176 | - Data arrays in the `Vtk` data structure can be stored in 3 different ways, two of which 177 | specialize the generic `DataArrayBase` struct and one is a plain old `IOBuffer`. 178 | Most data arrays can now be named, with the exception of `Coordinates`, `Points` and `Cells` 179 | arrays, since those are always unique. 180 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vtkio" 3 | version = "0.7.0-rc1" 4 | authors = ["Egor Larionov "] 5 | license = "MIT OR Apache-2.0" 6 | description = "Parser and writer for the legacy VTK file format" 7 | readme = "README.md" 8 | edition = "2021" 9 | 10 | homepage = "https://github.com/elrnv/vtkio" 11 | repository = "https://github.com/elrnv/vtkio" 12 | documentation = "https://docs.rs/vtkio" 13 | 14 | keywords = ["vtk", "parser", "writer", "io", "mesh"] 15 | 16 | [dependencies] 17 | nom = "8" 18 | num-traits = "0.2" 19 | num-derive = "0.4" 20 | byteorder = "1.3" 21 | base64 = "0.22" 22 | bytemuck = { version = "1.5", features = ["extern_crate_alloc"] } 23 | lz4 = { package = "lz4_flex", version = "0.11", optional = true } 24 | flate2 = { version = "1.0.19", optional = true } 25 | xz2 = { version = "0.1.6", optional = true } # LZMA 26 | # quick-xml = { path = "../quick-xml", features = ["serialize"], optional = true } 27 | quick-xml = { git = "https://github.com/elrnv/quick-xml.git", branch = "binary-support4", version = "0.36", features = ["serialize"], optional = true } 28 | serde = { version = "1.0", features = ["derive"], optional = true } 29 | tokio = { version = "1.3", features = ["fs", "io-util"], optional = true } 30 | rayon = { version = "1.0", optional = true } 31 | log = "0.4" 32 | trim-in-place = "0.1" 33 | 34 | [dev-dependencies] 35 | regex = "1.0" 36 | pretty_assertions = "1.0" 37 | env_logger = "0.11" 38 | 39 | [features] 40 | default = ["xml", "compression"] 41 | async = ["tokio"] 42 | compression = ["lz4", "xz2", "flate2"] 43 | parallel = ["rayon"] 44 | xml = ["quick-xml", "serde"] 45 | # binary = [] -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Egor Larionov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vtkio 2 | 3 | A parser and writer for the Visualization Toolkit (VTK) [file 4 | formats](https://kitware.github.io/vtk-examples/site/VTKFileFormats/). 5 | 6 | [![On crates.io](https://img.shields.io/crates/v/vtkio.svg)](https://crates.io/crates/vtkio) 7 | [![On docs.rs](https://docs.rs/vtkio/badge.svg)](https://docs.rs/vtkio/) 8 | [![Build Status](https://github.com/elrnv/vtkio/workflows/CI/badge.svg)](https://github.com/elrnv/vtkio/actions) 9 | 10 | This is meant to be a feature complete parser of Legacy and XML VTK file formats. Both serial and 11 | parallel XML file formats are supported. 12 | 13 | The Legacy format parser is written using [nom](https://crates.io/crates/nom). 14 | XML VTK files are import and exported with [`quick-xml`](https://crates.io/crates/quick-xml) and [`serde`](https://crates.io/crates/serde) crates. 15 | 16 | 17 | # Usage 18 | 19 | To use this library simply add the crate name to your `Cargo.toml` file: 20 | 21 | ```rust 22 | [dependencies] 23 | vtkio = "0.7" 24 | ``` 25 | 26 | 27 | ## Examples 28 | 29 | Many sample files can be found in the `assets` directory. Below are some examples for using this library. 30 | 31 | ### Import/Export 32 | 33 | Below we load a VTK file named `tet.vtk`, modify it and write it back in Legacy ASCII format. 34 | 35 | ```rust 36 | use vtkio::model::*; // import model definition of a VTK file 37 | fn main() { 38 | use std::path::PathBuf; 39 | let file_path = PathBuf::from("assets/tet.vtk"); 40 | 41 | let mut vtk_file = Vtk::import(&file_path) 42 | .expect(&format!("Failed to load file: {:?}", file_path)); 43 | 44 | vtk_file.version = Version::new((4,2)); // arbitrary change 45 | 46 | vtk_file.export_ascii(&file_path) 47 | .expect(&format!("Failed to save file: {:?}", file_path)); 48 | } 49 | ``` 50 | 51 | The next two examples show how to create new `Vtk` instances manually. 52 | 53 | 54 | ### Simple triangular cell 55 | 56 | Here, we create a Vtk instance containing a single triangle represented as a cell of an unstructured grid. 57 | 58 | ```rust 59 | fn make_triangle() -> Vtk { 60 | Vtk { 61 | version: Version { major: 4, minor: 2 }, 62 | title: String::new(), 63 | byte_order: ByteOrder::BigEndian, 64 | file_path: None, 65 | data: DataSet::inline(UnstructuredGridPiece { 66 | points: IOBuffer::F64(vec![ 67 | // coordinates of node 0 68 | -1.0, -1.0, 0.0, 69 | 70 | // coordinates of node 1 71 | 1.0, -1.0, 0.0, 72 | 73 | // coordinates of node 2 74 | 1.0, 1.0, 0.0, 75 | ]), 76 | cells: Cells { 77 | cell_verts: VertexNumbers::XML { 78 | // connect node 0, 1, 2 (in this order) 79 | connectivity: vec![0, 1, 2], 80 | 81 | // only one group of size 3 82 | offsets: vec![3], 83 | }, 84 | // only one cell of type triangle 85 | types: vec![CellType::Triangle; 1], 86 | }, 87 | data: Attributes { 88 | ..Default::default() 89 | }, 90 | }), 91 | } 92 | } 93 | ``` 94 | 95 | 96 | ### Mixing Cell Types 97 | 98 | The following example creates a mesh with a triangle and a quadrilateral. 99 | 100 | ```rust 101 | fn make_mixed_flat_elements() -> Vtk { 102 | Vtk { 103 | version: Version { major: 4, minor: 2 }, 104 | title: String::new(), 105 | byte_order: ByteOrder::BigEndian, 106 | file_path: None, 107 | data: DataSet::inline(UnstructuredGridPiece { 108 | points: IOBuffer::F64(vec![ 109 | -1.0, -1.0, 0.0, 110 | 1.0, -1.0, 0.0, 111 | 1.0, 1.0, 0.0, 112 | -1.0, 1.0, 0.0, 113 | 2.0, -1.0, 0.2, 114 | 2.0, 1.0, 0.2, 115 | ]), 116 | cells: Cells { 117 | cell_verts: VertexNumbers::XML { 118 | connectivity: vec![ 119 | // nodes of triangle 120 | 0, 1, 2, 121 | 122 | // nodes of quadrilateral 123 | 1, 4, 5, 2, 124 | ], 125 | offsets: vec![ 126 | // number of nodes cell 1 127 | 3, 128 | 129 | // number of nodes cell 1 + number of nodes of cell 2 130 | // 3 + 4 = 7 131 | 7 132 | ], 133 | }, 134 | types: vec![ 135 | CellType::Triangle, 136 | CellType::Quad 137 | ], 138 | }, 139 | data: Attributes { 140 | ..Default::default() 141 | }, 142 | }), 143 | } 144 | } 145 | ``` 146 | 147 | ### Extract field data 148 | 149 | Once a Vtk file is read or loaded from a file, it is useful to extract useful data from it. 150 | In the following snippet, an "id" field attached to vertices is extracted from a Vtk struct. 151 | 152 | ```rust 153 | fn extract_id_field(vtk: Vtk) -> Vec { 154 | let pieces = if let DataSet::UnstructuredGrid { pieces, .. } = vtk.data { 155 | pieces 156 | } else { 157 | panic!("Wrong vtk data type"); 158 | }; 159 | 160 | // If piece is already inline, this just returns a piece data clone. 161 | let piece = pieces[0].load_piece_data(None).expect("Failed to load piece data"); 162 | 163 | let attribute = &piece.data.point[0]; 164 | 165 | if let Attribute::Field { data_array, .. } = attribute { 166 | data_array 167 | .iter() 168 | .find(|&DataArrayBase { name, .. }| name == "id") 169 | .expect("Failed to find id field") 170 | .data 171 | .cast_into::() 172 | .expect("Failed cast") 173 | } else { 174 | panic!("No field attribute found"); 175 | } 176 | } 177 | ``` 178 | 179 | 180 | ## Features 181 | 182 | There are two main features available: 183 | 184 | - XML File support via the `xml` feature flag (enabled by default). 185 | This allows importing and exporting VTK files in the modern XML format. If disabled, only the legacy 186 | file format is supported, however the build is faster since it does not include additional 187 | dependencies (`serde` and `quick-xml`) and code needed to parse and write XML files. 188 | - Compression via the `compression` feature flag (enabled by default). 189 | This flag exposes additional APIs to export and import compressed VTK files (only for XML format). 190 | This feature has no benefit when the `xml` feature is disabled. 191 | 192 | To disable the features above simply set `default-features` to `false`. To enable a specific feature 193 | add it to the list under `features`. For instance to disable only the `compression` feature, add the 194 | `vtkio` dependency as 195 | 196 | ```rust 197 | [dependencies] 198 | vtkio = { version = "0.7", default-features = false, features = ["xml"] } 199 | ``` 200 | 201 | To disable all additional features use 202 | 203 | ```rust 204 | [dependencies] 205 | vtkio = { version = "0.7", default-features = false } 206 | ``` 207 | 208 | # Changes 209 | 210 | Version 0.3 of the crate supports only Legacy VTK formats. For a list of changes 211 | introduced in the new versions of `vtkio` (v0.4+) see the [CHANGELOG](CHANGELOG.md). 212 | 213 | # License 214 | 215 | This repository is licensed under either of 216 | 217 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) 218 | * MIT License ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) 219 | 220 | at your option. 221 | 222 | Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in 223 | the Work by You, as defined in the Apache-2.0 license, shall be dual licensed as above, without 224 | any additional terms or conditions. 225 | -------------------------------------------------------------------------------- /assets/RectilinearGrid.pvtr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /assets/RectilinearGrid/RectilinearGrid_0.vtr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | _DAAAAAAAAAAAAAAAAAAAAAAAAAA=DAAAAAAAAAAAAIA/AAAAPwAAgD8=DAAAAAAAAAAAAAAAAAAAADMzsz8=DAAAAAAAAAAAAAAAAAAAAAAAgD8=DAAAAAAAAAAAAAAAAAAAPwAAAAA=IAAAAAAAAAAAAAAAAAAIwAAAAAAAAPC/AAAAAAAA8D8AAAAAAAAIQA==EAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/EAAAAAAAAAAAAAAAAADwvwAAAAAAAPA/ 31 | 32 | 33 | -------------------------------------------------------------------------------- /assets/RectilinearGridAppendedBase64.vtr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | _DAAAAAAAAAAAAAAAAAAAAAAAAAA=DAAAAAAAAAAAAIA/AAAAPwAAgD8=DAAAAAAAAAAAAAAAAAAAADMzsz8=DAAAAAAAAAAAAAAAAAAAAAAAgD8=DAAAAAAAAAAAAAAAAAAAPwAAAAA=IAAAAAAAAAAAAAAAAAAIwAAAAAAAAPC/AAAAAAAA8D8AAAAAAAAIQA==EAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/EAAAAAAAAAAAAAAAAADwvwAAAAAAAPA/ 31 | 32 | 33 | -------------------------------------------------------------------------------- /assets/RectilinearGridCompressed.vtr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | _AQAAAAAAAAAAgAAAAAAAAAwAAAAAAAAACwAAAAAAAAA=eJxjYEAAAAAMAAE=AQAAAAAAAAAAgAAAAAAAAAwAAAAAAAAAEgAAAAAAAAA=eJxjYGiwZ2BgAOIGewAJvQG+AQAAAAAAAAAAgAAAAAAAAAwAAAAAAAAADwAAAAAAAAA=eJxjYIAAY+PN9gADFgFZAQAAAAAAAAAAgAAAAAAAAAwAAAAAAAAADQAAAAAAAAA=eJxjYICBBnsAAUsAwA==AQAAAAAAAAAAgAAAAAAAAAwAAAAAAAAADgAAAAAAAAA=eJxjYAADexABAAFHAEA=AQAAAAAAAAAAgAAAAAAAACAAAAAAAAAAGAAAAAAAAAA=eJxjYAABjgNgiuHDfihtD6E5HAA9JgPvAQAAAAAAAAAAgAAAAAAAABAAAAAAAAAADQAAAAAAAAA=eJxjYEAGH+wBAi8BMA==AQAAAAAAAAAAgAAAAAAAABAAAAAAAAAAEQAAAAAAAAA=eJxjYACBD/sZILQ9ABJGAt8= 31 | 32 | 33 | -------------------------------------------------------------------------------- /assets/RectilinearGridInlineBinary.vtr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | DAAAAAAAAAAAAAAAAAAAAAAAAAA= 9 | 10 | 11 | DAAAAAAAAAAAAIA/AAAAPwAAgD8= 12 | 13 | 14 | DAAAAAAAAAAAAAAAAAAAADMzsz8= 15 | 16 | 17 | DAAAAAAAAAAAAAAAAAAAAAAAgD8= 18 | 19 | 20 | DAAAAAAAAAAAAAAAAAAAPwAAAAA= 21 | 22 | 23 | 24 | 25 | IAAAAAAAAAAAAAAAAAAIwAAAAAAAAPC/AAAAAAAA8D8AAAAAAAAIQA== 26 | 27 | 28 | EAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/ 29 | 30 | 31 | EAAAAAAAAAAAAAAAAADwvwAAAAAAAPA/ 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /assets/RectilinearGridRawBinary.vtr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/RectilinearGridRawBinary.vtr -------------------------------------------------------------------------------- /assets/RectilinearGrid_ascii.vtr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 0 0 0 9 | 10 | 11 | 1 0.5 1 12 | 13 | 14 | 0 0 1.4 15 | 16 | 17 | 0 0 1 18 | 19 | 20 | 0 0.5 0 21 | 22 | 23 | 24 | 25 | -3 -1 1 3 26 | 27 | 28 | 0 1 29 | 30 | 31 | -1 1 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /assets/box.vtu: -------------------------------------------------------------------------------- 1 | AAAAAAAAACC/AAAAvwAAAD8AAAA/AAAAvwAAAL8AAAA/AAAAPwAAAA==AAAAAAAAAGA+TMzNAAAAAD+AAAA+TMzNAAAAAD+AAAAAAAAAP4AAAD3MzM0AAAAAP4AAAD3MzM0+TMzNAAAAAD+AAAA+TMzNAAAAAD+AAAAAAAAAP4AAAD3MzM0AAAAAP4AAAD3MzM0=AAAAAAAAACAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQ==AAAAAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=AAAAAAAAAMA/4KqqoAAAAL/gAAAAAAAAP+AAAAAAAAC/4KqqoAAAAL/gAAAAAAAAP+AAAAAAAAA/4KqqoAAAAD/gAAAAAAAAP+AAAAAAAAC/4KqqoAAAAD/gAAAAAAAAP+AAAAAAAAC/4KqqoAAAAL/gAAAAAAAAv+AAAAAAAAA/4KqqoAAAAL/gAAAAAAAAv+AAAAAAAAC/4KqqoAAAAD/gAAAAAAAAv+AAAAAAAAA/4KqqoAAAAD/gAAAAAAAAv+AAAAAAAAA=AAAAAAAAAMAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAMAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAUAAAAAAAAABwAAAAAAAAAGAAAAAAAAAAYAAAAAAAAABwAAAAAAAAACAAAAAAAAAAMAAAAAAAAABQAAAAAAAAAEAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAAEAAAAAAAAABAAAAAAAAAAGAAAAAAAAAAM=AAAAAAAAADAAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAAUAAAAAAAAABg=AAAAAAAAAAYHBwcHBwc= -------------------------------------------------------------------------------- /assets/box_para.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | IAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAA== 7 | 8 | 9 | YAAAAAAAAADNzEw+AAAAAAAAgD/NzEw+AAAAAAAAgD8AAAAAAACAP83MzD0AAAAAAACAP83MzD3NzEw+AAAAAAAAgD/NzEw+AAAAAAAAgD8AAAAAAACAP83MzD0AAAAAAACAP83MzD0= 10 | 11 | 12 | 1.0049875623 13 | 14 | 15 | 1.0198039033 16 | 17 | 18 | 19 | 20 | 1.0049875623 21 | 22 | 23 | 1.0198039033 24 | 25 | 26 | 27 | 28 | IAAAAAAAAAAAAAC/AAAAvwAAAD8AAAA/AAAAvwAAAL8AAAA/AAAAPw== 29 | 30 | 31 | 32 | 33 | GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= 34 | 35 | 36 | IAEAAAAAAACGSiE/mFCMOgAAAACbgb0+mFCMOgAAAACbgb0+DU2APgAAAACFSiE/Dk2APgAAAACbgb0+ROc/PwAAAACFSiE/ROc/PwAAAACFSiE/5QYAPwAAAACcgb0+5QYAPwAAAACcgb0+5QYAPwAAAACFSiE/5QYAPwAAAACFSiE/Dk2APgAAAACbgb0+DU2APgAAAACFSiE/ROc/PwAAAACbgb0+ROc/PwAAAACbgb0+o8d/PwAAAACGSiE/o8d/PwAAAADkKmE/5QYAPwAAAADkKmE/Dk2APgAAAACFSiE/Dk2APgAAAACFSiE/5QYAPwAAAAB8A/c9Dk2APgAAAAB4A/c95QYAPwAAAACcgb0+5QYAPwAAAACbgb0+DU2APgAAAAA= 37 | 38 | 39 | 0.2781035205 40 | 41 | 42 | 1.1812008038 43 | 44 | 45 | 46 | 47 | 0.2781035205 48 | 49 | 50 | 1.1812008038 51 | 52 | 53 | 54 | 55 | 56 | 57 | wAAAAAAAAAAAAACgqqrgPwAAAAAAAOC/AAAAAAAA4D8AAACgqqrgvwAAAAAAAOC/AAAAAAAA4D8AAACgqqrgPwAAAAAAAOA/AAAAAAAA4D8AAACgqqrgvwAAAAAAAOA/AAAAAAAA4D8AAACgqqrgvwAAAAAAAOC/AAAAAAAA4L8AAACgqqrgPwAAAAAAAOC/AAAAAAAA4L8AAACgqqrgvwAAAAAAAOA/AAAAAAAA4L8AAACgqqrgPwAAAAAAAOA/AAAAAAAA4L8= 58 | 59 | 60 | 0.87821827607 61 | 62 | 63 | 0.87821827607 64 | 65 | 66 | 67 | 68 | 0.87821827607 69 | 70 | 71 | 0.87821827607 72 | 73 | 74 | 75 | 76 | 77 | 78 | wAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAwAAAAAAAAACAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAHAAAAAAAAAAYAAAAAAAAABgAAAAAAAAAHAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAFAAAAAAAAAAQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAcAAAAAAAAAAQAAAAAAAAAEAAAAAAAAAAYAAAAAAAAAAwAAAAAAAAA= 79 | 80 | 81 | MAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAA= 82 | 83 | 84 | BgAAAAAAAAAHBwcHBwc= 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /assets/cube.pvtp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /assets/cube.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 4.2 2 | Cube example 3 | ASCII 4 | DATASET UNSTRUCTURED_GRID 5 | POINTS 8 float 6 | 0 0 0 7 | 0 0 -1 8 | 0 1 0 9 | 0 1 -1 10 | 1 0 0 11 | 1 0 -1 12 | 1 1 0 13 | 1 1 -1 14 | 15 | CELLS 1 9 16 | 8 0 4 5 1 2 6 7 3 17 | 18 | CELL_TYPES 1 19 | 12 20 | -------------------------------------------------------------------------------- /assets/cube_complex.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 2.0 2 | Cube example 3 | ASCII 4 | DATASET POLYDATA 5 | POINTS 8 float 6 | 0.0 0.0 0.0 7 | 1.0 0.0 0.0 8 | 1.0 1.0 0.0 9 | 0.0 1.0 0.0 10 | 0.0 0.0 1.0 11 | 1.0 0.0 1.0 12 | 1.0 1.0 1.0 13 | 0.0 1.0 1.0 14 | POLYGONS 6 30 15 | 4 0 1 2 3 16 | 4 4 5 6 7 17 | 4 0 1 5 4 18 | 4 2 3 7 6 19 | 4 0 4 7 3 20 | 4 1 2 6 5 21 | 22 | CELL_DATA 6 23 | SCALARS cell_scalars int 1 24 | LOOKUP_TABLE default 25 | 0 26 | 1 27 | 2 28 | 3 29 | 4 30 | 5 31 | NORMALS cell_normals float 32 | 0 0 -1 33 | 0 0 1 34 | 0 -1 0 35 | 0 1 0 36 | -1 0 0 37 | 1 0 0 38 | FIELD FieldData 2 39 | cellIds 1 6 int 40 | 0 1 2 3 4 5 41 | faceAttributes 2 6 float 42 | 0.0 1.0 1.0 2.0 2.0 3.0 3.0 4.0 4.0 5.0 5.0 6.0 43 | 44 | POINT_DATA 8 45 | SCALARS sample_scalars float 1 46 | LOOKUP_TABLE my_table 47 | 0.0 48 | 1.0 49 | 2.0 50 | 3.0 51 | 4.0 52 | 5.0 53 | 6.0 54 | 7.0 55 | LOOKUP_TABLE my_table 8 56 | 0.0 0.0 0.0 1.0 57 | 1.0 0.0 0.0 1.0 58 | 0.0 1.0 0.0 1.0 59 | 1.0 1.0 0.0 1.0 60 | 0.0 0.0 1.0 1.0 61 | 1.0 0.0 1.0 1.0 62 | 0.0 1.0 1.0 1.0 63 | 1.0 1.0 1.0 1.0 64 | -------------------------------------------------------------------------------- /assets/cube_complex_topo.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 2.0 2 | Cube example 3 | ASCII 4 | DATASET POLYDATA 5 | POINTS 8 float 6 | 0.0 0.0 0.0 7 | 1.0 0.0 0.0 8 | 1.0 1.0 0.0 9 | 0.0 1.0 0.0 10 | 0.0 0.0 1.0 11 | 1.0 0.0 1.0 12 | 1.0 1.0 1.0 13 | 0.0 1.0 1.0 14 | POLYGONS 6 30 15 | 4 0 1 2 3 16 | 4 4 5 6 7 17 | 4 0 1 5 4 18 | 4 2 3 7 6 19 | 4 0 4 7 3 20 | 4 1 2 6 5 21 | VERTICES 2 6 22 | 2 0 1 23 | 2 2 3 24 | 25 | CELL_DATA 8 26 | SCALARS cell_scalars int 1 27 | LOOKUP_TABLE default 28 | 0 29 | 1 30 | 2 31 | 3 32 | 4 33 | 5 34 | 6 35 | 7 36 | NORMALS cell_normals float 37 | 0 0 -1 38 | 0 0 1 39 | 0 -1 0 40 | 0 1 0 41 | -1 0 0 42 | 1 0 0 43 | 1 0 0 44 | 1 0 0 45 | FIELD FieldData 2 46 | cellIds 1 8 int 47 | 0 1 2 3 4 5 7 8 48 | faceAttributes 2 8 float 49 | 0.0 1.0 1.0 2.0 2.0 3.0 3.0 4.0 4.0 5.0 5.0 6.0 6.0 6.0 7.0 7.0 50 | 51 | POINT_DATA 8 52 | SCALARS sample_scalars float 1 53 | LOOKUP_TABLE my_table 54 | 0.0 55 | 1.0 56 | 2.0 57 | 3.0 58 | 4.0 59 | 5.0 60 | 6.0 61 | 7.0 62 | LOOKUP_TABLE my_table 8 63 | 0.0 0.0 0.0 1.0 64 | 1.0 0.0 0.0 1.0 65 | 0.0 1.0 0.0 1.0 66 | 1.0 1.0 0.0 1.0 67 | 0.0 0.0 1.0 1.0 68 | 1.0 0.0 1.0 1.0 69 | 0.0 1.0 1.0 1.0 70 | 1.0 1.0 1.0 1.0 71 | -------------------------------------------------------------------------------- /assets/dodecagon.vtk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/dodecagon.vtk -------------------------------------------------------------------------------- /assets/dodecagon_ascii.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 4.2 2 | Dodecagon example 3 | ASCII 4 | DATASET UNSTRUCTURED_GRID 5 | POINTS 12 float 6 | 0.5 0 0 0.433013 0 -0.25 0.25 0 -0.433013 7 | 3.06162e-17 0 -0.5 -0.25 0 -0.433013 -0.433013 0 -0.25 8 | -0.5 0 -6.12323e-17 -0.433013 0 0.25 -0.25 0 0.433013 9 | -9.18485e-17 0 0.5 0.25 0 0.433013 0.433013 0 0.25 10 | 11 | METADATA 12 | INFORMATION 1 13 | NAME L2_NORM_RANGE LOCATION vtkDataArray 14 | DATA 2 0.5 0.5 15 | 16 | CELLS 1 13 17 | 12 0 1 2 3 4 5 6 7 8 9 10 11 18 | 19 | CELL_TYPES 1 20 | 7 21 | 22 | -------------------------------------------------------------------------------- /assets/dodecagon_ascii_simple.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 4.2 2 | Dodecagon example 3 | ASCII 4 | DATASET UNSTRUCTURED_GRID 5 | POINTS 12 float 6 | 0.5 0 0 0.433013 0 -0.25 0.25 0 -0.433013 7 | 3.06162e-17 0 -0.5 -0.25 0 -0.433013 -0.433013 0 -0.25 8 | -0.5 0 -6.12323e-17 -0.433013 0 0.25 -0.25 0 0.433013 9 | -9.18485e-17 0 0.5 0.25 0 0.433013 0.433013 0 0.25 10 | 11 | CELLS 1 13 12 | 12 0 1 2 3 4 5 6 7 8 9 10 11 13 | 14 | CELL_TYPES 1 15 | 7 16 | 17 | -------------------------------------------------------------------------------- /assets/dodecagon_simple.vtk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/dodecagon_simple.vtk -------------------------------------------------------------------------------- /assets/field.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 2.0 2 | field example 3 | ASCII 4 | 5 | FIELD FieldData 2 6 | cellIds 1 6 int 7 | 0 1 2 3 4 5 8 | faceAttributes 2 6 float 9 | 0.0 1.0 1.0 2.0 2.0 3.0 3.0 4.0 4.0 5.0 5.0 6.0 10 | -------------------------------------------------------------------------------- /assets/hexahedron.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 0 14 | 15 | 16 | 1.7320508076 17 | 18 | 19 | 20 | 21 | 0 22 | 23 | 24 | 1.7320508076 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | _YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgL8AAAAAAACAPwAAAAAAAAAAAACAPwAAgL8AAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAIA/AACAPwAAAAAAAIA/AACAPwAAgL8=QAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABQAAAAAAAAABAAAAAAAAAAIAAAAAAAAABgAAAAAAAAAHAAAAAAAAAAMAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA==AQAAAAAAAAAM 38 | 39 | 40 | -------------------------------------------------------------------------------- /assets/hexahedron_ascii.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 40 41 42 43 44 45 46 47 7 | 8 | 9 | 40 41 42 43 44 45 46 47 10 | 11 | 12 | 40 41 42 43 44 45 46 47 13 | 14 | 15 | 40 41 42 43 44 45 46 47 16 | 17 | 18 | 1 2 4 8 16 32 64 128 19 | 20 | 21 | 1 2 4 8 16 32 64 128 22 | 23 | 24 | 25 | 26 | 40 27 | 28 | 29 | 41 30 | 31 | 32 | 42 33 | 34 | 35 | 43 36 | 37 | 38 | 256 39 | 40 | 41 | 1024 42 | 43 | 44 | 45 | 46 | 0 0 0 0 0 -1 47 | 0 1 0 0 1 -1 48 | 1 0 0 1 0 -1 49 | 1 1 0 1 1 -1 50 | 51 | 52 | 53 | 54 | 0 4 5 1 2 6 55 | 7 3 56 | 57 | 58 | 8 59 | 60 | 61 | 12 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /assets/hexahedron_binary.vtu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/hexahedron_binary.vtu -------------------------------------------------------------------------------- /assets/hexahedron_inline_binary.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CAAAAAAAAAAoKSorLC0uLw== 7 | 8 | 9 | EAAAAAAAAAAoACkAKgArACwALQAuAC8A 10 | 11 | 12 | IAAAAAAAAAAoAAAAKQAAACoAAAArAAAALAAAAC0AAAAuAAAALwAAAA== 13 | 14 | 15 | IAAAAAAAAAAoAAAAKQAAACoAAAArAAAALAAAAC0AAAAuAAAALwAAAA== 16 | 17 | 18 | IAAAAAAAAAAAAIA/AAAAQAAAgEAAAABBAACAQQAAAEIAAIBCAAAAQw== 19 | 20 | 21 | QAAAAAAAAAAAAAAAAADwPwAAAAAAAABAAAAAAAAAEEAAAAAAAAAgQAAAAAAAADBAAAAAAAAAQEAAAAAAAABQQAAAAAAAAGBA 22 | 23 | 24 | 25 | 26 | AQAAAAAAAAAo 27 | 28 | 29 | AgAAAAAAAAApAA== 30 | 31 | 32 | BAAAAAAAAAAqAAAA 33 | 34 | 35 | BAAAAAAAAAArAAAA 36 | 37 | 38 | BAAAAAAAAAAAAIBD 39 | 40 | 41 | CAAAAAAAAAAAAAAAAACQQA== 42 | 43 | 44 | 45 | 46 | YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgL8AAAAAAACAPwAAAAAAAAAAAACAPwAAgL8AAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAIA/AACAPwAAAAAAAIA/AACAPwAAgL8= 47 | 48 | 49 | 0 50 | 51 | 52 | 1.7320508076 53 | 54 | 55 | 56 | 57 | 0 58 | 59 | 60 | 1.7320508076 61 | 62 | 63 | 64 | 65 | 66 | 67 | QAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABQAAAAAAAAABAAAAAAAAAAIAAAAAAAAABgAAAAAAAAAHAAAAAAAAAAMAAAAAAAAA 68 | 69 | 70 | CAAAAAAAAAAIAAAAAAAAAA== 71 | 72 | 73 | AQAAAAAAAAAM 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /assets/hexahedron_lz4.vtu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/hexahedron_lz4.vtu -------------------------------------------------------------------------------- /assets/hexahedron_lz4_inline_binary.vtu: -------------------------------------------------------------------------------- 1 | AQAAAAAAAAAAgAAAAAAAAGAAAAAAAAAAMQAAAAAAAAA=HwABAAIigL8XACKAPwgAAAIAAAwAABgAAAgABAIABAwABBgABRAAkD8AAIA/AACAvw==AQAAAAAAAAAAgAAAAAAAAEAAAAAAAAAAJQAAAAAAAAA=EwABABMECAATBQgAEwEIABMCCAATBggAEwcIAIADAAAAAAAAAA==AQAAAAAAAAAAgAAAAAAAAAgAAAAAAAAACQAAAAAAAAA=gAgAAAAAAAAAAQAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAA=EAw= -------------------------------------------------------------------------------- /assets/hexahedron_lzma_inline_binary.vtu: -------------------------------------------------------------------------------- 1 | AQAAAAAAAAAAgAAAAAAAAGAAAAAAAAAAVAAAAAAAAAA=/Td6WFoAAATm1rRGAgAhARwAAAAQz1jM4ABfABhdAABuBMhFyK1W9uD9tSdenv6myo1MgjQtAABHAGHxnRm3vgABNGCEJx7WH7bzfQEAAAAABFlaAQAAAAAAAAAAgAAAAAAAAEAAAAAAAAAAUAAAAAAAAAA=/Td6WFoAAATm1rRGAgAhARwAAAAQz1jM4AA/ABRdAABqf3sRcnVE9tuf42GTc5gyx1nDACXDXi+PtgXxAAEwQEjCHIkftvN9AQAAAAAEWVo=AQAAAAAAAAAAgAAAAAAAAAgAAAAAAAAAQAAAAAAAAAA=/Td6WFoAAATm1rRGAgAhARwAAAAQz1jMAQAHCAAAAAAAAAAA1EqAwrVi/CgAASAIuxnZux+2830BAAAAAARZWg==AQAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAPAAAAAAAAAA=/Td6WFoAAATm1rRGAgAhARwAAAAQz1jMAQAADAAAAAAM3fZk/uXckwABGQGlLIHMH7bzfQEAAAAABFla -------------------------------------------------------------------------------- /assets/hexahedron_parallel.pvtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/hexahedron_parallel_0.vtu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/hexahedron_parallel_0.vtu -------------------------------------------------------------------------------- /assets/hexahedron_parallel_lzma.pvtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/hexahedron_parallel_lzma_0.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 0 14 | 15 | 16 | 1.7320508076 17 | 18 | 19 | 20 | 21 | 0 22 | 23 | 24 | 1.7320508076 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | _AQAAAACAAABgAAAAUAAAAA==/Td6WFoAAAFpIt42AsAgYCEBHADVyr+K4ABfABhdAABuBMhFyK1W9uD9tSdenv6myo1MgjQtAAAt9fkxAAEwYIDicrKQQpkNAQAAAAABWVo=AQAAAACAAABAAAAATAAAAA==/Td6WFoAAAFpIt42AsAcQCEBHAAHIsY44AA/ABRdAABqf3sRcnVE9tuf42GTc5gyx1nDAFJBZZwAASxAFZ9rb5BCmQ0BAAAAAAFZWg==AQAAAACAAAAIAAAAPAAAAA==/Td6WFoAAAFpIt42AsAMCCEBHAAUM5NTAQAHCAAAAAAAAAAA3MTHtgABHAhEYCrIkEKZDQEAAAAAAVlaAQAAAACAAAABAAAAOAAAAA==/Td6WFoAAAFpIt42AsAFASEBHACtAIx5AQAADAAAAACmo7TbAAEVAaljNGCQQpkNAQAAAAABWVo= 38 | 39 | 40 | -------------------------------------------------------------------------------- /assets/hexahedron_zlib.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 0 14 | 15 | 16 | 1.7320508076 17 | 18 | 19 | 20 | 21 | 0 22 | 23 | 24 | 1.7320508076 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | _AQAAAAAAAAAAgAAAAAAAAGAAAAAAAAAAIAAAAAAAAAA=eNpjYMAGGvZDaXskMXuIOLoYTD1Y3h5JLVg9AHfjCvU=AQAAAAAAAAAAgAAAAAAAAEAAAAAAAAAAHgAAAAAAAAA=eNpjYIAAFijNCqUZoTQTlGaD0uxQmhlKAwADkAAdAQAAAAAAAAAAgAAAAAAAAAgAAAAAAAAACwAAAAAAAAA=eNrjYIAAAABIAAk=AQAAAAAAAAAAgAAAAAAAAAEAAAAAAAAACQAAAAAAAAA=eNrjAQAADQAN 38 | 39 | 40 | -------------------------------------------------------------------------------- /assets/hexahedron_zlib_binary.vtu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/hexahedron_zlib_binary.vtu -------------------------------------------------------------------------------- /assets/hexahedron_zlib_inline_binary.vtu: -------------------------------------------------------------------------------- 1 | AQAAAAAAAAAAgAAAAAAAAGAAAAAAAAAAKQAAAAAAAAA=eJxtisEJAAAIAt3M1dqs1QIrkMiPcHfAt8h5GmPzy7aXp7XqC3fjCvU=AQAAAAAAAAAAgAAAAAAAAEAAAAAAAAAAHwAAAAAAAAA=eJwtxTcBAAAMw7B082fcI9YjyZqHg5OXj4sfA5AAHQ==AQAAAAAAAAAAgAAAAAAAAAgAAAAAAAAACwAAAAAAAAA=eJzjYIAAAABIAAk=AQAAAAAAAAAAgAAAAAAAAAEAAAAAAAAACQAAAAAAAAA=eJzjAQAADQAN -------------------------------------------------------------------------------- /assets/hexahedron_zlib_para.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | AQAAAAAAAAAAgAAAAAAAAGAAAAAAAAAAIwAAAAAAAAA=eAFjYMAGGvZDRBvsEbIgNkgcXQykAiYOkwOrBasHAHfjCvU= 12 | 13 | 14 | 0 15 | 16 | 17 | 1.7320508076 18 | 19 | 20 | 21 | 22 | 0 23 | 24 | 25 | 1.7320508076 26 | 27 | 28 | 29 | 30 | 31 | 32 | AQAAAAAAAAAAgAAAAAAAAEAAAAAAAAAAHgAAAAAAAAA=eAFjYIAAFijNCqUZoTQTlGaD0uxQmhlKAwADkAAd 33 | 34 | 35 | AQAAAAAAAAAAgAAAAAAAAAgAAAAAAAAACwAAAAAAAAA=eAHjYIAAAABIAAk= 36 | 37 | 38 | AQAAAAAAAAAAgAAAAAAAAAEAAAAAAAAACQAAAAAAAAA=eAHjAQAADQAN 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /assets/para_test.vtk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/para_test.vtk -------------------------------------------------------------------------------- /assets/para_test_ascii.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 4.2 2 | vtk output 3 | ASCII 4 | DATASET UNSTRUCTURED_GRID 5 | POINTS 12 double 6 | 13.2 135.4 -7.7 13.7 134.2 -8.7 12.2 134.7 -8.6 7 | 12.7 133.6 -7.0 3.6 119.4 -0.3 -2.3 137.0 -2.5 8 | 5.4 119.7 0.0 -2.7 135.9 -1.2 -2.9 137.5 -1.2 9 | -1.8 136.6 -1.7 4.3 119.7 0.4 4.6 118.7 -0.002 10 | 11 | CELLS 3 15 12 | 4 9 5 7 8 13 | 4 3 2 0 1 14 | 4 11 6 4 10 15 | 16 | CELL_TYPES 3 17 | 10 18 | 10 19 | 10 20 | 21 | CELL_DATA 3 22 | FIELD FieldData 2 23 | Ones 1 3 float 24 | 1 1 1 25 | METADATA 26 | INFORMATION 0 27 | 28 | Zeros 1 3 float 29 | 0 0 0 30 | METADATA 31 | INFORMATION 0 32 | 33 | POINT_DATA 12 34 | FIELD FieldData 5 35 | Zeros 1 12 float 36 | 0 0 0 0 0 0 0 0 0 37 | 0 0 0 38 | METADATA 39 | INFORMATION 0 40 | 41 | Floats 1 12 float 42 | 2.3 2.5 2.3 2.1 1.4 0.8 1.6 0.7 0.8 43 | 0.7 1.5 1.6 44 | METADATA 45 | INFORMATION 0 46 | 47 | Ints 1 12 int 48 | 1 1 1 0 0 0 0 0 0 49 | 0 0 0 50 | METADATA 51 | INFORMATION 0 52 | 53 | NegativeInts 1 12 int 54 | -1 -1 -1 -1 -1 -1 -1 -1 -1 55 | -1 -1 -1 56 | METADATA 57 | INFORMATION 0 58 | 59 | MixedInts 1 12 int 60 | 2 -1 3 -1 -1 -1 -1 -1 -1 61 | 0 -1 1 62 | METADATA 63 | INFORMATION 0 64 | 65 | -------------------------------------------------------------------------------- /assets/para_tet.vtk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/para_tet.vtk -------------------------------------------------------------------------------- /assets/para_tet_ascii.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 4.2 2 | vtk output 3 | ASCII 4 | DATASET UNSTRUCTURED_GRID 5 | POINTS 4 double 6 | 0 0 0 0 0 -1 0 1 0 7 | 1 0 0 8 | METADATA 9 | INFORMATION 1 10 | NAME L2_NORM_RANGE LOCATION vtkDataArray 11 | DATA 2 0 1 12 | 13 | CELLS 1 5 14 | 4 0 1 2 3 15 | 16 | CELL_TYPES 1 17 | 10 18 | 19 | CELL_DATA 1 20 | FIELD FieldData 1 21 | FloatValue 1 1 float 22 | 0 23 | METADATA 24 | INFORMATION 0 25 | 26 | -------------------------------------------------------------------------------- /assets/point.vtp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | DAAAAAAAAAAAAAAAAAAAAAAAAAA= 11 | 12 | 13 | 14 | 15 | CAAAAAAAAAAAAAAAAAAAAA== 16 | 17 | 18 | CAAAAAAAAAABAAAAAAAAAA== 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /assets/point_ascii.vtp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 0 0 0 11 | 12 | 13 | 14 | 15 | 0 16 | 17 | 18 | 1 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /assets/point_cloud.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 2.0 2 | PointCloud example 3 | ASCII 4 | 5 | DATASET POLYDATA 6 | POINTS 3 float 7 | 0 0 0 1 0 0 0 0 -1 8 | 9 | VERTICES 3 6 10 | 1 0 11 | 1 1 12 | 1 2 13 | 14 | POINT_DATA 3 15 | 16 | CELL_DATA 3 17 | 18 | -------------------------------------------------------------------------------- /assets/point_cloud.vtp: -------------------------------------------------------------------------------- 1 | AAAAAAAAACQAAAAAAAAAAAAAAAA/gAAAAAAAAAAAAAAAAAAAAAAAAL+AAAA=AAAAAAAAABgAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAI=AAAAAAAAABgAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAM= -------------------------------------------------------------------------------- /assets/polyEx0.vtp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 0 0 0 1 0 0 1 1 0 0 1 0 0 0 1 1 0 1 1 1 1 0 1 1 9 | 10 | 11 | 12 | 13 | 0 1 2 3 4 5 6 7 14 | 15 | 16 | 17 | 18 | 0 1 2 3 4 5 19 | 20 | 22 | 0 0 -1 0 0 1 0 -1 0 0 1 0 -1 0 0 1 0 0 23 | 24 | 25 | 26 | 27 | 0 1 2 3 4 5 6 7 0 1 5 4 2 3 7 6 0 4 7 3 1 2 6 5 28 | 29 | 30 | 4 8 12 16 20 24 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /assets/pygmsh/ascii.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 5.1 2 | written by meshio v5.3.0 3 | ASCII 4 | DATASET UNSTRUCTURED_GRID 5 | POINTS 18 double 6 | 0.0 0.0 0.0 1.0 -0.2 0.0 1.1 1.2 0.0 0.1 0.7 0.0 0.3333333333325021 -0.06666666666650042 0.0 0.6666666666657866 -0.1333333333331573 0.0 1.0249999999999424 0.14999999999919245 0.0 1.0499999999998704 0.4999999999981836 0.0 1.074999999999934 0.8499999999990746 0.0 0.766666666667985 1.0333333333339925 0.0 0.433333333334733 0.8666666666673664 0.0 0.050000000000122564 0.3500000000008579 0.0 0.7444729167676052 0.3524793413776178 0.0 0.3781088913238718 0.4816987298113132 0.0 0.7412636346823331 0.6806963451979247 0.0 0.5070791452210437 0.16277273408010906 0.0 0.253704273975508 0.18556095944515594 0.0 0.7797139636550688 0.08823831456107314 0.0 7 | CELLS 39 94 8 | OFFSETS vtktypeint64 9 | 0 10 | 2 11 | 4 12 | 6 13 | 8 14 | 10 15 | 12 16 | 14 17 | 16 18 | 18 19 | 20 20 | 22 21 | 24 22 | 27 23 | 30 24 | 33 25 | 36 26 | 39 27 | 42 28 | 45 29 | 48 30 | 51 31 | 54 32 | 57 33 | 60 34 | 63 35 | 66 36 | 69 37 | 72 38 | 75 39 | 78 40 | 81 41 | 84 42 | 87 43 | 90 44 | 91 45 | 92 46 | 93 47 | 94 48 | CONNECTIVITY vtktypeint64 49 | 0 50 | 4 51 | 4 52 | 5 53 | 5 54 | 1 55 | 1 56 | 6 57 | 6 58 | 7 59 | 7 60 | 8 61 | 8 62 | 2 63 | 2 64 | 9 65 | 9 66 | 10 67 | 10 68 | 3 69 | 3 70 | 11 71 | 11 72 | 0 73 | 10 74 | 13 75 | 14 76 | 13 77 | 12 78 | 14 79 | 10 80 | 3 81 | 13 82 | 8 83 | 2 84 | 9 85 | 4 86 | 5 87 | 15 88 | 9 89 | 10 90 | 14 91 | 3 92 | 11 93 | 13 94 | 6 95 | 7 96 | 12 97 | 8 98 | 9 99 | 14 100 | 7 101 | 8 102 | 14 103 | 12 104 | 7 105 | 14 106 | 15 107 | 5 108 | 17 109 | 5 110 | 1 111 | 17 112 | 1 113 | 6 114 | 17 115 | 12 116 | 13 117 | 15 118 | 13 119 | 11 120 | 16 121 | 15 122 | 13 123 | 16 124 | 11 125 | 0 126 | 16 127 | 0 128 | 4 129 | 16 130 | 6 131 | 12 132 | 17 133 | 12 134 | 15 135 | 17 136 | 4 137 | 15 138 | 16 139 | 0 140 | 1 141 | 2 142 | 3 143 | CELL_TYPES 38 144 | 3 145 | 3 146 | 3 147 | 3 148 | 3 149 | 3 150 | 3 151 | 3 152 | 3 153 | 3 154 | 3 155 | 3 156 | 5 157 | 5 158 | 5 159 | 5 160 | 5 161 | 5 162 | 5 163 | 5 164 | 5 165 | 5 166 | 5 167 | 5 168 | 5 169 | 5 170 | 5 171 | 5 172 | 5 173 | 5 174 | 5 175 | 5 176 | 5 177 | 5 178 | 1 179 | 1 180 | 1 181 | 1 182 | -------------------------------------------------------------------------------- /assets/pygmsh/ascii.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 0.00000000000e+00 9 | 0.00000000000e+00 10 | 0.00000000000e+00 11 | 1.00000000000e+00 12 | -2.00000000000e-01 13 | 0.00000000000e+00 14 | 1.10000000000e+00 15 | 1.20000000000e+00 16 | 0.00000000000e+00 17 | 1.00000000000e-01 18 | 7.00000000000e-01 19 | 0.00000000000e+00 20 | 3.33333333333e-01 21 | -6.66666666665e-02 22 | 0.00000000000e+00 23 | 6.66666666666e-01 24 | -1.33333333333e-01 25 | 0.00000000000e+00 26 | 1.02500000000e+00 27 | 1.49999999999e-01 28 | 0.00000000000e+00 29 | 1.05000000000e+00 30 | 4.99999999998e-01 31 | 0.00000000000e+00 32 | 1.07500000000e+00 33 | 8.49999999999e-01 34 | 0.00000000000e+00 35 | 7.66666666668e-01 36 | 1.03333333333e+00 37 | 0.00000000000e+00 38 | 4.33333333335e-01 39 | 8.66666666667e-01 40 | 0.00000000000e+00 41 | 5.00000000001e-02 42 | 3.50000000001e-01 43 | 0.00000000000e+00 44 | 7.44472916768e-01 45 | 3.52479341378e-01 46 | 0.00000000000e+00 47 | 3.78108891324e-01 48 | 4.81698729811e-01 49 | 0.00000000000e+00 50 | 7.41263634682e-01 51 | 6.80696345198e-01 52 | 0.00000000000e+00 53 | 5.07079145221e-01 54 | 1.62772734080e-01 55 | 0.00000000000e+00 56 | 2.53704273976e-01 57 | 1.85560959445e-01 58 | 0.00000000000e+00 59 | 7.79713963655e-01 60 | 8.82383145611e-02 61 | 0.00000000000e+00 62 | 63 | 64 | 65 | 66 | 67 | 0 68 | 4 69 | 4 70 | 5 71 | 5 72 | 1 73 | 1 74 | 6 75 | 6 76 | 7 77 | 7 78 | 8 79 | 8 80 | 2 81 | 2 82 | 9 83 | 9 84 | 10 85 | 10 86 | 3 87 | 3 88 | 11 89 | 11 90 | 0 91 | 10 92 | 13 93 | 14 94 | 13 95 | 12 96 | 14 97 | 10 98 | 3 99 | 13 100 | 8 101 | 2 102 | 9 103 | 4 104 | 5 105 | 15 106 | 9 107 | 10 108 | 14 109 | 3 110 | 11 111 | 13 112 | 6 113 | 7 114 | 12 115 | 8 116 | 9 117 | 14 118 | 7 119 | 8 120 | 14 121 | 12 122 | 7 123 | 14 124 | 15 125 | 5 126 | 17 127 | 5 128 | 1 129 | 17 130 | 1 131 | 6 132 | 17 133 | 12 134 | 13 135 | 15 136 | 13 137 | 11 138 | 16 139 | 15 140 | 13 141 | 16 142 | 11 143 | 0 144 | 16 145 | 0 146 | 4 147 | 16 148 | 6 149 | 12 150 | 17 151 | 12 152 | 15 153 | 17 154 | 4 155 | 15 156 | 16 157 | 0 158 | 1 159 | 2 160 | 3 161 | 162 | 163 | 164 | 2 165 | 4 166 | 6 167 | 8 168 | 10 169 | 12 170 | 14 171 | 16 172 | 18 173 | 20 174 | 22 175 | 24 176 | 27 177 | 30 178 | 33 179 | 36 180 | 39 181 | 42 182 | 45 183 | 48 184 | 51 185 | 54 186 | 57 187 | 60 188 | 63 189 | 66 190 | 69 191 | 72 192 | 75 193 | 78 194 | 81 195 | 84 196 | 87 197 | 90 198 | 91 199 | 92 200 | 93 201 | 94 202 | 203 | 204 | 205 | 3 206 | 3 207 | 3 208 | 3 209 | 3 210 | 3 211 | 3 212 | 3 213 | 3 214 | 3 215 | 3 216 | 3 217 | 5 218 | 5 219 | 5 220 | 5 221 | 5 222 | 5 223 | 5 224 | 5 225 | 5 226 | 5 227 | 5 228 | 5 229 | 5 230 | 5 231 | 5 232 | 5 233 | 5 234 | 5 235 | 5 236 | 5 237 | 5 238 | 5 239 | 1 240 | 1 241 | 1 242 | 1 243 | 244 | 245 | 246 | 247 | 248 | 249 | -------------------------------------------------------------------------------- /assets/pygmsh/binary.vtk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/pygmsh/binary.vtk -------------------------------------------------------------------------------- /assets/pygmsh/gen.py: -------------------------------------------------------------------------------- 1 | import pygmsh 2 | 3 | with pygmsh.geo.Geometry() as geom: 4 | p = geom.add_polygon( 5 | [ 6 | [0.0, 0.0], 7 | [1.0, -0.2], 8 | [1.1, 1.2], 9 | [0.1, 0.7], 10 | ], 11 | mesh_size=0.4, 12 | ) 13 | geom.add_physical(p.lines[0], label="bottom") 14 | geom.add_physical(p.lines[1], label="right") 15 | geom.add_physical(p.lines[2], label="top") 16 | geom.add_physical(p.lines[3], label="left") 17 | 18 | mesh = geom.generate_mesh() 19 | 20 | mesh.write("no-compression.vtu", compression=None) 21 | mesh.write("lzma.vtu", compression="lzma") 22 | mesh.write("zlib.vtu", compression="zlib") 23 | mesh.write("ascii.vtu", binary=False) 24 | mesh.write("binary.vtk") 25 | mesh.write("ascii.vtk", binary=False) 26 | -------------------------------------------------------------------------------- /assets/pygmsh/lzma.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | AQAAAACAAACwAQAALAEAAA==/Td6WFoAAATm1rRGAgAhARYAAAB0L+Wj4AGvAOldAABuFTfD5hQGEWOiYOj3Slyvfg1L/Z3iyRycR0v7+mOn0ba3Qa6+uxHTkBa13rJ9UW0iFhj//utywp06/cuuBp7r/6RF/0kQoQF1wJmUOfjtXDwO7oyM72B1k1bOtSrX7Q0EP0RdrG6mYpB8fY3uRXcPLRuV7F4NlhoBsuNcKUphVnWEjbr0xN4z91QfOD8gJGvTgxj+c6oSBQZKgdtigQALByl5tf4xMtymx21LFrFg6zcv9wiCzWeolkrsvIzWHJ/1DV9+aqDd8NNsMoPJ8LbmXclBx+HI8VRh4JKcNhXg+zXNCDzMd6RhAAAAAPixvspGiUmFAAGFArADAAAV8tzoscRn+wIAAAAABFla 9 | 10 | 11 | 12 | 13 | AQAAAACAAADwAgAAsAAAAA==/Td6WFoAAATm1rRGAgAhARYAAAB0L+Wj4ALvAG5dAABqf4CGWT3j9Uqwil2OUtsCHzHFFrl+aqabptUzL9SOe39SAvCK7iPawz8fvDCpSHkBzp1bECAXygDmyLZ4X/nMqID8Eqbc4azCjU2G0AEMSPklcaC2HvsDEIGliAwrSJ1nUyGCUcs8KhCW/QesAAAAi+NSyjXe5WYAAYoB8AUAAJ8es8GxxGf7AgAAAAAEWVo= 14 | 15 | 16 | AQAAAACAAAAwAQAAgAAAAA==/Td6WFoAAATm1rRGAgAhARYAAAB0L+Wj4AEvAD9dAAEAOcG2vwm8gB0fh9y6FkceUbXO4HhwUvGjyt0gZdVKBz8EkkpMcf8iDB+6wFXI0nHkq4NBKD80x7XEniAEygAAlCvKhF26m8oAAVuwAgAAAHbs0h+xxGf7AgAAAAAEWVo= 17 | 18 | 19 | AQAAAACAAAAwAQAAUAAAAA==/Td6WFoAAATm1rRGAgAhARYAAAB0L+Wj4AEvAA9dAAGAOfWXBWQaoCCI8wHgAAAA85sVNsk6+VAAASuwAgAAALfh8BSxxGf7AgAAAAAEWVo= 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /assets/pygmsh/no-compression.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | sAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/mpmZmZmZyb8AAAAAAAAAAJqZmZmZmfE/MzMzMzMz8z8AAAAAAAAAAJqZmZmZmbk/ZmZmZmZm5j8AAAAAAAAAANcaVVVVVdU/RuIQERERsb8AAAAAAAAAAF42VVVVVeU/S/gQERERwb8AAAAAAAAAAGNlZmZmZvA/jMEyMzMzwz8AAAAAAAAAAIXKzMzMzPA/L4D/////3z8AAAAAAAAAAAoyMzMzM/E/pBIzMzMz6z8AAAAAAAAAAOu2iIiIiOg/IZSIiIiI8D8AAAAAAAAAADoevLu7u9s/WtS7u7u76z8AAAAAAAAAAJnemZmZmak/xaJmZmZm1j8AAAAAAAAAAHrJyN240uc/FuHuggWP1j8AAAAAAAAAALIFpKLvMtg//R3E6CbU3j8AAAAAAAAAAD6XlYNuuOc/0Deks0PI5T8AAAAAAAAAABSoJgv+OeA/QPzGqLzVxD8AAAAAAAAAAIAi5dmwPNA/IJwdJnbAxz8AAAAAAAAAAPlBxLJq8+g/mkFLQ8mWtj8AAAAAAAAAAA== 9 | 10 | 11 | 12 | 13 | 8AIAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAUAAAAAAAAABQAAAAAAAAABAAAAAAAAAAEAAAAAAAAABgAAAAAAAAAGAAAAAAAAAAcAAAAAAAAABwAAAAAAAAAIAAAAAAAAAAgAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAkAAAAAAAAACQAAAAAAAAAKAAAAAAAAAAoAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAsAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAoAAAAAAAAADQAAAAAAAAAOAAAAAAAAAA0AAAAAAAAADAAAAAAAAAAOAAAAAAAAAAoAAAAAAAAAAwAAAAAAAAANAAAAAAAAAAgAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAPAAAAAAAAAAkAAAAAAAAACgAAAAAAAAAOAAAAAAAAAAMAAAAAAAAACwAAAAAAAAANAAAAAAAAAAYAAAAAAAAABwAAAAAAAAAMAAAAAAAAAAgAAAAAAAAACQAAAAAAAAAOAAAAAAAAAAcAAAAAAAAACAAAAAAAAAAOAAAAAAAAAAwAAAAAAAAABwAAAAAAAAAOAAAAAAAAAA8AAAAAAAAABQAAAAAAAAARAAAAAAAAAAUAAAAAAAAAAQAAAAAAAAARAAAAAAAAAAEAAAAAAAAABgAAAAAAAAARAAAAAAAAAAwAAAAAAAAADQAAAAAAAAAPAAAAAAAAAA0AAAAAAAAACwAAAAAAAAAQAAAAAAAAAA8AAAAAAAAADQAAAAAAAAAQAAAAAAAAAAsAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAQAAAAAAAAAAYAAAAAAAAADAAAAAAAAAARAAAAAAAAAAwAAAAAAAAADwAAAAAAAAARAAAAAAAAAAQAAAAAAAAADwAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAMAAAAAAAAA 14 | 15 | 16 | MAEAAAIAAAAAAAAABAAAAAAAAAAGAAAAAAAAAAgAAAAAAAAACgAAAAAAAAAMAAAAAAAAAA4AAAAAAAAAEAAAAAAAAAASAAAAAAAAABQAAAAAAAAAFgAAAAAAAAAYAAAAAAAAABsAAAAAAAAAHgAAAAAAAAAhAAAAAAAAACQAAAAAAAAAJwAAAAAAAAAqAAAAAAAAAC0AAAAAAAAAMAAAAAAAAAAzAAAAAAAAADYAAAAAAAAAOQAAAAAAAAA8AAAAAAAAAD8AAAAAAAAAQgAAAAAAAABFAAAAAAAAAEgAAAAAAAAASwAAAAAAAABOAAAAAAAAAFEAAAAAAAAAVAAAAAAAAABXAAAAAAAAAFoAAAAAAAAAWwAAAAAAAABcAAAAAAAAAF0AAAAAAAAAXgAAAAAAAAA= 17 | 18 | 19 | MAEAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAUAAAAAAAAABQAAAAAAAAAFAAAAAAAAAAUAAAAAAAAABQAAAAAAAAAFAAAAAAAAAAUAAAAAAAAABQAAAAAAAAAFAAAAAAAAAAUAAAAAAAAABQAAAAAAAAAFAAAAAAAAAAUAAAAAAAAABQAAAAAAAAAFAAAAAAAAAAUAAAAAAAAABQAAAAAAAAAFAAAAAAAAAAUAAAAAAAAABQAAAAAAAAAFAAAAAAAAAAUAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAA= 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /assets/pygmsh/zlib.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | AQAAAACAAACwAQAABQEAAA==eJxjYMAHPtjPmgkCJ/fDRCD8j/bGYPDZHlV8p30aGDyDi1+XCgWCq/ZujwQEBQU3ws2JMwOJP7X3/gESPwgXT04F6f9g33PQCGj+Ybg5rafOAMEHe/2G/0BwHy7OBVJm/NF+iRCIfg0Xf72tAwhe2CtOAdEf4OJWcnt279592z7qCpDajVA/8x7I/Svtjy4C2X8NLl518sTdHZee24s9fNfE2o8Q38S6ZNF7oxv2f2WPvFC7cg8ubjd9anPejuf2F8yXbHY+8RQuLrJCjfuf5QN7hz/HVuy5egQu3qD09OYGmwv2CnNk1coOHIeL/3Q8sinr8wv7WY7ezienbYOLAwDOhYjK 9 | 10 | 11 | 12 | 13 | AQAAAACAAADwAgAAfAAAAA==eJx1kUEOgCAMBEFRUBHw/5/1oHOZhF4mXZploSF8lcRNjOIuZrGIi3iIp7iKl0gxf/9s6qt0+zPnnOTyf/RJbvydF3//E7m4F7+muSK96hydXOR81Efp3iM6/uTu6nnXmJwPzQXpVJJODu53ni49Sbc/72Of7OUFXXQDPw== 14 | 15 | 16 | AQAAAACAAAAwAQAAWAAAAA==eJwtxdEGgwAAAMDJJMlkMhEjRsQYI8YYMSJGRKza/3/GHrp7uWC32Tt05NiJD059dOaTcxc+u/TFlWtfffPdjR9++uXWb3fu/fHg0ZO/nr149c9/z+4HFg== 17 | 18 | 19 | AQAAAACAAAAwAQAAFQAAAA==eJxjZoAAZhrRrEOMZiRAAwBbWACX 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /assets/rectilinear_grid.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 3.0 2 | vtk output 3 | ASCII 4 | DATASET RECTILINEAR_GRID 5 | DIMENSIONS 3 4 1 6 | X_COORDINATES 3 float 7 | 0 2 4 8 | Y_COORDINATES 4 float 9 | 1 2 3 4 10 | Z_COORDINATES 1 float 11 | 0 12 | CELL_DATA 6 13 | FIELD FieldData 1 14 | cellscalar 1 6 float 15 | 1.1 7.5 1.2 1.5 2.6 8.1 16 | POINT_DATA 12 17 | -------------------------------------------------------------------------------- /assets/rectilinear_grid_binary.vtk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/rectilinear_grid_binary.vtk -------------------------------------------------------------------------------- /assets/square.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 2.0 2 | Square example 3 | ASCII 4 | DATASET POLYDATA 5 | POINTS 4 float 6 | 0.0 0.0 0.0 7 | 1.0 0.0 0.0 8 | 1.0 0.0 -1.0 9 | 0.0 0.0 -1.0 10 | 11 | POLYGONS 1 5 12 | 4 0 1 2 3 13 | -------------------------------------------------------------------------------- /assets/structured_grid.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 3.0 2 | vtk output 3 | ASCII 4 | DATASET STRUCTURED_GRID 5 | DIMENSIONS 2 2 2 6 | POINTS 8 float 7 | 0 0.2 0 0.1 0.184843 0 0 0.25 0 8 | 0.1 0.234843 0 0 0.2 0.333333 0.1 0.184843 0.333333 9 | 0 0.25 0.333333 0.1 0.234843 0.333333 10 | 11 | CELL_DATA 1 12 | SCALARS cellval float 13 | LOOKUP_TABLE default 14 | 1489 15 | VECTORS cellvec float 16 | .6 .7 .5 17 | 18 | POINT_DATA 8 19 | SCALARS ptval float 20 | LOOKUP_TABLE default 21 | 0 1 2 3 4 5 6 7 22 | VECTORS ptvec float 23 | 0 0.0287671 0 0 0.0258604 0 0 0.0287671 0 24 | 0 0.0258604 0 0 0.0287671 0 0 0.0258604 0 25 | 0 0.0287671 0 0 0.0258604 0 26 | -------------------------------------------------------------------------------- /assets/tet.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 4.2 2 | Tetrahedron example 3 | ASCII 4 | DATASET UNSTRUCTURED_GRID 5 | POINTS 4 float 6 | 0 0 0 7 | 0 0 -1 8 | 0 1 0 9 | 1 0 0 10 | 11 | CELLS 1 5 12 | 4 0 1 2 3 13 | 14 | CELL_TYPES 1 15 | 10 16 | -------------------------------------------------------------------------------- /assets/tet.vtu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/tet.vtu -------------------------------------------------------------------------------- /assets/tet_test.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 4.2 2 | Tetrahedron example 3 | ASCII 4 | DATASET UNSTRUCTURED_GRID 5 | POINTS 4 float 6 | 0 0 0 7 | 0 0 -1 8 | 0 1 0 9 | 1 0 0 10 | 11 | CELLS 1 5 12 | 4 0 1 2 3 13 | 14 | CELL_TYPES 1 15 | 10 16 | 17 | POINT_DATA 4 18 | CELL_DATA 1 19 | -------------------------------------------------------------------------------- /assets/tet_test_binary.vtk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/tet_test_binary.vtk -------------------------------------------------------------------------------- /assets/tri.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 2.0 2 | Triangle example 3 | ASCII 4 | DATASET POLYDATA 5 | POINTS 3 float 6 | 0.0 0.0 0.0 7 | 1.0 0.0 0.0 8 | 0.0 0.0 -1.0 9 | 10 | POLYGONS 1 4 11 | 3 0 1 2 12 | -------------------------------------------------------------------------------- /assets/tri_attrib.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 2.0 2 | Triangle example 3 | ASCII 4 | DATASET POLYDATA 5 | POINTS 3 float 6 | 0.0 0.0 0.0 7 | 1.0 0.0 0.0 8 | 0.0 0.0 -1.0 9 | 10 | POLYGONS 1 4 11 | 3 0 1 2 12 | 13 | CELL_DATA 1 14 | COLOR_SCALARS scalars 3 15 | 1.0 0.0 0.0 16 | 17 | TEXTURE_COORDINATES tex_coords 3 float 18 | 1.0 0.0 0.0 19 | 20 | TENSORS tensors double 21 | 1.0 0.0 0.0 22 | 0.0 1.0 0.0 23 | 0.0 0.0 1.0 24 | -------------------------------------------------------------------------------- /assets/tri_attrib_binary.vtk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/tri_attrib_binary.vtk -------------------------------------------------------------------------------- /assets/triangle_vtkidtype.vtk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elrnv/vtkio/e14c8ff3caf84ae5e21c2ee70aa18275d6e54051/assets/triangle_vtkidtype.vtk -------------------------------------------------------------------------------- /assets/unstructured_grid_complex.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 2.0 2 | Unstructured Grid Example 3 | ASCII 4 | DATASET UNSTRUCTURED_GRID 5 | POINTS 27 float 6 | 0 0 0 1 0 0 2 0 0 0 1 0 1 1 0 2 1 0 7 | 0 0 1 1 0 1 2 0 1 0 1 1 1 1 1 2 1 1 8 | 0 1 2 1 1 2 2 1 2 0 1 3 1 1 3 2 1 3 9 | 0 1 4 1 1 4 2 1 4 0 1 5 1 1 5 2 1 5 10 | 0 1 6 1 1 6 2 1 6 11 | 12 | CELLS 12 71 13 | 10 0 1 4 3 6 7 10 9 2 5 14 | 8 0 1 4 3 6 7 10 9 15 | 8 1 2 5 4 7 8 11 10 16 | 4 6 10 9 12 17 | 4 5 11 10 14 18 | 6 15 16 17 14 13 12 19 | 6 18 15 19 16 20 17 20 | 4 22 23 20 19 21 | 3 21 22 18 22 | 3 22 19 18 23 | 2 26 25 24 | 1 24 25 | 26 | CELL_TYPES 12 27 | 24 28 | 12 29 | 12 30 | 10 31 | 10 32 | 7 33 | 6 34 | 9 35 | 5 36 | 5 37 | 3 38 | 1 39 | 40 | 41 | POINT_DATA 27 42 | SCALARS scalars float 1 43 | LOOKUP_TABLE default 44 | 0.0 1.0 2.0 3.0 4.0 5.0 45 | 6.0 7.0 8.0 9.0 10.0 11.0 46 | 12.0 13.0 14.0 15.0 16.0 17.0 47 | 18.0 19.0 20.0 21.0 22.0 23.0 48 | 24.0 25.0 26.0 49 | VECTORS vectors float 50 | 1 0 0 1 1 0 0 2 0 1 0 0 1 1 0 0 2 0 51 | 1 0 0 1 1 0 0 2 0 1 0 0 1 1 0 0 2 0 52 | 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 53 | 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 54 | 0 0 1 0 0 1 0 0 1 55 | -------------------------------------------------------------------------------- /assets/volume_complex.vti: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | _H 14 |  15 |  16 | (22( 17 | 18 | (22( 19 |  20 |  21 |  22 | 23 | 24 | -------------------------------------------------------------------------------- /assets/volume_complex.vtk: -------------------------------------------------------------------------------- 1 | # vtk DataFile Version 2.0 2 | Volume example 3 | ASCII 4 | DATASET STRUCTURED_POINTS 5 | DIMENSIONS 3 4 6 6 | ASPECT_RATIO 1 1 1 7 | ORIGIN 0 0 0 8 | POINT_DATA 72 9 | SCALARS volume_scalars char 1 10 | LOOKUP_TABLE default 11 | 0 0 0 0 0 0 0 0 0 0 0 0 12 | 0 5 10 15 20 25 25 20 15 10 5 0 13 | 0 10 20 30 40 50 50 40 30 20 10 0 14 | 0 10 20 30 40 50 50 40 30 20 10 0 15 | 0 5 10 15 20 25 25 20 15 10 5 0 16 | 0 0 0 0 0 0 0 0 0 0 0 0 17 | -------------------------------------------------------------------------------- /src/basic.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | use std::num::NonZeroUsize; 3 | use std::str::FromStr; 4 | 5 | use byteorder::ByteOrder; 6 | use num_traits::Zero; 7 | 8 | use nom::branch::alt; 9 | use nom::bytes::streaming::{tag, tag_no_case}; 10 | use nom::character::complete::{digit1, line_ending, multispace0, space0}; 11 | use nom::combinator::{complete, cut, eof, map, map_opt, opt, recognize}; 12 | use nom::error::ParseError; 13 | use nom::multi::many_m_n; 14 | use nom::sequence::{delimited, preceded, terminated}; 15 | use nom::{IResult, Needed, ParseTo, Parser}; 16 | 17 | use crate::model::IOBuffer; 18 | 19 | /// This enum indicates if bulk data is saved in binary. 20 | /// NOTE: VTK files are saved in ASCII format with bulk data optionally saved in 21 | /// Binary among ASCII type keywords. Binary data must be placed into the file 22 | /// immediately after the "newline" (`\n`) character from the previous ASCII 23 | /// keyword and parameter sequence. For example point positions and cell indices 24 | /// and types can be saved in Binary in VTK files. 25 | #[derive(Copy, Clone, PartialEq, Debug)] 26 | pub enum FileType { 27 | Binary, 28 | Ascii, 29 | } 30 | 31 | /* 32 | * General parser combinator building blocks 33 | */ 34 | 35 | /// A combinator that takes a parser `inner` and produces a parser that also consumes both leading and 36 | /// trailing spaces, tabs, LFs, CRLFs, returning the output of `inner`. 37 | pub fn ws<'a, F, O, E: ParseError<&'a [u8]>>( 38 | inner: F, 39 | ) -> impl Parser<&'a [u8], Output = O, Error = E> 40 | where 41 | F: Parser<&'a [u8], Output = O, Error = E>, 42 | { 43 | delimited(multispace0, inner, multispace0) 44 | } 45 | 46 | /// A combinator that takes a parser `inner` and produces a parser that also consumes both leading and 47 | /// trailing space and tab characters (" " & "\t"), returning the output of `inner`. 48 | pub fn sp<'a, F, O, E: ParseError<&'a [u8]>>( 49 | inner: F, 50 | ) -> impl Parser<&'a [u8], Output = O, Error = E> 51 | where 52 | F: Parser<&'a [u8], Output = O, Error = E>, 53 | { 54 | delimited(space0, inner, space0) 55 | } 56 | 57 | /// Recognizes a line ending or an eof. 58 | pub fn eol_or_eof<'a, E: ParseError<&'a [u8]>>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E> { 59 | alt((line_ending, eof)).parse(input) 60 | } 61 | 62 | /// A combinator that takes a parser `inner` and produces a parser that also consumes trailing 63 | /// line endings ("\r\n" & "\n"), returning the output of `inner`. 64 | pub fn line<'a, F, O, E: ParseError<&'a [u8]>>( 65 | inner: F, 66 | ) -> impl Parser<&'a [u8], Output = O, Error = E> 67 | where 68 | F: Parser<&'a [u8], Output = O, Error = E>, 69 | { 70 | terminated(inner, eol_or_eof) 71 | } 72 | 73 | /// Parses a `tag_no_case` possibly surrounded by spaces (" " & "\t") and followed by a EOL. 74 | pub fn tag_no_case_line<'a, E: ParseError<&'a [u8]>>( 75 | tag: &'static str, 76 | ) -> impl Parser<&'a [u8], Output = &'a [u8], Error = E> { 77 | line(sp(tag_no_case(tag))) 78 | } 79 | 80 | /// Parses a "block" that is preceded by possibly multiple multispaces and a specific `block_tag` 81 | /// identified by the given parser, returns the result of applying `body` after the `block_tag`. 82 | pub fn tagged_block<'a, O, E: ParseError<&'a [u8]>, F, G>( 83 | block_tag: F, 84 | body: G, 85 | ) -> impl Parser<&'a [u8], Output = O, Error = E> 86 | where 87 | F: Parser<&'a [u8], Error = E>, 88 | G: Parser<&'a [u8], Output = O, Error = E>, 89 | { 90 | preceded( 91 | preceded(multispace0, block_tag), 92 | // Add cut around block body in case the block is used in an `alt` as we don't want to 93 | // try other alternatives if the block_tag matched. 94 | cut(body), 95 | ) 96 | } 97 | 98 | /* 99 | * Number parsing 100 | */ 101 | 102 | /// Parse a number in binary form from a byte array. 103 | pub trait FromBinary 104 | where 105 | Self: Sized, 106 | { 107 | fn from_binary(input: &[u8]) -> IResult<&[u8], Self>; 108 | } 109 | 110 | macro_rules! impl_from_binary { 111 | ($type:ty) => { 112 | impl FromBinary for $type { 113 | fn from_binary(input: &[u8]) -> IResult<&[u8], $type> { 114 | debug_assert_eq!(std::mem::size_of::<$type>(), 1); 115 | if input.len() < 1 { 116 | // SAFETY: Called with nonzero value 117 | unsafe { 118 | Err(nom::Err::Incomplete(Needed::Size( 119 | std::num::NonZeroUsize::new_unchecked(1), 120 | ))) 121 | } 122 | } else { 123 | Ok((&input[1..], input[0] as $type)) 124 | } 125 | } 126 | } 127 | }; 128 | ($type:ty, $read_fn:ident) => { 129 | impl FromBinary for $type { 130 | fn from_binary(input: &[u8]) -> IResult<&[u8], $type> { 131 | let size: usize = std::mem::size_of::<$type>(); 132 | if input.len() < size { 133 | // SAFETY: Can only be called for `size` > 0 as `input.len()` and `size` are both usize 134 | unsafe { 135 | Err(nom::Err::Incomplete(Needed::Size( 136 | std::num::NonZeroUsize::new_unchecked(size), 137 | ))) 138 | } 139 | } else { 140 | let res = T::$read_fn(input); 141 | Ok((&input[size..], res)) 142 | } 143 | } 144 | } 145 | }; 146 | } 147 | 148 | impl_from_binary!(u8); 149 | impl_from_binary!(i8); 150 | impl_from_binary!(u16, read_u16); 151 | impl_from_binary!(i16, read_i16); 152 | impl_from_binary!(u32, read_u32); 153 | impl_from_binary!(i32, read_i32); 154 | impl_from_binary!(u64, read_u64); 155 | impl_from_binary!(i64, read_i64); 156 | impl_from_binary!(f32, read_f32); 157 | impl_from_binary!(f64, read_f64); 158 | 159 | /// Parse a number in ASCII form from a byte array. 160 | pub trait FromAscii 161 | where 162 | Self: Sized, 163 | { 164 | fn from_ascii(input: &[u8]) -> IResult<&[u8], Self>; 165 | } 166 | 167 | macro_rules! impl_from_ascii { 168 | ($type:ty, $fn:path) => { 169 | impl FromAscii for $type { 170 | fn from_ascii(input: &[u8]) -> IResult<&[u8], $type> { 171 | $fn(input) 172 | } 173 | } 174 | }; 175 | } 176 | 177 | impl_from_ascii!(u8, nom::character::complete::u8); 178 | impl_from_ascii!(i8, nom::character::complete::i8); 179 | impl_from_ascii!(u16, nom::character::complete::u16); 180 | impl_from_ascii!(i16, nom::character::complete::i16); 181 | impl_from_ascii!(u32, nom::character::complete::u32); 182 | impl_from_ascii!(i32, nom::character::complete::i32); 183 | impl_from_ascii!(u64, nom::character::complete::u64); 184 | impl_from_ascii!(i64, nom::character::complete::i64); 185 | impl_from_ascii!(f32, real); 186 | impl_from_ascii!(f64, real); 187 | 188 | /// Parse a floating point number from a byte array. 189 | /// This extends `nom`'s implementation by allowing floats without a decimal point (e.g. `3e3`). 190 | pub fn real(input: &[u8]) -> IResult<&[u8], T> 191 | where 192 | T: FromStr, 193 | { 194 | map_opt( 195 | recognize(( 196 | opt(alt((tag("+"), tag("-")))), 197 | alt(( 198 | complete(delimited(digit1, tag("."), opt(digit1))), 199 | complete(delimited(opt(digit1), tag("."), digit1)), 200 | complete(digit1), 201 | )), 202 | opt(complete(( 203 | alt((tag("e"), tag("E"))), 204 | opt(alt((tag("+"), tag("-")))), 205 | digit1, 206 | ))), 207 | )), 208 | |i: &[u8]| i.parse_to(), 209 | ) 210 | .parse(input) 211 | } 212 | 213 | /// A trait identifying all scalar types supported by VTK. 214 | pub trait Scalar: FromStr + FromAscii + FromBinary {} 215 | 216 | macro_rules! impl_scalar { 217 | ($($type:ty),* $(,)*) => { 218 | $( 219 | impl Scalar for $type {} 220 | )* 221 | } 222 | } 223 | 224 | impl_scalar!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); 225 | 226 | /* 227 | * Buffer parsing 228 | */ 229 | 230 | /// Parse a set of typed numbers into an `IOBuffer`. 231 | pub fn parse_data_buffer(input: &[u8], n: usize, ft: FileType) -> IResult<&[u8], IOBuffer> 232 | where 233 | T: Scalar + Any + Clone + Zero + std::fmt::Debug, 234 | BO: ByteOrder, 235 | IOBuffer: From>, 236 | { 237 | map(|i| parse_data_vec::(i, n, ft), IOBuffer::from).parse(input) 238 | } 239 | 240 | /// Parse a set of unsigned bytes into an `IOBuffer`. 241 | pub fn parse_data_buffer_u8(input: &[u8], n: usize, ft: FileType) -> IResult<&[u8], IOBuffer> { 242 | map(|i| parse_data_vec_u8(i, n, ft), IOBuffer::from).parse(input) 243 | } 244 | 245 | /// Parse a set of signed bytes into an `IOBuffer`. 246 | pub fn parse_data_buffer_i8(input: &[u8], n: usize, ft: FileType) -> IResult<&[u8], IOBuffer> { 247 | map(|i| parse_data_vec_i8(i, n, ft), IOBuffer::from).parse(input) 248 | } 249 | 250 | /// Parse a set of bits into an `IOBuffer`. 251 | pub fn parse_data_bit_buffer(input: &[u8], n: usize, ft: FileType) -> IResult<&[u8], IOBuffer> { 252 | map(|i| parse_data_bit_vec(i, n, ft), IOBuffer::from).parse(input) 253 | } 254 | 255 | /// Parse a set of typed numbers into a `Vec`. 256 | pub fn parse_data_vec(input: &[u8], n: usize, ft: FileType) -> IResult<&[u8], Vec> 257 | where 258 | T: Scalar, 259 | BO: ByteOrder, 260 | { 261 | match ft { 262 | FileType::Ascii => many_m_n(n, n, ws(T::from_ascii)).parse(input), 263 | FileType::Binary => many_m_n(n, n, T::from_binary::).parse(input), 264 | } 265 | } 266 | 267 | /// Parse a set of unsigned bytes into a `Vec`. 268 | pub fn parse_data_vec_u8(input: &[u8], n: usize, ft: FileType) -> IResult<&[u8], Vec> { 269 | match ft { 270 | FileType::Ascii => many_m_n(n, n, ws(u8::from_ascii)).parse(input), 271 | FileType::Binary => { 272 | // If expecting bytes, byte order doesn't matter, just return the entire block. 273 | if input.len() < n { 274 | // SAFETY: Can only be called for `n` > 0 as `input.len()` and `n` are both usize 275 | unsafe { 276 | Err(nom::Err::Incomplete(Needed::Size( 277 | NonZeroUsize::new_unchecked(n), 278 | ))) 279 | } 280 | } else { 281 | Ok((&input[n..], input[0..n].to_vec())) 282 | } 283 | } 284 | } 285 | } 286 | 287 | /// Parse a set of signed bytes into a `Vec`. 288 | pub fn parse_data_vec_i8(input: &[u8], n: usize, ft: FileType) -> IResult<&[u8], Vec> { 289 | match ft { 290 | FileType::Ascii => many_m_n(n, n, ws(i8::from_ascii)).parse(input), 291 | FileType::Binary => { 292 | // If expecting bytes, byte order doesn't matter, just return the entire block. 293 | // Unsafety is used here to avoid having to iterate. 294 | if input.len() < n { 295 | // SAFETY: Can only be called for `n` > 0 as `input.len()` and `n` are both usize 296 | unsafe { 297 | Err(nom::Err::Incomplete(Needed::Size( 298 | NonZeroUsize::new_unchecked(n), 299 | ))) 300 | } 301 | } else { 302 | // SAFETY: All u8 are representable as i8 and both are 8 bits. 303 | Ok(( 304 | &input[n..], 305 | unsafe { std::slice::from_raw_parts(input[0..n].as_ptr() as *const i8, n) } 306 | .to_vec(), 307 | )) 308 | } 309 | } 310 | } 311 | } 312 | 313 | pub fn parse_data_bit_vec(input: &[u8], n: usize, ft: FileType) -> IResult<&[u8], Vec> { 314 | match ft { 315 | FileType::Ascii => many_m_n(n, n, ws(u8::from_ascii)).parse(input), 316 | FileType::Binary => { 317 | let nbytes = n / 8 + usize::from(n % 8 != 0); 318 | if input.len() < nbytes { 319 | // SAFETY: Can only be called for `nbytes` > 0 as `input.len()` and `nbytes` are both usize 320 | unsafe { 321 | Err(nom::Err::Incomplete(Needed::Size( 322 | NonZeroUsize::new_unchecked(nbytes), 323 | ))) 324 | } 325 | } else { 326 | Ok((&input[nbytes..], input[0..nbytes].to_vec())) 327 | } 328 | } 329 | } 330 | } 331 | 332 | #[cfg(test)] 333 | mod tests { 334 | use super::*; 335 | use byteorder::BigEndian; 336 | 337 | #[test] 338 | fn can_parse_float() { 339 | assert_eq!(real::(&b"-0.00005"[..]).unwrap().1, -0.00005); 340 | assert_eq!(real::(&b"4."[..]).unwrap().1, 4.0); 341 | assert_eq!(real::(&b"3"[..]).unwrap().1, 3.0); 342 | assert_eq!(real::(&b"-.3"[..]).unwrap().1, -0.3); 343 | assert_eq!(real::(&b"3e3"[..]).unwrap().1, 3000.0); 344 | assert_eq!(real::(&b"-3.2e2"[..]).unwrap().1, -320.0); 345 | } 346 | #[test] 347 | fn can_parse_binary_float() { 348 | assert_eq!( 349 | f32::from_binary::(&[0u8, 0, 0, 0]).unwrap().1, 350 | 0.0_f32 351 | ); 352 | assert_eq!( 353 | f32::from_binary::(&[62u8, 32, 0, 0]).unwrap().1, 354 | 0.15625_f32 355 | ); 356 | } 357 | #[test] 358 | fn data_test() { 359 | let f = parse_data_buffer::("".as_bytes(), 0, FileType::Ascii); 360 | assert_eq!(f, Ok(("".as_bytes(), IOBuffer::from(Vec::::new())))); 361 | let f = parse_data_buffer::("3".as_bytes(), 1, FileType::Ascii); 362 | assert_eq!(f, Ok(("".as_bytes(), IOBuffer::from(vec![3.0f32])))); 363 | let f = parse_data_buffer::("3 32".as_bytes(), 2, FileType::Ascii); 364 | assert_eq!(f, Ok(("".as_bytes(), IOBuffer::from(vec![3.0f32, 32.0])))); 365 | let f = parse_data_buffer::("3 32 32.0 4e3".as_bytes(), 4, FileType::Ascii); 366 | assert_eq!( 367 | f, 368 | Ok(( 369 | "".as_bytes(), 370 | IOBuffer::from(vec![3.0f32, 32.0, 32.0, 4.0e3]) 371 | )) 372 | ); 373 | let f = parse_data_buffer::("3 32 32.0 4e3".as_bytes(), 4, FileType::Ascii); 374 | assert_eq!( 375 | f, 376 | Ok(( 377 | "".as_bytes(), 378 | IOBuffer::from(vec![3.0f64, 32.0, 32.0, 4.0e3]) 379 | )) 380 | ); 381 | } 382 | } 383 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Import and export library for Visualization Toolkit (VTK) 2 | //! [files](https://kitware.github.io/vtk-examples/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 | //! use std::path::PathBuf; 19 | //! let file_path = PathBuf::from("../assets/tet.vtk"); 20 | //! 21 | //! let mut vtk_file = Vtk::import(&file_path) 22 | //! .expect(&format!("Failed to load file: {:?}", file_path)); 23 | //! 24 | //! vtk_file.version = Version::new_legacy(4,2); // arbitrary change 25 | //! 26 | //! vtk_file.export_ascii(&file_path) 27 | //! .expect(&format!("Failed to save file: {:?}", file_path)); 28 | //! ``` 29 | //! 30 | //! Files are sometimes provided as strings or byte slices, so it is also useful to be able to 31 | //! parse VTK files and write them back to a string or byte slice. 32 | //! 33 | //! ```no_run 34 | //! use vtkio::model::*; // import model definition of a VTK file 35 | //! let data: &[u8] = include_str!("../assets/tet.vtk").as_bytes(); // Or just include_bytes! 36 | //! 37 | //! let mut vtk_file = Vtk::parse_legacy_be(data).expect(&format!("Failed to parse file")); 38 | //! 39 | //! vtk_file.version = Version::new_legacy(4,2); // arbitrary change 40 | //! 41 | //! let mut output = String::new(); 42 | //! Vtk::write_legacy_ascii(vtk_file, &mut output).expect(&format!("Failed to write file")); 43 | //! 44 | //! println!("{}", output); 45 | //! ``` 46 | //! 47 | //! To quickly extract some data from a file, you can cast it to an `f64` type as follows 48 | //! 49 | //! ```no_run 50 | //! use vtkio::model::*; // import model definition of a VTK file 51 | //! 52 | //! // Load up vtk file. 53 | //! let file_path = "../assets/para_tet.vtk"; 54 | //! let mut vtk = Vtk::import(&file_path) 55 | //! .expect(&format!("Failed to load file: {:?}", file_path)); 56 | //! 57 | //! // Get all the pieces knowing that type they are. 58 | //! let pieces = if let DataSet::UnstructuredGrid { pieces, .. } = vtk.data { 59 | //! pieces 60 | //! } else { 61 | //! panic!("Wrong vtk data type"); 62 | //! }; 63 | //! 64 | //! // Often files have only a single piece, so we load it up here. 65 | //! // To avoid cloning you can also use `into_loaded_piece_data` here. 66 | //! let piece = pieces[0].load_piece_data(None).unwrap(); 67 | //! 68 | //! // Get the first cell attribute. 69 | //! let attribute = &piece.data.cell[0]; 70 | //! 71 | //! // Find the attribute with a specific name (in this case "FloatValue"), 72 | //! // and return the corresponding data in `f64` format. 73 | //! let field_name = "FloatValue"; 74 | //! let data = if let Attribute::Field { data_array, .. } = attribute { 75 | //! data_array 76 | //! .iter() 77 | //! .find(|&DataArrayBase { name, .. }| name == field_name) 78 | //! .expect(&format!("Failed to find {:?} field", field_name)) 79 | //! .data 80 | //! .cast_into::() // Cast to f64 to get a view of the data. 81 | //! .expect("Failed cast to f64") 82 | //! } else { 83 | //! panic!("First attribute is not a field"); 84 | //! }; 85 | //! 86 | //! assert_eq!(data.as_slice(), &[0.0]); 87 | //! ``` 88 | 89 | pub(crate) mod basic; 90 | #[macro_use] 91 | pub mod model; 92 | pub mod parser; 93 | pub mod writer; 94 | #[cfg(feature = "xml")] 95 | pub mod xml; 96 | 97 | #[cfg(feature = "xml")] 98 | use std::convert::{TryFrom, TryInto}; 99 | use std::fs::File; 100 | #[cfg(feature = "xml")] 101 | use std::io::BufRead; 102 | use std::io::{self, BufWriter, Read, Write}; 103 | use std::path::Path; 104 | 105 | use crate::writer::{AsciiWriter, BinaryWriter, WriteVtk}; 106 | 107 | pub use model::IOBuffer; 108 | 109 | /// The primary `vtkio` API is provided through the `Vtk` struct. 110 | pub use model::Vtk; 111 | 112 | /// Error type for Import/Export operations. 113 | #[derive(Debug)] 114 | pub enum Error { 115 | IO(io::Error), 116 | Write(writer::Error), 117 | Parse(nom::error::ErrorKind), 118 | #[cfg(feature = "xml")] 119 | XML(xml::Error), 120 | UnknownFileExtension(Option), 121 | Load(model::Error), 122 | Unknown, 123 | } 124 | 125 | impl std::fmt::Display for Error { 126 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 127 | match self { 128 | Error::IO(source) => write!(f, "IO error: {}", source), 129 | Error::Write(source) => write!(f, "Write error: {}", source), 130 | Error::Parse(source) => write!(f, "Parse error: {:?}", source), 131 | #[cfg(feature = "xml")] 132 | Error::XML(source) => write!(f, "XML error: {}", source), 133 | Error::UnknownFileExtension(Some(ext)) => { 134 | write!(f, "Unknown file extension: {:?}", ext) 135 | } 136 | Error::UnknownFileExtension(None) => write!(f, "Missing file extension"), 137 | Error::Load(source) => write!(f, "Load error: {}", source), 138 | Error::Unknown => write!(f, "Unknown error"), 139 | } 140 | } 141 | } 142 | 143 | impl std::error::Error for Error { 144 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 145 | match self { 146 | Error::IO(source) => Some(source), 147 | Error::Write(source) => Some(source), 148 | Error::Parse(_) => None, 149 | #[cfg(feature = "xml")] 150 | Error::XML(source) => Some(source), 151 | Error::UnknownFileExtension(_) => None, 152 | Error::Load(source) => Some(source), 153 | Error::Unknown => None, 154 | } 155 | } 156 | } 157 | 158 | /// Convert `std::io` error into `vtkio` error. 159 | impl From for Error { 160 | fn from(e: io::Error) -> Error { 161 | Error::IO(e) 162 | } 163 | } 164 | 165 | /// Convert `vtkio::model::Error` into a `vtkio::Error`. 166 | impl From for Error { 167 | fn from(e: model::Error) -> Error { 168 | Error::Load(e) 169 | } 170 | } 171 | 172 | /// Convert [`xml::Error`] error into the top level `vtkio` error. 173 | /// 174 | /// [`xml::Error`]: xml.enum.Error.html 175 | #[cfg(feature = "xml")] 176 | impl From for Error { 177 | fn from(e: xml::Error) -> Error { 178 | Error::XML(e) 179 | } 180 | } 181 | 182 | /// Convert `vtkio` error into `std::io` error. 183 | impl From for io::Error { 184 | fn from(err: Error) -> io::Error { 185 | match err { 186 | Error::IO(e) => e, 187 | _ => io::Error::new(io::ErrorKind::Other, format!("{:?}", err)), 188 | } 189 | } 190 | } 191 | 192 | impl From for Error { 193 | fn from(e: writer::Error) -> Error { 194 | Error::Write(e) 195 | } 196 | } 197 | 198 | impl Vtk { 199 | /// Helper for parsing legacy VTK files. 200 | fn parse_vtk(mut reader: impl Read, parse: F, buf: &mut Vec) -> Result 201 | where 202 | F: Fn(&[u8]) -> nom::IResult<&[u8], Vtk>, 203 | { 204 | reader.read_to_end(buf)?; 205 | match parse(buf) { 206 | Ok((_, vtk)) => Ok(vtk), 207 | Err(e) => match e { 208 | nom::Err::Incomplete(_) => Err(Error::Unknown), 209 | nom::Err::Error(e) | nom::Err::Failure(e) => Err(Error::Parse(e.code)), 210 | }, 211 | } 212 | } 213 | 214 | /// Helper for importing legacy VTK files from the given path. 215 | fn import_vtk(file_path: &Path, parse: F) -> Result 216 | where 217 | F: Fn(&[u8]) -> nom::IResult<&[u8], Vtk>, 218 | { 219 | let file = File::open(file_path)?; 220 | Vtk::parse_vtk(file, parse, &mut Vec::new()) 221 | } 222 | 223 | /// Parse a legacy VTK file from the given reader. 224 | /// 225 | /// If the file is in binary format, numeric types will be interpreted in big endian format, 226 | /// which is the most common among VTK files. 227 | /// Note that this function and [`parse_legacy_le`](Vtk::parse_legacy_le) also work equally well for 228 | /// parsing VTK files in ASCII format. 229 | /// 230 | /// # Examples 231 | /// 232 | /// Parsing an ASCII file: 233 | /// 234 | /// ``` 235 | /// use vtkio::model::*; // import the model definition of a VTK file 236 | /// let vtk_ascii: &[u8] = b" 237 | /// ## vtk DataFile Version 2.0 238 | /// Triangle example 239 | /// ASCII 240 | /// DATASET POLYDATA 241 | /// POINTS 3 float 242 | /// 0.0 0.0 0.0 243 | /// 1.0 0.0 0.0 244 | /// 0.0 0.0 -1.0 245 | /// 246 | /// POLYGONS 1 4 247 | /// 3 0 1 2 248 | /// "; 249 | /// 250 | /// let vtk = Vtk::parse_legacy_be(vtk_ascii).expect("Failed to parse vtk file"); 251 | /// 252 | /// assert_eq!(vtk, Vtk { 253 | /// version: Version::new_legacy(2,0), 254 | /// byte_order: ByteOrder::BigEndian, 255 | /// title: String::from("Triangle example"), 256 | /// file_path: None, 257 | /// data: DataSet::inline(PolyDataPiece { 258 | /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(), 259 | /// polys: Some(VertexNumbers::Legacy { 260 | /// num_cells: 1, 261 | /// vertices: vec![3, 0, 1, 2] 262 | /// }), 263 | /// data: Attributes::new(), 264 | /// ..Default::default() 265 | /// }) 266 | /// }); 267 | /// ``` 268 | pub fn parse_legacy_be(reader: impl Read) -> Result { 269 | Vtk::parse_vtk(reader, parser::parse_be, &mut Vec::new()) 270 | } 271 | 272 | /// Parse a legacy VTK file from the given reader. 273 | /// 274 | /// If the file is in binary format, numeric types will be interpreted in little endian format. 275 | /// Note that this function and [`parse_legacy_be`](Vtk::parse_legacy_be) also work equally well for 276 | /// parsing VTK files in ASCII format. 277 | /// 278 | /// # Examples 279 | /// 280 | /// Parsing an ASCII file: 281 | /// 282 | /// ``` 283 | /// use vtkio::model::*; // import the model definition of a VTK file 284 | /// let vtk_ascii: &[u8] = b" 285 | /// ## vtk DataFile Version 2.0 286 | /// Triangle example 287 | /// ASCII 288 | /// DATASET POLYDATA 289 | /// POINTS 3 float 290 | /// 0.0 0.0 0.0 291 | /// 1.0 0.0 0.0 292 | /// 0.0 0.0 -1.0 293 | /// 294 | /// POLYGONS 1 4 295 | /// 3 0 1 2 296 | /// "; 297 | /// 298 | /// let vtk = Vtk::parse_legacy_le(vtk_ascii).expect("Failed to parse vtk file"); 299 | /// 300 | /// assert_eq!(vtk, Vtk { 301 | /// version: Version::new_legacy(2,0), 302 | /// byte_order: ByteOrder::LittleEndian, 303 | /// title: String::from("Triangle example"), 304 | /// file_path: None, 305 | /// data: DataSet::inline(PolyDataPiece { 306 | /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(), 307 | /// polys: Some(VertexNumbers::Legacy { 308 | /// num_cells: 1, 309 | /// vertices: vec![3, 0, 1, 2] 310 | /// }), 311 | /// data: Attributes::new(), 312 | /// ..Default::default() 313 | /// }) 314 | /// }); 315 | /// ``` 316 | pub fn parse_legacy_le(reader: impl Read) -> Result { 317 | Vtk::parse_vtk(reader, parser::parse_le, &mut Vec::new()) 318 | } 319 | 320 | /// Parse a legacy VTK file in big endian format from the given reader and a buffer. 321 | /// 322 | /// This is the buffered version of [`Vtk::parse_legacy_be`](Vtk::parse_legacy_be), which allows one to reuse the same 323 | /// heap allocated space when reading many files. 324 | pub fn parse_legacy_buf_be(reader: impl Read, buf: &mut Vec) -> Result { 325 | Vtk::parse_vtk(reader, parser::parse_be, buf) 326 | } 327 | 328 | /// Parse a legacy VTK file in little endian format from the given reader and a buffer. 329 | /// 330 | /// This is the buffered version of [`parse_legacy_le`](Vtk::parse_legacy_le), which allows one to reuse the same 331 | /// heap allocated space when reading many files. 332 | pub fn parse_legacy_buf_le(reader: impl Read, buf: &mut Vec) -> Result { 333 | Vtk::parse_vtk(reader, parser::parse_le, buf) 334 | } 335 | 336 | /// Parse a modern XML style VTK file from a given reader. 337 | /// 338 | /// # Examples 339 | /// 340 | /// Parsing a binary file in big endian format representing a polygon mesh consisting of a single 341 | /// triangle: 342 | /// 343 | /// ``` 344 | /// use vtkio::model::*; // import the model definition of a VTK file 345 | /// 346 | /// let input: &[u8] = b"\ 347 | /// \ 348 | /// \ 349 | /// \ 350 | /// \ 351 | /// \ 352 | /// AAAAAAAAAAQAAAAAAAAAAAAAAAA/gAAAAAAAAAAAAAAAAAAAAAAAAL+AAAA=\ 353 | /// \ 354 | /// \ 355 | /// \ 356 | /// \ 357 | /// AAAAAAAAAAgAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAI=\ 358 | /// \ 359 | /// \ 360 | /// AAAAAAAAAAgAAAAAAAAAAw==\ 361 | /// \ 362 | /// \ 363 | /// \ 364 | /// \ 365 | /// "; 366 | /// 367 | /// let vtk = Vtk::parse_xml(input).expect("Failed to parse XML VTK file"); 368 | /// 369 | /// assert_eq!(vtk, Vtk { 370 | /// version: Version::new_xml(2,0), 371 | /// byte_order: ByteOrder::BigEndian, // This is default 372 | /// title: String::new(), 373 | /// file_path: None, 374 | /// data: DataSet::inline(PolyDataPiece { 375 | /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(), 376 | /// polys: Some(VertexNumbers::XML { 377 | /// connectivity: vec![0, 1, 2], 378 | /// offsets: vec![3] 379 | /// }), 380 | /// data: Attributes::new(), 381 | /// ..Default::default() 382 | /// }) 383 | /// }); 384 | /// ``` 385 | #[cfg(feature = "xml")] 386 | pub fn parse_xml(reader: impl BufRead) -> Result { 387 | // There is no extension to check with the data is provided directly. 388 | // Luckily the xml file contains all the data necessary to determine which data is 389 | // being parsed. 390 | let vtk_file = xml::VTKFile::parse(reader)?; 391 | Ok(vtk_file.try_into()?) 392 | } 393 | 394 | #[cfg(feature = "async_blocked")] 395 | async fn import_vtk_async(file_path: &Path, parse: F) -> Result 396 | where 397 | F: Fn(&[u8]) -> nom::IResult<&[u8], Vtk>, 398 | { 399 | use nom::IResult; 400 | use tokio::fs::File; 401 | use tokio::io::AsyncReadExt; 402 | 403 | let mut file = File::open(file_path).await?; 404 | let mut buf = Vec::new(); 405 | file.read_to_end(&mut buf).await?; 406 | match VTKFile::parse(&buf) { 407 | IResult::Done(_, vtk) => Ok(vtk), 408 | IResult::Error(e) => Err(Error::Parse(e.into_error_kind())), 409 | IResult::Incomplete(_) => Err(Error::Unknown), 410 | } 411 | } 412 | 413 | /// Import a VTK file at the specified path. 414 | /// 415 | /// This function determines the vtk file type from the extension as prescribed by the [VTK 416 | /// file formats documentation](https://kitware.github.io/vtk-examples/site/VTKFileFormats/): 417 | /// 418 | /// - Legacy (`.vtk`) -- Simple legacy file format (Non-XML) 419 | /// - Image data (`.vti`) -- Serial vtkImageData (structured) 420 | /// - PolyData (`.vtp`) -- Serial vtkPolyData (unstructured) 421 | /// - RectilinearGrid (`.vtr`) -- Serial vtkRectilinearGrid (structured) 422 | /// - StructuredGrid (`.vts`) -- Serial vtkStructuredGrid (structured) 423 | /// - UnstructuredGrid (`.vtu`) -- Serial vtkUnstructuredGrid (unstructured) 424 | /// - PImageData (`.pvti`) -- Parallel vtkImageData (structured) 425 | /// - PPolyData (`.pvtp`) -- Parallel vtkPolyData (unstructured) 426 | /// - PRectilinearGrid (`.pvtr`) -- Parallel vtkRectilinearGrid (structured) 427 | /// - PStructuredGrid (`.pvts`) -- Parallel vtkStructuredGrid (structured) 428 | /// - PUnstructuredGrid (`.pvtu`) -- Parallel vtkUnstructuredGrid (unstructured) 429 | /// 430 | /// # Examples 431 | /// 432 | /// The following example imports a legacy `.vtk` file called `tet.vtk`, and panics with an 433 | /// appropriate error message if the file fails to load. 434 | /// 435 | /// ```should_panic 436 | /// use vtkio::Vtk; 437 | /// use std::path::PathBuf; 438 | /// 439 | /// let file_path = PathBuf::from("tet.vtk"); 440 | /// 441 | /// let mut vtk_file = Vtk::import(&file_path) 442 | /// .expect(&format!("Failed to load file: {:?}", file_path)); 443 | /// ``` 444 | pub fn import(file_path: impl AsRef) -> Result { 445 | Vtk::import_impl(file_path.as_ref()) 446 | } 447 | 448 | /// A non-generic helper for the `import` function. 449 | fn import_impl(path: &Path) -> Result { 450 | let ext = path 451 | .extension() 452 | .and_then(|s| s.to_str()) 453 | .ok_or(Error::UnknownFileExtension(None))?; 454 | match ext { 455 | "vtk" => Vtk::import_vtk(path, parser::parse_be), 456 | #[cfg(feature = "xml")] 457 | ext => { 458 | let ft = xml::FileType::try_from_ext(ext) 459 | .ok_or(Error::UnknownFileExtension(Some(ext.to_string())))?; 460 | let vtk_file = xml::VTKFile::import(path)?; 461 | let exp_ft = xml::FileType::from(vtk_file.data_set_type); 462 | if ft != exp_ft { 463 | Err(Error::XML(xml::Error::TypeExtensionMismatch)) 464 | } else { 465 | let mut vtk: Vtk = vtk_file.try_into()?; 466 | vtk.file_path = Some(path.into()); 467 | Ok(vtk) 468 | } 469 | } 470 | #[cfg(not(feature = "xml"))] 471 | _ => Err(Error::UnknownFileExtension(None)), 472 | } 473 | } 474 | 475 | /// Import a VTK file at the specified path. 476 | /// 477 | /// This is the async version of [`import`](Vtk::import). 478 | /// 479 | /// This function determines the vtk file type from the extension as prescribed by the [VTK 480 | /// file formats documentation](https://kitware.github.io/vtk-examples/site/VTKFileFormats/): 481 | /// 482 | /// - Legacy (`.vtk`) -- Simple legacy file format (Non-XML) 483 | /// - Image data (`.vti`) -- Serial vtkImageData (structured) 484 | /// - PolyData (`.vtp`) -- Serial vtkPolyData (unstructured) 485 | /// - RectilinearGrid (`.vtr`) -- Serial vtkRectilinearGrid (structured) 486 | /// - StructuredGrid (`.vts`) -- Serial vtkStructuredGrid (structured) 487 | /// - UnstructuredGrid (`.vtu`) -- Serial vtkUnstructuredGrid (unstructured) 488 | /// - PImageData (`.pvti`) -- Parallel vtkImageData (structured) 489 | /// - PPolyData (`.pvtp`) -- Parallel vtkPolyData (unstructured) 490 | /// - PRectilinearGrid (`.pvtr`) -- Parallel vtkRectilinearGrid (structured) 491 | /// - PStructuredGrid (`.pvts`) -- Parallel vtkStructuredGrid (structured) 492 | /// - PUnstructuredGrid (`.pvtu`) -- Parallel vtkUnstructuredGrid (unstructured) 493 | /// 494 | /// # Examples 495 | /// 496 | /// The following example imports a legacy `.vtk` file called `tet.vtk`, and panics with an 497 | /// appropriate error message if the file fails to load. 498 | /// 499 | /// ```should_panic 500 | /// use vtkio::Vtk; 501 | /// use std::path::PathBuf; 502 | /// 503 | /// let file_path = PathBuf::from("tet.vtk"); 504 | /// 505 | /// let mut vtk_file = Vtk::import_async(&file_path).await 506 | /// .expect(&format!("Failed to load file: {:?}", file_path)); 507 | /// ``` 508 | #[cfg(feature = "async_blocked")] 509 | pub async fn import_async(file_path: impl AsRef) -> Result { 510 | let path = file_path.as_ref(); 511 | let ext = path.extension().and_then(|s| s.to_str()).ok_or()?; 512 | match ext { 513 | "vtk" => import_vtk_async(path, parser::parse_be).await, 514 | #[cfg(feature = "xml")] 515 | ext => { 516 | let ft = xml::FileType::try_from_ext(ext) 517 | .ok_or(Error::UnknownFileExtension(Some(ext.to_string())))?; 518 | let vtk_file = xml::import_async(path).await?; 519 | let exp_ft = xml::FileType::from(vtk_file.data_set_type); 520 | if ft != exp_ft { 521 | Err(Error::XML(xml::Error::TypeExtensionMismatch)) 522 | } else { 523 | Ok(vtk_file.try_into()?) 524 | } 525 | } 526 | #[cfg(not(feature = "xml"))] 527 | _ => Err(Error::UnknownFileExtension(None)), 528 | } 529 | } 530 | 531 | /// Import an XML VTK file in raw form. 532 | /// 533 | /// This importer performs a direct translation from the XML string to a Rust representation 534 | /// without any decoding or conversion. For a more complete import use [`import`]. 535 | /// 536 | /// [`VTKFile`] is used internally as an intermediate step for constructing the [`Vtk`] model, 537 | /// which has built-in facilities for loading pieces referenced in "parallel" XML formats as well 538 | /// as representing Legacy VTK formats, which are more compact when serialized. 539 | /// 540 | /// [`Vtk`]: model/struct.Vtk.html 541 | /// [`VTKFile`]: xml/struct.VTKFile.html 542 | /// [`import`]: fn.import.html 543 | #[cfg(feature = "unstable")] 544 | pub fn import_xml(file_path: impl AsRef) -> Result { 545 | let path = file_path.as_ref(); 546 | let ext = path 547 | .extension() 548 | .and_then(|s| s.to_str()) 549 | .ok_or(Error::UnknownFileExtension(None))?; 550 | 551 | // Check that the file extension is one of the known ones. 552 | let _ = xml::FileType::try_from_ext(ext) 553 | .ok_or(Error::UnknownFileExtension(Some(ext.to_string())))?; 554 | 555 | Ok(xml::import(path)?) 556 | } 557 | 558 | /// Import a legacy VTK file at the specified path. 559 | /// 560 | /// If the file is in binary format, numeric types will be interpreted in little endian format. 561 | /// For the default byte order used by most `.vtk` files use [`import`] or [`import_legacy_be`]. 562 | /// 563 | /// [`import`]: fn.import.html 564 | /// [`import_legacy_be`]: fn.import_legacy_be.html 565 | pub fn import_legacy_le(file_path: impl AsRef) -> Result { 566 | Vtk::import_vtk(file_path.as_ref(), parser::parse_le) 567 | } 568 | 569 | #[deprecated(since = "0.6.2", note = "Please use Vtk::import_legacy_le instead")] 570 | pub fn import_le(file_path: impl AsRef) -> Result { 571 | Vtk::import_legacy_le(file_path.as_ref()) 572 | } 573 | 574 | /// Import a legacy VTK file at the specified path. 575 | /// 576 | /// If the file is in binary format, numeric types will be interpreted in big endian format. 577 | /// This function behaves the same as [`import`], but expects the given file to be strictly in 578 | /// legacy `.vtk` format. 579 | /// 580 | /// [`import`]: fn.import.html 581 | pub fn import_legacy_be(file_path: impl AsRef) -> Result { 582 | Vtk::import_vtk(file_path.as_ref(), parser::parse_be) 583 | } 584 | 585 | #[deprecated(since = "0.6.2", note = "Please use Vtk::import_legacy_be instead")] 586 | pub fn import_be(file_path: impl AsRef) -> Result { 587 | Vtk::import_legacy_be(file_path.as_ref()) 588 | } 589 | 590 | /// Export given [`Vtk`] file to the specified file. 591 | /// 592 | /// The type of file exported is determined by the extension in `file_path`. 593 | /// 594 | /// Files ending in `.vtk` are exported in binary format. For exporting in ASCII, use 595 | /// [`export_ascii`]. 596 | /// 597 | /// Endianness is determined by the `byte_order` field of the [`Vtk`] type. 598 | /// 599 | /// # Examples 600 | /// 601 | /// ```no_run 602 | /// use vtkio::model::*; 603 | /// use std::path::PathBuf; 604 | /// let vtk = Vtk { 605 | /// version: Version::new_legacy(4,1), 606 | /// byte_order: ByteOrder::BigEndian, 607 | /// title: String::from("Tetrahedron"), 608 | /// file_path: Some(PathBuf::from("./test.vtk")), 609 | /// data: DataSet::inline(UnstructuredGridPiece { 610 | /// 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(), 611 | /// cells: Cells { 612 | /// cell_verts: VertexNumbers::Legacy { 613 | /// num_cells: 1, 614 | /// vertices: vec![4, 0, 1, 2, 3] 615 | /// }, 616 | /// types: vec![CellType::Tetra], 617 | /// }, 618 | /// data: Attributes::new(), 619 | /// }) 620 | /// }; 621 | /// vtk.export("test.vtk"); 622 | /// ``` 623 | /// 624 | /// [`Vtk`]: struct.Vtk.html 625 | /// [`export_ascii`]: fn.export_ascii.html 626 | pub fn export(self, file_path: impl AsRef) -> Result<(), Error> { 627 | self.export_impl(file_path.as_ref()) 628 | } 629 | 630 | /// A non-generic helper for the export function. 631 | fn export_impl(self, path: &Path) -> Result<(), Error> { 632 | let ext = path 633 | .extension() 634 | .and_then(|s| s.to_str()) 635 | .ok_or(Error::UnknownFileExtension(None))?; 636 | match ext { 637 | "vtk" => { 638 | let file = File::create(path)?; 639 | BinaryWriter(BufWriter::new(file)).write_vtk(self)?; 640 | Ok(()) 641 | } 642 | #[cfg(feature = "xml")] 643 | ext => { 644 | let ft = xml::FileType::try_from_ext(ext) 645 | .ok_or(Error::UnknownFileExtension(Some(ext.to_string())))?; 646 | let vtk_file = xml::VTKFile::try_from(self)?; 647 | let exp_ft = xml::FileType::from(vtk_file.data_set_type); 648 | if ft != exp_ft { 649 | Err(Error::XML(xml::Error::TypeExtensionMismatch)) 650 | } else { 651 | vtk_file.export(path)?; 652 | Ok(()) 653 | } 654 | } 655 | #[cfg(not(feature = "xml"))] 656 | _ => Err(Error::UnknownFileExtension(None)), 657 | } 658 | } 659 | 660 | /// Write the given VTK file in binary legacy format to the specified [`Write`](std::io::Write)r. 661 | /// 662 | /// # Examples 663 | /// 664 | /// Writing a binary file in big endian format representing a polygon mesh consisting of a single 665 | /// triangle: 666 | /// 667 | /// ``` 668 | /// use vtkio::model::*; // import model definition of a VTK file 669 | /// 670 | /// let mut vtk_bytes = Vec::::new(); 671 | /// 672 | /// Vtk { 673 | /// version: Version::new_legacy(2,0), 674 | /// byte_order: ByteOrder::BigEndian, 675 | /// title: String::from("Triangle example"), 676 | /// file_path: None, 677 | /// data: DataSet::inline(PolyDataPiece { 678 | /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(), 679 | /// polys: Some(VertexNumbers::Legacy { 680 | /// num_cells: 1, 681 | /// vertices: vec![3, 0, 1, 2] 682 | /// }), 683 | /// data: Attributes::new(), 684 | /// ..Default::default() 685 | /// }) 686 | /// }.write_legacy(&mut vtk_bytes); 687 | /// 688 | /// println!("{}", String::from_utf8_lossy(&vtk_bytes)); 689 | /// ``` 690 | pub fn write_legacy(self, writer: impl std::io::Write) -> Result<(), Error> { 691 | BinaryWriter(writer).write_vtk(self)?; 692 | Ok(()) 693 | } 694 | 695 | /// Write the given VTK file in binary legacy format to the specified [`Write`](std::fmt::Write)r. 696 | /// 697 | /// # Examples 698 | /// 699 | /// Writing an ASCII file representing a polygon mesh consisting of a single triangle: 700 | /// 701 | /// ``` 702 | /// use vtkio::model::*; // import model definition of a VTK file 703 | /// 704 | /// let mut vtk_string = String::new(); 705 | /// 706 | /// Vtk { 707 | /// version: Version::new_legacy(2,0), 708 | /// byte_order: ByteOrder::BigEndian, // Ignored 709 | /// title: String::from("Triangle example"), 710 | /// file_path: None, 711 | /// data: DataSet::inline(PolyDataPiece { 712 | /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(), 713 | /// polys: Some(VertexNumbers::Legacy { 714 | /// num_cells: 1, 715 | /// vertices: vec![3, 0, 1, 2] 716 | /// }), 717 | /// data: Attributes::new(), 718 | /// ..Default::default() 719 | /// }) 720 | /// }.write_legacy_ascii(&mut vtk_string); 721 | /// 722 | /// assert_eq!(vtk_string.as_str(), "\ 723 | /// ## vtk DataFile Version 2.0 724 | /// Triangle example 725 | /// ASCII 726 | /// 727 | /// DATASET POLYDATA 728 | /// POINTS 3 float 729 | /// 0 0 0 1 0 0 0 0 -1 730 | /// 731 | /// POLYGONS 1 4 732 | /// 3 0 1 2 733 | /// 734 | /// POINT_DATA 3 735 | /// 736 | /// CELL_DATA 1 737 | /// 738 | /// "); 739 | /// ``` 740 | pub fn write_legacy_ascii(self, writer: impl std::fmt::Write) -> Result<(), Error> { 741 | AsciiWriter(writer).write_vtk(self)?; 742 | Ok(()) 743 | } 744 | 745 | /// Write the given VTK file in modern XML format to the specified [`Write`](std::io::Write)r. 746 | /// 747 | /// # Examples 748 | /// 749 | /// Writing a binary file in big endian format representing a polygon mesh consisting of a single 750 | /// triangle: 751 | /// 752 | /// ``` 753 | /// use vtkio::model::*; // import model definition of a VTK file 754 | /// 755 | /// let mut vtk_bytes = Vec::::new(); 756 | /// 757 | /// Vtk { 758 | /// version: Version::new_xml(2,0), 759 | /// byte_order: ByteOrder::BigEndian, 760 | /// title: String::from("Triangle example"), 761 | /// file_path: None, 762 | /// data: DataSet::inline(PolyDataPiece { 763 | /// points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(), 764 | /// polys: Some(VertexNumbers::Legacy { 765 | /// num_cells: 1, 766 | /// vertices: vec![3, 0, 1, 2] 767 | /// }), 768 | /// data: Attributes::new(), 769 | /// ..Default::default() 770 | /// }) 771 | /// }.write_xml(&mut vtk_bytes); 772 | /// 773 | /// assert_eq!(String::from_utf8_lossy(&vtk_bytes), "\ 774 | /// \ 775 | /// \ 776 | /// \ 777 | /// \ 778 | /// \ 779 | /// AAAAAAAAACQAAAAAAAAAAAAAAAA/gAAAAAAAAAAAAAAAAAAAAAAAAL+AAAA=\ 780 | /// \ 781 | /// \ 782 | /// \ 783 | /// \ 784 | /// AAAAAAAAABgAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAI=\ 785 | /// \ 786 | /// \ 787 | /// AAAAAAAAAAgAAAAAAAAAAw==\ 788 | /// \ 789 | /// \ 790 | /// \ 791 | /// \ 792 | /// "); 793 | /// ``` 794 | #[cfg(feature = "xml")] 795 | pub fn write_xml(self, writer: impl Write) -> Result<(), Error> { 796 | let vtk_file = xml::VTKFile::try_from(self)?; 797 | vtk_file.write(writer)?; 798 | Ok(()) 799 | } 800 | 801 | /// Export the VTK data to the specified path in little endian binary format. 802 | /// 803 | /// This function is used as [`export`] but overrides endiannes. 804 | /// 805 | /// [`export`]: fn.export.html 806 | pub fn export_le(self, file_path: impl AsRef) -> Result<(), Error> { 807 | let file = File::create(file_path.as_ref())?; 808 | BinaryWriter(BufWriter::new(file)).write_vtk_le(self)?; 809 | Ok(()) 810 | } 811 | 812 | /// Export the VTK data to the specified path in big endian binary format. 813 | /// 814 | /// This function is used as [`export`] but overrides endiannes. 815 | /// 816 | /// [`export`]: fn.export.html 817 | pub fn export_be(self, file_path: impl AsRef) -> Result<(), Error> { 818 | let file = File::create(file_path.as_ref())?; 819 | BinaryWriter(BufWriter::new(file)).write_vtk_be(self)?; 820 | Ok(()) 821 | } 822 | 823 | /// Export VTK data to the specified file in ASCII format. 824 | /// 825 | /// # Examples 826 | /// 827 | /// ```no_run 828 | /// use vtkio::model::*; 829 | /// use std::path::PathBuf; 830 | /// let vtk = Vtk { 831 | /// version: Version::new_legacy(4,1), 832 | /// title: String::from("Tetrahedron"), 833 | /// byte_order: ByteOrder::BigEndian, 834 | /// file_path: Some(PathBuf::from("./test.vtk")), 835 | /// data: DataSet::inline(UnstructuredGridPiece { 836 | /// 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(), 837 | /// cells: Cells { 838 | /// cell_verts: VertexNumbers::Legacy { 839 | /// num_cells: 1, 840 | /// vertices: vec![4, 0, 1, 2, 3] 841 | /// }, 842 | /// types: vec![CellType::Tetra], 843 | /// }, 844 | /// data: Attributes::new(), 845 | /// }) 846 | /// }; 847 | /// vtk.export_ascii("test.vtk"); 848 | /// ``` 849 | pub fn export_ascii(self, file_path: impl AsRef) -> Result<(), Error> { 850 | // Ascii formats are typically used for small files, so it makes sense to make the write 851 | // in-memory first. 852 | let mut out_str = AsciiWriter(String::new()); 853 | out_str.write_vtk(self)?; 854 | let mut file = File::create(file_path.as_ref())?; 855 | file.write_all(out_str.0.as_bytes())?; 856 | Ok(()) 857 | } 858 | } 859 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian}; 4 | use nom::branch::{alt, permutation}; 5 | use nom::bytes::complete::{is_not, tag, tag_no_case}; 6 | use nom::character::complete::{line_ending, multispace0, u32 as parse_u32, u8 as parse_u8}; 7 | use nom::combinator::{complete, cut, eof, flat_map, map, map_opt, map_res, opt}; 8 | use nom::multi::{many0, many_m_n}; 9 | use nom::sequence::{pair, preceded, separated_pair, terminated}; 10 | use nom::{IResult, Parser}; 11 | use num_traits::FromPrimitive; 12 | 13 | use crate::basic::*; 14 | use crate::model::ByteOrder as ByteOrderTag; 15 | use crate::model::*; 16 | 17 | /* 18 | * Parsing routines 19 | */ 20 | 21 | fn version(input: &[u8]) -> IResult<&[u8], Version> { 22 | let (input, _) = ( 23 | sp(tag("#")), 24 | sp(tag_no_case("vtk")), 25 | sp(tag_no_case("DataFile")), 26 | sp(tag_no_case("Version")), 27 | ) 28 | .parse(input)?; 29 | sp(map( 30 | separated_pair(parse_u32, tag("."), parse_u32), 31 | |(major, minor)| Version::new_legacy(major, minor), 32 | )) 33 | .parse(input) 34 | } 35 | 36 | fn file_type(input: &[u8]) -> IResult<&[u8], FileType> { 37 | alt(( 38 | map(sp(tag_no_case("ASCII")), |_| FileType::Ascii), 39 | map(sp(tag_no_case("BINARY")), |_| FileType::Binary), 40 | )) 41 | .parse(input) 42 | } 43 | 44 | fn title(input: &[u8]) -> IResult<&[u8], &str> { 45 | map_res(is_not("\r\n"), std::str::from_utf8).parse(input) 46 | } 47 | 48 | fn header(input: &[u8]) -> IResult<&[u8], (Version, String, FileType)> { 49 | let (input, _) = multispace0(input)?; 50 | terminated( 51 | ( 52 | line(version), 53 | line(map(title, String::from)), 54 | line(file_type), 55 | ), 56 | multispace0, 57 | ) 58 | .parse(input) 59 | } 60 | 61 | fn data_type(input: &[u8]) -> IResult<&[u8], ScalarType> { 62 | alt(( 63 | map(tag_no_case("bit"), |_| ScalarType::Bit), 64 | map(tag_no_case("int"), |_| ScalarType::I32), 65 | map(tag_no_case("char"), |_| ScalarType::I8), 66 | map(tag_no_case("long"), |_| ScalarType::I64), 67 | map(tag_no_case("short"), |_| ScalarType::I16), 68 | map(tag_no_case("float"), |_| ScalarType::F32), 69 | map(tag_no_case("double"), |_| ScalarType::F64), 70 | map(tag_no_case("unsigned_int"), |_| ScalarType::U32), 71 | map(tag_no_case("unsigned_char"), |_| ScalarType::U8), 72 | map(tag_no_case("unsigned_long"), |_| ScalarType::U64), 73 | map(tag_no_case("vtkIdType"), |_| ScalarType::I32), 74 | )) 75 | .parse(input) 76 | } 77 | 78 | fn name(input: &[u8]) -> IResult<&[u8], &str> { 79 | map_res(is_not(" \t\r\n"), std::str::from_utf8).parse(input) 80 | } 81 | 82 | /// Recognize and throw away `METADATA` block. Metadata is separated by an empty line. 83 | fn meta(input: &[u8]) -> IResult<&[u8], ()> { 84 | tagged_block( 85 | tag_no_case("METADATA"), 86 | map( 87 | alt(( 88 | eof, 89 | complete(|mut input| { 90 | // Loop until an empty line or eof is found (should be more efficient than 91 | // `alt(take_until(), take_until()))` 92 | loop { 93 | // Consume everything until and including the next line ending 94 | (input, _) = preceded(opt(is_not("\n\r")), line_ending).parse(input)?; 95 | match alt((eof, line_ending)).parse(input) { 96 | k @ Ok(_) => return k, 97 | e @ Err(nom::Err::Failure(_) | nom::Err::Incomplete(_)) => _ = e?, 98 | _ => {} 99 | }; 100 | } 101 | }), 102 | )), 103 | |_| (), 104 | ), 105 | ) 106 | .parse(input) 107 | } 108 | 109 | /// Parses an array of three values preceded by a tag. E.g. "DIMENSIONS 1 2 3" 110 | fn array3<'a, T: FromAscii>( 111 | tag: &'static str, 112 | ) -> impl Parser<&'a [u8], Output = [T; 3], Error = nom::error::Error<&'a [u8]>> { 113 | preceded( 114 | sp(tag_no_case(tag)), 115 | cut(map( 116 | (sp(T::from_ascii), sp(T::from_ascii), sp(T::from_ascii)), 117 | |(nx, ny, nz)| [nx, ny, nz], 118 | )), 119 | ) 120 | } 121 | 122 | /// Helper struct for parsing polygon topology sections. 123 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 124 | enum PolyDataTopology { 125 | Verts = 0, 126 | Lines, 127 | Polys, 128 | Strips, 129 | } 130 | 131 | /** 132 | * Mesh data parsing. 133 | */ 134 | pub struct VtkParser(PhantomData); 135 | 136 | impl VtkParser { 137 | fn points(input: &[u8], ft: FileType) -> IResult<&[u8], IOBuffer> { 138 | tagged_block(tag_no_case("POINTS"), |input| { 139 | let (input, (n, d_t)) = line((sp(parse_u32), sp(data_type))).parse(input)?; 140 | 141 | match d_t { 142 | ScalarType::F32 => parse_data_buffer::(input, 3 * n as usize, ft), 143 | ScalarType::F64 => parse_data_buffer::(input, 3 * n as usize, ft), 144 | _ => Err(nom::Err::Error(nom::error::make_error( 145 | input, 146 | nom::error::ErrorKind::Switch, 147 | ))), 148 | } 149 | }) 150 | .parse(input) 151 | } 152 | 153 | /// Parse cell topology indices in the legacy way. 154 | /// 155 | /// Cells are stored as a single contiguous array with the format `n v0 v1 ... vn` for each cell. 156 | fn legacy_cell_topo( 157 | input: &[u8], 158 | n: u32, 159 | size: u32, 160 | ft: FileType, 161 | ) -> IResult<&[u8], VertexNumbers> { 162 | map( 163 | |i| parse_data_vec::(i, size as usize, ft), 164 | |data| VertexNumbers::Legacy { 165 | num_cells: n, 166 | vertices: data, 167 | }, 168 | ) 169 | .parse(input) 170 | } 171 | 172 | /// Parse cell topology indices in modern way using offsets and connectivity arras. 173 | /// 174 | /// Cells are stored as two arrays: OFFSETS and CONNECTIVITY, which are specified separately. 175 | fn modern_cell_topo( 176 | input: &[u8], 177 | n: u32, 178 | size: u32, 179 | ft: FileType, 180 | ) -> IResult<&[u8], VertexNumbers> { 181 | map( 182 | ( 183 | |i| Self::topo(i, "OFFSETS", n, ft), 184 | |i| Self::topo(i, "CONNECTIVITY", size, ft), 185 | ), 186 | |(offsets, connectivity)| VertexNumbers::XML { 187 | offsets, 188 | connectivity, 189 | }, 190 | ) 191 | .parse(input) 192 | } 193 | 194 | /// Parse either a CONNECTIVITY or OFFSETS array for modern topology. 195 | fn topo<'a>( 196 | input: &'a [u8], 197 | tag: &'static str, 198 | n: u32, 199 | ft: FileType, 200 | ) -> IResult<&'a [u8], Vec> { 201 | tagged_block( 202 | line(pair(tag_no_case(tag), sp(tag_no_case("vtktypeint64")))), 203 | |input| parse_data_vec::(input, n as usize, ft), 204 | ) 205 | .parse(input) 206 | } 207 | 208 | /// Parse a collection of cells. The given tag should be one of 209 | /// * "CELLS" 210 | /// * "VERTICES" 211 | /// * "LINES" 212 | /// * "POLYGONS" 213 | /// * "TRIANGLE_STRIPS" 214 | fn cell_verts<'a>( 215 | input: &'a [u8], 216 | tag: &'static str, 217 | ft: FileType, 218 | ) -> IResult<&'a [u8], VertexNumbers> { 219 | tagged_block(tag_no_case(tag), |input| { 220 | let (input, (n, size)) = line((sp(parse_u32), sp(parse_u32))).parse(input)?; 221 | alt(( 222 | move |i| Self::modern_cell_topo(i, n, size, ft), 223 | move |i| Self::legacy_cell_topo(i, n, size, ft), 224 | )) 225 | .parse(input) 226 | }) 227 | .parse(input) 228 | } 229 | 230 | fn coordinates<'a>( 231 | input: &'a [u8], 232 | axis_tag: &'static str, 233 | ft: FileType, 234 | ) -> IResult<&'a [u8], IOBuffer> { 235 | tagged_block(tag_no_case(axis_tag), |input| { 236 | let (input, (n, dt)) = line(pair(sp(parse_u32), sp(data_type))).parse(input)?; 237 | match dt { 238 | ScalarType::F32 => parse_data_buffer::(input, n as usize, ft), 239 | ScalarType::F64 => parse_data_buffer::(input, n as usize, ft), 240 | _ => Err(nom::Err::Error(nom::error::make_error( 241 | input, 242 | nom::error::ErrorKind::Switch, 243 | ))), 244 | } 245 | }) 246 | .parse(input) 247 | } 248 | 249 | /** 250 | * Attribute Parsing 251 | */ 252 | 253 | fn attribute_data( 254 | input: &[u8], 255 | n: usize, 256 | data_type: ScalarType, 257 | ft: FileType, 258 | ) -> IResult<&[u8], IOBuffer> { 259 | match data_type { 260 | ScalarType::Bit => parse_data_bit_buffer(input, n, ft), 261 | ScalarType::U8 => parse_data_buffer_u8(input, n, ft), 262 | ScalarType::I8 => parse_data_buffer_i8(input, n, ft), 263 | ScalarType::U16 => parse_data_buffer::(input, n, ft), 264 | ScalarType::I16 => parse_data_buffer::(input, n, ft), 265 | ScalarType::U32 => parse_data_buffer::(input, n, ft), 266 | ScalarType::I32 => parse_data_buffer::(input, n, ft), 267 | ScalarType::U64 => parse_data_buffer::(input, n, ft), 268 | ScalarType::I64 => parse_data_buffer::(input, n, ft), 269 | ScalarType::F32 => parse_data_buffer::(input, n, ft), 270 | ScalarType::F64 => parse_data_buffer::(input, n, ft), 271 | } 272 | } 273 | 274 | fn attribute_scalars( 275 | input: &[u8], 276 | num_elements: usize, 277 | ft: FileType, 278 | ) -> IResult<&[u8], Attribute> { 279 | tagged_block(tag_no_case("SCALARS"), |input| { 280 | let (input, (aname, dt, num_comp)) = 281 | line((sp(name), sp(data_type), opt(parse_u32))).parse(input)?; 282 | let (input, lookup_tbl_name) = 283 | opt(line(preceded(sp(tag_no_case("LOOKUP_TABLE")), sp(name)))).parse(input)?; 284 | let (input, data) = 285 | Self::attribute_data(input, num_comp.unwrap_or(1) as usize * num_elements, dt, ft)?; 286 | let (input, _) = opt(meta).parse(input)?; 287 | 288 | Ok(( 289 | input, 290 | Attribute::DataArray(DataArray { 291 | name: String::from(aname), 292 | elem: ElementType::Scalars { 293 | num_comp: num_comp.unwrap_or(1), 294 | lookup_table: lookup_tbl_name 295 | .and_then(|n| (n != "default").then(|| String::from(n))), 296 | }, 297 | data, 298 | }), 299 | )) 300 | }) 301 | .parse(input) 302 | } 303 | 304 | fn attribute_lookup_table(input: &[u8], ft: FileType) -> IResult<&[u8], Attribute> { 305 | tagged_block(tag_no_case("LOOKUP_TABLE"), |input| { 306 | let (input, (name, num_elements)) = line((sp(name), sp(parse_u32))).parse(input)?; 307 | let (input, data) = match ft { 308 | FileType::Ascii => { 309 | Self::attribute_data(input, 4 * num_elements as usize, ScalarType::F32, ft)? 310 | } 311 | FileType::Binary => { 312 | Self::attribute_data(input, 4 * num_elements as usize, ScalarType::U8, ft)? 313 | } 314 | }; 315 | let (input, _) = opt(meta).parse(input)?; 316 | 317 | Ok(( 318 | input, 319 | Attribute::DataArray(DataArray { 320 | name: String::from(name), 321 | elem: ElementType::LookupTable, 322 | data, 323 | }), 324 | )) 325 | }) 326 | .parse(input) 327 | } 328 | 329 | /// Helper to `attribute_color_scalars`. This function calls the appropriate data parser for color 330 | /// scalars. 331 | fn attribute_color_scalars_data( 332 | input: &[u8], 333 | n: usize, 334 | ft: FileType, 335 | ) -> IResult<&[u8], IOBuffer> { 336 | match ft { 337 | FileType::Ascii => Self::attribute_data(input, n, ScalarType::F32, ft), 338 | FileType::Binary => Self::attribute_data(input, n, ScalarType::U8, ft), 339 | } 340 | } 341 | 342 | fn attribute_color_scalars( 343 | input: &[u8], 344 | num_elements: usize, 345 | ft: FileType, 346 | ) -> IResult<&[u8], Attribute> { 347 | tagged_block(tag_no_case("COLOR_SCALARS"), |input| { 348 | let (input, (name, num_comp)) = line((sp(name), sp(parse_u32))).parse(input)?; 349 | let (input, data) = 350 | Self::attribute_color_scalars_data(input, num_comp as usize * num_elements, ft)?; 351 | let (input, _) = opt(meta).parse(input)?; 352 | Ok(( 353 | input, 354 | Attribute::DataArray(DataArray { 355 | name: String::from(name), 356 | elem: ElementType::ColorScalars(num_comp), 357 | data, 358 | }), 359 | )) 360 | }) 361 | .parse(input) 362 | } 363 | 364 | fn attribute_vectors( 365 | input: &[u8], 366 | num_elements: usize, 367 | ft: FileType, 368 | ) -> IResult<&[u8], Attribute> { 369 | Self::attribute_fixed_dim(input, "VECTORS", 3, ElementType::Vectors, num_elements, ft) 370 | } 371 | 372 | fn attribute_normals( 373 | input: &[u8], 374 | num_elements: usize, 375 | ft: FileType, 376 | ) -> IResult<&[u8], Attribute> { 377 | Self::attribute_fixed_dim(input, "NORMALS", 3, ElementType::Normals, num_elements, ft) 378 | } 379 | 380 | fn attribute_tex_coords( 381 | input: &[u8], 382 | num_elements: usize, 383 | ft: FileType, 384 | ) -> IResult<&[u8], Attribute> { 385 | tagged_block(tag_no_case("TEXTURE_COORDINATES"), |input| { 386 | let (input, (name, dim, dt)) = 387 | line((sp(name), sp(parse_u32), sp(data_type))).parse(input)?; 388 | let (input, data) = Self::attribute_data(input, dim as usize * num_elements, dt, ft)?; 389 | let (input, _) = opt(meta).parse(input)?; 390 | Ok(( 391 | input, 392 | Attribute::DataArray(DataArray { 393 | name: String::from(name), 394 | elem: ElementType::TCoords(dim), 395 | data, 396 | }), 397 | )) 398 | }) 399 | .parse(input) 400 | } 401 | 402 | fn attribute_tensors( 403 | input: &[u8], 404 | num_elements: usize, 405 | ft: FileType, 406 | ) -> IResult<&[u8], Attribute> { 407 | Self::attribute_fixed_dim(input, "TENSORS", 9, ElementType::Tensors, num_elements, ft) 408 | } 409 | 410 | fn attribute_field_array(input: &[u8], ft: FileType) -> IResult<&[u8], FieldArray> { 411 | let (input, (name, num_comp, num_tuples, dt)) = line(preceded( 412 | multispace0, 413 | (sp(name), sp(parse_u32), sp(parse_u32), sp(data_type)), 414 | )) 415 | .parse(input)?; 416 | let (input, data) = 417 | Self::attribute_data(input, (num_comp as usize) * (num_tuples as usize), dt, ft)?; 418 | let (input, _) = opt(meta).parse(input)?; 419 | Ok(( 420 | input, 421 | FieldArray { 422 | name: String::from(name), 423 | elem: num_comp, 424 | data, 425 | }, 426 | )) 427 | } 428 | 429 | fn attribute_field(input: &[u8], ft: FileType) -> IResult<&[u8], Attribute> { 430 | tagged_block(tag_no_case("FIELD"), |input| { 431 | let (input, (name, n)) = line((sp(name), sp(parse_u32))).parse(input)?; 432 | let (input, data_array) = many_m_n(n as usize, n as usize, |i| { 433 | Self::attribute_field_array(i, ft) 434 | }) 435 | .parse(input)?; 436 | 437 | Ok(( 438 | input, 439 | (Attribute::Field { 440 | name: String::from(name), 441 | data_array, 442 | }), 443 | )) 444 | }) 445 | .parse(input) 446 | } 447 | 448 | fn attribute_fixed_dim<'a>( 449 | input: &'a [u8], 450 | tag: &'static str, 451 | dim: usize, 452 | elem: ElementType, 453 | num_elements: usize, 454 | ft: FileType, 455 | ) -> IResult<&'a [u8], Attribute> { 456 | tagged_block(tag_no_case(tag), |input| { 457 | let (input, (name, dt)) = line((sp(name), sp(data_type))).parse(input)?; 458 | let (input, data) = Self::attribute_data(input, dim * num_elements, dt, ft)?; 459 | let (input, _) = opt(meta).parse(input)?; 460 | Ok(( 461 | input, 462 | Attribute::DataArray(DataArray { 463 | name: String::from(name), 464 | elem: elem.clone(), 465 | data, 466 | }), 467 | )) 468 | }) 469 | .parse(input) 470 | } 471 | 472 | fn attribute(input: &[u8], num_elements: usize, ft: FileType) -> IResult<&[u8], Attribute> { 473 | alt(( 474 | |i| Self::attribute_scalars(i, num_elements, ft), 475 | |i| Self::attribute_color_scalars(i, num_elements, ft), 476 | |i| Self::attribute_lookup_table(i, ft), 477 | |i| Self::attribute_vectors(i, num_elements, ft), 478 | |i| Self::attribute_normals(i, num_elements, ft), 479 | |i| Self::attribute_tex_coords(i, num_elements, ft), 480 | |i| Self::attribute_tensors(i, num_elements, ft), 481 | |i| Self::attribute_field(i, ft), 482 | )) 483 | .parse(input) 484 | } 485 | 486 | /// Parse CELL_DATA and POINT_DATA attributes 487 | fn point_or_cell_attributes<'a>( 488 | input: &'a [u8], 489 | tag: &'static str, 490 | ft: FileType, 491 | ) -> IResult<&'a [u8], Vec> { 492 | alt(( 493 | tagged_block( 494 | tag_no_case(tag), 495 | flat_map(sp(parse_u32), |n| { 496 | many0(move |i| Self::attribute(i, n as usize, ft)) 497 | }), 498 | ), 499 | map(ws(eof), |_| Vec::new()), 500 | )) 501 | .parse(input) 502 | } 503 | 504 | /// Parse all DataSet attributes 505 | fn attributes(input: &[u8], ft: FileType) -> IResult<&[u8], Attributes> { 506 | map( 507 | ( 508 | opt(|i| Self::point_or_cell_attributes(i, "CELL_DATA", ft)), 509 | opt(|i| Self::point_or_cell_attributes(i, "POINT_DATA", ft)), 510 | opt(|i| Self::point_or_cell_attributes(i, "CELL_DATA", ft)), 511 | ), 512 | |(c1, p, c2)| Attributes { 513 | point: p.unwrap_or_default(), 514 | cell: if let Some(c) = c1 { 515 | c 516 | } else { 517 | c2.unwrap_or_default() 518 | }, 519 | }, 520 | ) 521 | .parse(input) 522 | } 523 | 524 | /// Parse STRUCTURED_POINTS type dataset. 525 | fn structured_points(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> { 526 | tagged_block(tag_no_case_line("STRUCTURED_POINTS"), |input| { 527 | let (input, parms) = permutation(( 528 | line(array3("DIMENSIONS")), 529 | line(array3("ORIGIN")), 530 | line(alt((array3("SPACING"), array3("ASPECT_RATIO")))), 531 | )) 532 | .parse(input)?; 533 | 534 | let (input, data) = Self::attributes(input, ft)?; 535 | Ok(( 536 | input, 537 | DataSet::ImageData { 538 | extent: Extent::Dims(parms.0), 539 | origin: parms.1, 540 | spacing: parms.2, 541 | meta: None, 542 | pieces: vec![Piece::Inline(Box::new(ImageDataPiece { 543 | extent: Extent::Dims(parms.0), 544 | data, 545 | }))], 546 | }, 547 | )) 548 | }) 549 | .parse(input) 550 | } 551 | 552 | /// Parse STRUCTURED_GRID type dataset 553 | fn structured_grid(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> { 554 | tagged_block(tag_no_case_line("STRUCTURED_GRID"), |input| { 555 | let (input, dims) = line(array3("DIMENSIONS")).parse(input)?; 556 | let (input, points) = Self::points(input, ft)?; 557 | let (input, _) = opt(meta).parse(input)?; 558 | let (input, data) = Self::attributes(input, ft)?; 559 | 560 | Ok(( 561 | input, 562 | DataSet::inline(StructuredGridPiece { 563 | extent: Extent::Dims(dims), 564 | points, 565 | data, 566 | }), 567 | )) 568 | }) 569 | .parse(input) 570 | } 571 | 572 | /// Parse RECTILINEAR_GRID type dataset 573 | fn rectilinear_grid(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> { 574 | tagged_block(tag_no_case_line("RECTILINEAR_GRID"), |input| { 575 | let (input, dims) = line(array3("DIMENSIONS")).parse(input)?; 576 | let (input, (x, y, z)) = ( 577 | |i| Self::coordinates(i, "X_COORDINATES", ft), 578 | |i| Self::coordinates(i, "Y_COORDINATES", ft), 579 | |i| Self::coordinates(i, "Z_COORDINATES", ft), 580 | ) 581 | .parse(input)?; 582 | let (input, data) = Self::attributes(input, ft)?; 583 | let (input, _) = opt(meta).parse(input)?; 584 | 585 | Ok(( 586 | input, 587 | DataSet::inline(RectilinearGridPiece { 588 | extent: Extent::Dims(dims), 589 | coords: Coordinates { x, y, z }, 590 | data, 591 | }), 592 | )) 593 | }) 594 | .parse(input) 595 | } 596 | 597 | /// Parse FIELD type dataset 598 | fn field_data(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> { 599 | Self::attribute_field(input, ft).map(|(input, field)| { 600 | if let Attribute::Field { name, data_array } = field { 601 | (input, DataSet::Field { name, data_array }) 602 | } else { 603 | unreachable!("attribute_field should always return an Attribute::Field"); 604 | } 605 | }) 606 | } 607 | 608 | /// Parse a single ASCII cell type value. Essentially a byte converted to `CellType` enum. 609 | fn cell_type(input: &[u8]) -> IResult<&[u8], CellType> { 610 | map_opt(parse_u8, CellType::from_u8).parse(input) 611 | } 612 | 613 | /// Parse a single binary cell type value. Essentially a byte converted to `CellType` enum. 614 | fn cell_type_binary(input: &[u8]) -> IResult<&[u8], CellType> { 615 | map_opt(i32::from_binary::, |x| CellType::from_u8(x as u8)).parse(input) 616 | } 617 | 618 | fn cell_type_data(input: &[u8], n: usize, ft: FileType) -> IResult<&[u8], Vec> { 619 | match ft { 620 | FileType::Ascii => many_m_n(n, n, ws(Self::cell_type)).parse(input), 621 | FileType::Binary => many_m_n(n, n, Self::cell_type_binary).parse(input), 622 | } 623 | } 624 | 625 | /// Parse cell types for unstructured grids 626 | fn cell_types(input: &[u8], ft: FileType) -> IResult<&[u8], Vec> { 627 | tagged_block(tag_no_case("CELL_TYPES"), |input| { 628 | let (input, n) = line(sp(parse_u32)).parse(input)?; 629 | Self::cell_type_data(input, n as usize, ft) 630 | }) 631 | .parse(input) 632 | } 633 | 634 | /// Parse UNSTRUCTURED_GRID type dataset 635 | fn unstructured_grid(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> { 636 | tagged_block(tag_no_case_line("UNSTRUCTURED_GRID"), |input| { 637 | let (input, p) = Self::points(input, ft)?; 638 | let (input, _) = opt(meta).parse(input)?; 639 | let (input, cell_verts) = Self::cell_verts(input, "CELLS", ft)?; 640 | let (input, types) = Self::cell_types(input, ft)?; 641 | let (input, data) = Self::attributes(input, ft)?; 642 | 643 | Ok(( 644 | input, 645 | DataSet::inline(UnstructuredGridPiece { 646 | points: p, 647 | cells: Cells { cell_verts, types }, 648 | data, 649 | }), 650 | )) 651 | }) 652 | .parse(input) 653 | } 654 | 655 | /// Parse PolyData topology 656 | fn poly_data_topo( 657 | input: &[u8], 658 | ft: FileType, 659 | ) -> IResult<&[u8], (PolyDataTopology, VertexNumbers)> { 660 | alt(( 661 | map( 662 | |i| Self::cell_verts(i, "LINES", ft), 663 | |x| (PolyDataTopology::Lines, x), 664 | ), 665 | map( 666 | |i| Self::cell_verts(i, "POLYGONS", ft), 667 | |x| (PolyDataTopology::Polys, x), 668 | ), 669 | map( 670 | |i| Self::cell_verts(i, "VERTICES", ft), 671 | |x| (PolyDataTopology::Verts, x), 672 | ), 673 | map( 674 | |i| Self::cell_verts(i, "TRIANGLE_STRIPS", ft), 675 | |x| (PolyDataTopology::Strips, x), 676 | ), 677 | )) 678 | .parse(input) 679 | } 680 | 681 | /// Parse POLYDATA type dataset 682 | fn poly_data(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> { 683 | tagged_block(tag_no_case_line("POLYDATA"), |input| { 684 | let (input, points) = Self::points(input, ft)?; 685 | let (input, _) = opt(meta).parse(input)?; 686 | let (input, topo1) = opt(|i| Self::poly_data_topo(i, ft)).parse(input)?; 687 | let (input, topo2) = opt(|i| Self::poly_data_topo(i, ft)).parse(input)?; 688 | let (input, topo3) = opt(|i| Self::poly_data_topo(i, ft)).parse(input)?; 689 | let (input, topo4) = opt(|i| Self::poly_data_topo(i, ft)).parse(input)?; 690 | let (input, data) = Self::attributes(input, ft)?; 691 | 692 | // Avoid clones of the topology data by "taking" them out of the (unordered) options 693 | let mut topos = [topo1, topo2, topo3, topo4]; 694 | let take_topo = |topos: &mut [Option<(PolyDataTopology, VertexNumbers)>], topo_type| { 695 | topos 696 | .iter() 697 | .position(|x| matches!(x, Some((tt, _)) if tt == &topo_type)) 698 | .and_then(|pos| topos[pos].take().map(|(_, topo)| topo)) 699 | }; 700 | 701 | Ok(( 702 | input, 703 | DataSet::inline(PolyDataPiece { 704 | points, 705 | verts: take_topo(&mut topos, PolyDataTopology::Verts), 706 | lines: take_topo(&mut topos, PolyDataTopology::Lines), 707 | polys: take_topo(&mut topos, PolyDataTopology::Polys), 708 | strips: take_topo(&mut topos, PolyDataTopology::Strips), 709 | data, 710 | }), 711 | )) 712 | }) 713 | .parse(input) 714 | } 715 | 716 | fn dataset(input: &[u8], ft: FileType) -> IResult<&[u8], DataSet> { 717 | alt(( 718 | tagged_block( 719 | tag_no_case("DATASET"), 720 | alt(( 721 | |i| Self::poly_data(i, ft), 722 | |i| Self::structured_grid(i, ft), 723 | |i| Self::rectilinear_grid(i, ft), 724 | |i| Self::structured_points(i, ft), 725 | |i| Self::unstructured_grid(i, ft), 726 | )), 727 | ), 728 | |i| Self::field_data(i, ft), 729 | )) 730 | .parse(input) 731 | } 732 | 733 | /// Parse the entire VTK file 734 | fn vtk(input: &[u8]) -> IResult<&[u8], Vtk> { 735 | complete(|input| { 736 | let (input, h) = header(input)?; 737 | let (input, d) = Self::dataset(input, h.2)?; 738 | // Ignore all trailing spaces and newlines 739 | let (input, _) = multispace0(input)?; 740 | 741 | Ok(( 742 | input, 743 | Vtk { 744 | version: h.0, 745 | // This is ignored in Legacy formats 746 | byte_order: ByteOrderTag::new::(), 747 | title: h.1, 748 | data: d, 749 | file_path: None, 750 | }, 751 | )) 752 | }) 753 | .parse(input) 754 | } 755 | } 756 | 757 | /// Parse the entire VTK file using native endian byte order. 758 | pub fn parse_ne(input: &[u8]) -> IResult<&[u8], Vtk> { 759 | VtkParser::::vtk(input) 760 | } 761 | 762 | /// Parse the entire VTK file using little endian byte order. 763 | pub fn parse_le(input: &[u8]) -> IResult<&[u8], Vtk> { 764 | VtkParser::::vtk(input) 765 | } 766 | 767 | /// Parse the entire VTK file using big endian byte order. 768 | /// 769 | /// This is the default VTK byte order. Binary `.vtk` files produced by ParaView are in big endian 770 | /// form. 771 | pub fn parse_be(input: &[u8]) -> IResult<&[u8], Vtk> { 772 | VtkParser::::vtk(input) 773 | } 774 | 775 | #[cfg(test)] 776 | mod tests { 777 | use super::*; 778 | 779 | #[test] 780 | fn file_type_test() { 781 | let f = file_type("BINARY".as_bytes()); 782 | assert_eq!(f, Ok((&b""[..], FileType::Binary))); 783 | let f = file_type("ASCII".as_bytes()); 784 | assert_eq!(f, Ok((&b""[..], FileType::Ascii))); 785 | } 786 | #[test] 787 | fn version_test() { 788 | let f = version("# vtk DataFile Version 2.0 \ntitle\n".as_bytes()); 789 | assert_eq!(f, Ok(("\ntitle\n".as_bytes(), Version::new_legacy(2, 0)))); 790 | } 791 | #[test] 792 | fn title_test() { 793 | let f = title("This is a title\nBINARY".as_bytes()); 794 | assert_eq!(f, Ok(("\nBINARY".as_bytes(), "This is a title"))); 795 | } 796 | #[test] 797 | fn header_test() { 798 | let f = header(" \t\n# vtk DataFile Version 2.0 \nThis is a title\nBINARY\n".as_bytes()); 799 | assert_eq!( 800 | f, 801 | Ok(( 802 | "".as_bytes(), 803 | ( 804 | Version::new_legacy(2, 0), 805 | "This is a title".to_string(), 806 | FileType::Binary 807 | ) 808 | )) 809 | ); 810 | } 811 | #[test] 812 | fn meta_test() { 813 | assert_eq!(meta("METADATA".as_bytes()), Ok(("".as_bytes(), ()))); 814 | assert_eq!(meta(" \tMETADATA".as_bytes()), Ok(("".as_bytes(), ()))); 815 | assert_eq!(meta("METADATA\n".as_bytes()), Ok(("".as_bytes(), ()))); 816 | assert_eq!(meta("METADATA\n\n".as_bytes()), Ok(("".as_bytes(), ()))); 817 | assert_eq!( 818 | meta("METADATA\n\nPOLYGONS 1 4".as_bytes()), 819 | Ok(("POLYGONS 1 4".as_bytes(), ())) 820 | ); 821 | assert_eq!( 822 | meta("METADATA\nINFORMATION 2\nNAME L2_NORM_RANGE LOCATION vtkDataArray\nDATA 2 0.865742 1.73177\n".as_bytes()), 823 | Ok(("".as_bytes(), ())) 824 | ); 825 | assert_eq!( 826 | meta("METADATA\nINFORMATION 2\nNAME L2_NORM_RANGE LOCATION vtkDataArray\nDATA 2 0.865742 1.73177\n\nPOLYGONS 1 4".as_bytes()), 827 | Ok(("POLYGONS 1 4".as_bytes(), ())) 828 | ); 829 | } 830 | #[test] 831 | fn points_test() { 832 | let in1 = "POINTS 0 float\n"; 833 | let in2 = "POINTS 3 float\n2 45 2 3 4 1 46 2 0\nother"; 834 | let f = VtkParser::::points(in1.as_bytes(), FileType::Ascii); 835 | assert_eq!(f, Ok(("".as_bytes(), Vec::::new().into()))); 836 | let f = VtkParser::::points(in2.as_bytes(), FileType::Ascii); 837 | assert_eq!( 838 | f, 839 | Ok(( 840 | "other".as_bytes(), 841 | vec![2.0f32, 45., 2., 3., 4., 1., 46., 2., 0.].into() 842 | )) 843 | ); 844 | } 845 | #[test] 846 | fn cells_test() { 847 | let in1 = "CELLS 0 0\n"; 848 | let in2 = "CELLS 1 3\n2 1 2\nother"; 849 | 850 | let f = VtkParser::::cell_verts(in1.as_bytes(), "CELLS", FileType::Ascii); 851 | assert_eq!( 852 | f, 853 | Ok(( 854 | "".as_bytes(), 855 | VertexNumbers::Legacy { 856 | num_cells: 0, 857 | vertices: vec![] 858 | } 859 | )) 860 | ); 861 | let f = VtkParser::::cell_verts(in2.as_bytes(), "CELLS", FileType::Ascii); 862 | assert_eq!( 863 | f, 864 | Ok(( 865 | "other".as_bytes(), 866 | VertexNumbers::Legacy { 867 | num_cells: 1, 868 | vertices: vec![2, 1, 2] 869 | } 870 | )) 871 | ); 872 | } 873 | #[test] 874 | fn cell_type_test() { 875 | let f = VtkParser::::cell_type("2".as_bytes()); 876 | assert_eq!(f, Ok(("".as_bytes(), CellType::PolyVertex))); 877 | let f = VtkParser::::cell_type("10".as_bytes()); 878 | assert_eq!(f, Ok(("".as_bytes(), CellType::Tetra))); 879 | } 880 | 881 | macro_rules! test { 882 | ($fn:ident ($in:expr, $($args:expr),*) => ($rem:expr, $out:expr)) => { 883 | assert_eq!(VtkParser::::$fn($in.as_bytes(), $($args),*), Ok(($rem.as_bytes(), $out.clone()))); 884 | }; 885 | ($fn:ident ($in:expr) => ($rem:expr, $out:expr)) => { 886 | assert_eq!(VtkParser::::$fn($in.as_bytes()), Ok(($rem.as_bytes(), $out.clone()))); 887 | }; 888 | ($fn:ident ($in:expr, $($args:expr),*) => $out:expr) => { 889 | test!($fn($in, $($args),*) => ("", $out)); 890 | }; 891 | ($fn:ident ($in:expr) => $out:expr) => { 892 | test!($fn($in) => ("", $out)); 893 | } 894 | } 895 | 896 | #[test] 897 | fn cell_types_test() { 898 | let in1 = "CELL_TYPES 0\nother"; 899 | let out1 = Vec::::new(); 900 | let in2 = "CELL_TYPES 3\n2 1 10\nother"; 901 | let out2 = vec![CellType::PolyVertex, CellType::Vertex, CellType::Tetra]; 902 | test!(cell_types(in1, FileType::Ascii) => ("other", out1)); 903 | test!(cell_types(in2, FileType::Ascii) => ("other", out2)); 904 | } 905 | 906 | #[test] 907 | fn unstructured_grid_test() { 908 | let in1 = "UNSTRUCTURED_GRID\nPOINTS 4 float\n\ 909 | 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 910 | CELL_TYPES 2\n 10 10\nother"; 911 | let out1 = DataSet::inline(UnstructuredGridPiece { 912 | points: vec![2.0f32, 45., 2., 3., 4., 1., 46., 2., 0., 4., 32., 1.].into(), 913 | cells: Cells { 914 | cell_verts: VertexNumbers::Legacy { 915 | num_cells: 2, 916 | vertices: vec![4, 0, 1, 2, 3, 4, 3, 2, 1, 0], 917 | }, 918 | types: vec![CellType::Tetra; 2], 919 | }, 920 | data: Attributes::new(), 921 | }); 922 | 923 | test!(unstructured_grid(in1, FileType::Ascii) => ("other", out1)); 924 | } 925 | #[test] 926 | fn attribute_test() { 927 | // scalar attribute 928 | let in1 = "SCALARS cell_scalars int 1\n0 1 2 3 4 5"; 929 | let out1 = Attribute::DataArray(DataArray { 930 | name: String::from("cell_scalars"), 931 | elem: ElementType::Scalars { 932 | num_comp: 1, 933 | lookup_table: None, 934 | }, 935 | data: vec![0, 1, 2, 3, 4, 5].into(), 936 | }); 937 | test!(attribute(in1, 6, FileType::Ascii) => ("", out1)); 938 | } 939 | #[test] 940 | fn attributes_test() { 941 | // empty cell attributes 942 | test!(point_or_cell_attributes("\n", "CELL_DATA", FileType::Ascii) => Vec::new()); 943 | // empty point attributes 944 | test!(point_or_cell_attributes("", "POINT_DATA", FileType::Ascii) => Vec::new()); 945 | // empty 946 | test!(attributes("\n", FileType::Ascii) => Attributes::new()); 947 | // scalar cell attribute 948 | let in1 = "CELL_DATA 6\nSCALARS cell_scalars int 1\n0 1 2 3 4 5\n"; 949 | let scalar_data = DataArray { 950 | name: String::new(), 951 | elem: ElementType::Scalars { 952 | num_comp: 1, 953 | lookup_table: None, 954 | }, 955 | data: vec![0, 1, 2, 3, 4, 5].into(), 956 | }; 957 | let out1 = vec![Attribute::DataArray(DataArray { 958 | name: String::from("cell_scalars"), 959 | ..scalar_data.clone() 960 | })]; 961 | test!(point_or_cell_attributes(in1, "CELL_DATA", FileType::Ascii) => out1); 962 | // scalar point and cell attributes 963 | let in2 = "POINT_DATA 6\n SCALARS point_scalars int 1\n0 1 2 3 4 5\n 964 | CELL_DATA 6\n SCALARS cell_scalars int 1\n0 1 2 3 4 5"; 965 | let pt_res = vec![Attribute::DataArray(DataArray { 966 | name: String::from("point_scalars"), 967 | ..scalar_data.clone() 968 | })]; 969 | let cl_res = vec![Attribute::DataArray(DataArray { 970 | name: String::from("cell_scalars"), 971 | ..scalar_data 972 | })]; 973 | let out2 = Attributes { 974 | point: pt_res, 975 | cell: cl_res, 976 | }; 977 | test!(attributes(in2, FileType::Ascii) => out2); 978 | } 979 | #[test] 980 | fn dataset_simple_test() { 981 | let in1 = "DATASET UNSTRUCTURED_GRID\nPOINTS 0 float\nCELLS 0 0\nCELL_TYPES 0\n"; 982 | let out1 = DataSet::inline(UnstructuredGridPiece { 983 | points: Vec::::new().into(), 984 | cells: Cells { 985 | cell_verts: VertexNumbers::Legacy { 986 | num_cells: 0, 987 | vertices: vec![], 988 | }, 989 | types: vec![], 990 | }, 991 | data: Attributes::new(), 992 | }); 993 | test!(dataset(in1, FileType::Ascii) => out1); 994 | } 995 | #[test] 996 | fn dataset_test() { 997 | let in1 = "DATASET UNSTRUCTURED_GRID\nPOINTS 3 float\n2 45 2 3 4 1 46 2 0\ 998 | CELLS 0 0\nCELL_TYPES 0\n"; 999 | let out1 = DataSet::inline(UnstructuredGridPiece { 1000 | points: vec![2.0f32, 45., 2., 3., 4., 1., 46., 2., 0.].into(), 1001 | cells: Cells { 1002 | cell_verts: VertexNumbers::Legacy { 1003 | num_cells: 0, 1004 | vertices: vec![], 1005 | }, 1006 | types: vec![], 1007 | }, 1008 | data: Attributes::new(), 1009 | }); 1010 | test!(dataset(in1, FileType::Ascii) => out1); 1011 | } 1012 | #[test] 1013 | fn dataset_crlf_test() { 1014 | let in1 = "DATASET UNSTRUCTURED_GRID\r\nPOINTS 3 float\r\n2 45 2 3 4 1 46 2 0\ 1015 | CELLS 0 0\r\nCELL_TYPES 0\r\n"; 1016 | let out1 = DataSet::inline(UnstructuredGridPiece { 1017 | points: vec![2.0f32, 45., 2., 3., 4., 1., 46., 2., 0.].into(), 1018 | cells: Cells { 1019 | cell_verts: VertexNumbers::Legacy { 1020 | num_cells: 0, 1021 | vertices: vec![], 1022 | }, 1023 | types: vec![], 1024 | }, 1025 | data: Attributes::new(), 1026 | }); 1027 | test!(dataset(in1, FileType::Ascii) => out1); 1028 | } 1029 | } 1030 | -------------------------------------------------------------------------------- /src/xml/se.rs: -------------------------------------------------------------------------------- 1 | //! Module to handle custom serde `Serializer` 2 | //! 3 | //! This module serves as a patch for the quick_xml serde support. 4 | 5 | use quick_xml::DeError; 6 | use serde::ser::Serialize; 7 | 8 | #[cfg(not(feature = "binary"))] 9 | struct ByteWriter(W) 10 | where 11 | W: std::io::Write; 12 | 13 | #[cfg(not(feature = "binary"))] 14 | impl std::fmt::Write for ByteWriter { 15 | fn write_str(&mut self, s: &str) -> std::fmt::Result { 16 | self.0.write(s.as_bytes()).map_err(|_| std::fmt::Error)?; 17 | std::fmt::Result::Ok(()) 18 | } 19 | } 20 | 21 | #[cfg(not(feature = "binary"))] 22 | impl std::io::Write for ByteWriter { 23 | fn write(&mut self, buf: &[u8]) -> std::io::Result { 24 | self.0.write(buf) 25 | } 26 | 27 | fn flush(&mut self) -> std::io::Result<()> { 28 | self.0.flush() 29 | } 30 | } 31 | 32 | /// Serialize struct into an `io::Write`r 33 | #[cfg(feature = "binary")] 34 | pub fn to_writer(writer: W, value: &S) -> Result<(), DeError> { 35 | let mut buf_writer = std::io::BufWriter::new(writer); 36 | let serializer = quick_xml::se::io::Serializer::new(&mut buf_writer); 37 | value.serialize(serializer) 38 | } 39 | 40 | #[cfg(not(feature = "binary"))] 41 | pub fn to_writer(writer: W, value: &S) -> Result<(), DeError> { 42 | let mut buf_writer = ByteWriter(std::io::BufWriter::new(writer)); 43 | let serializer = quick_xml::se::Serializer::new(&mut buf_writer); 44 | value.serialize(serializer) 45 | } 46 | 47 | /// Serialize struct into a `fmt::Write`r 48 | pub fn to_fmt_writer( 49 | mut writer: W, 50 | value: &S, 51 | ) -> Result<(), DeError> { 52 | let serializer = quick_xml::se::Serializer::new(&mut writer); 53 | value.serialize(serializer) 54 | } 55 | 56 | /// Serialize struct into a `String` 57 | pub fn to_string(value: &S) -> Result { 58 | let mut s = String::new(); 59 | let serializer = quick_xml::se::Serializer::new(&mut s); 60 | value.serialize(serializer)?; 61 | Ok(s) 62 | } 63 | 64 | /// Serialize struct into a `Vec` 65 | pub fn to_bytes(value: &S) -> Result, DeError> { 66 | let mut bytes = Vec::new(); 67 | to_writer(&mut bytes, value)?; 68 | Ok(bytes) 69 | } 70 | -------------------------------------------------------------------------------- /tests/pygmsh.rs: -------------------------------------------------------------------------------- 1 | //! This test module tests against simple files generated by the pygmsh package. 2 | 3 | use nom::IResult; 4 | use vtkio::model::*; 5 | use vtkio::parser::*; 6 | use vtkio::writer::*; 7 | use vtkio::Error; 8 | 9 | macro_rules! test_b { 10 | ($fn:ident ($in:expr, $($args:expr),*) => $out:expr) => { 11 | assert_eq!($fn($in, $($args),*), IResult::Ok(("".as_bytes(), $out.clone()))); 12 | }; 13 | ($fn:ident ($in:expr) => $out:expr) => { 14 | assert_eq!($fn($in), IResult::Ok(("".as_bytes(), $out.clone()))); 15 | }; 16 | } 17 | 18 | macro_rules! test_ignore_rem { 19 | ($fn:ident ($in:expr, $($args:expr),*) => $out:expr) => { 20 | { 21 | let result = $fn($in, $($args),*); 22 | assert!(result.is_ok()); 23 | assert_eq!(result.unwrap().1, $out.clone()); 24 | } 25 | }; 26 | ($fn:ident ($in:expr) => $out:expr) => { 27 | { 28 | let result = $fn($in); 29 | assert!(result.is_ok()); 30 | assert_eq!(result.unwrap().1, $out.clone()); 31 | } 32 | }; 33 | } 34 | 35 | type Result = std::result::Result<(), Error>; 36 | 37 | // Helper functions to convert between endianness. 38 | 39 | fn ne(vtk: &Vtk) -> Vtk { 40 | Vtk { 41 | byte_order: ByteOrder::native(), 42 | ..vtk.clone() 43 | } 44 | } 45 | 46 | fn le(vtk: &Vtk) -> Vtk { 47 | Vtk { 48 | byte_order: ByteOrder::LittleEndian, 49 | ..vtk.clone() 50 | } 51 | } 52 | 53 | fn make_test_file(leading_zero_offset: bool) -> Vtk { 54 | Vtk { 55 | version: Version::new_legacy(5, 1), 56 | byte_order: ByteOrder::BigEndian, 57 | title: String::from("written by meshio v5.3.0"), 58 | file_path: None, 59 | data: DataSet::inline(UnstructuredGridPiece { 60 | points: vec![ 61 | 0.0f64, 62 | 0.0, 63 | 0.0, 64 | 1.0, 65 | -0.2, 66 | 0.0, 67 | 1.1, 68 | 1.2, 69 | 0.0, 70 | 0.1, 71 | 0.7, 72 | 0.0, 73 | 0.3333333333325021, 74 | -0.06666666666650042, 75 | 0.0, 76 | 0.6666666666657866, 77 | -0.1333333333331573, 78 | 0.0, 79 | 1.0249999999999424, 80 | 0.14999999999919245, 81 | 0.0, 82 | 1.0499999999998704, 83 | 0.4999999999981836, 84 | 0.0, 85 | 1.074999999999934, 86 | 0.8499999999990746, 87 | 0.0, 88 | 0.766666666667985, 89 | 1.0333333333339925, 90 | 0.0, 91 | 0.433333333334733, 92 | 0.8666666666673664, 93 | 0.0, 94 | 0.050000000000122564, 95 | 0.3500000000008579, 96 | 0.0, 97 | 0.7444729167676052, 98 | 0.3524793413776178, 99 | 0.0, 100 | 0.3781088913238718, 101 | 0.4816987298113132, 102 | 0.0, 103 | 0.7412636346823331, 104 | 0.6806963451979247, 105 | 0.0, 106 | 0.5070791452210437, 107 | 0.16277273408010906, 108 | 0.0, 109 | 0.253704273975508, 110 | 0.18556095944515594, 111 | 0.0, 112 | 0.7797139636550688, 113 | 0.08823831456107314, 114 | 0.0, 115 | ] 116 | .into(), 117 | cells: Cells { 118 | cell_verts: VertexNumbers::XML { 119 | offsets: { 120 | let mut offsets = vec![ 121 | 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 27, 30, 33, 36, 39, 42, 45, 122 | 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 91, 92, 93, 123 | 94, 124 | ]; 125 | if leading_zero_offset { 126 | offsets.insert(0, 0); 127 | } 128 | offsets 129 | }, 130 | connectivity: vec![ 131 | 0, 4, 4, 5, 5, 1, 1, 6, 6, 7, 7, 8, 8, 2, 2, 9, 9, 10, 10, 3, 3, 11, 11, 0, 132 | 10, 13, 14, 13, 12, 14, 10, 3, 13, 8, 2, 9, 4, 5, 15, 9, 10, 14, 3, 11, 13, 133 | 6, 7, 12, 8, 9, 14, 7, 8, 14, 12, 7, 14, 15, 5, 17, 5, 1, 17, 1, 6, 17, 12, 134 | 13, 15, 13, 11, 16, 15, 13, 16, 11, 0, 16, 0, 4, 16, 6, 12, 17, 12, 15, 17, 135 | 4, 15, 16, 0, 1, 2, 3, 136 | ], 137 | }, 138 | types: vec![ 139 | vec![CellType::Line; 12], 140 | vec![CellType::Triangle; 22], 141 | vec![CellType::Vertex; 4], 142 | ] 143 | .into_iter() 144 | .flatten() 145 | .collect::>(), 146 | }, 147 | data: Attributes { 148 | point: vec![], 149 | cell: vec![], 150 | }, 151 | }), 152 | } 153 | } 154 | 155 | #[test] 156 | fn legacy_ascii() -> Result { 157 | let input = include_str!("../assets/pygmsh/ascii.vtk").as_bytes(); 158 | let out1 = make_test_file(true); 159 | assert!(parse_be(input).is_ok()); 160 | test_ignore_rem!(parse_be(input) => out1); 161 | let mut outtest = String::new(); 162 | outtest.write_vtk_ne(out1.clone())?; 163 | // println!("{}", outtest); 164 | test_b!(parse_ne(String::new().write_vtk_ne(out1.clone())?.as_bytes()) => ne(&out1)); 165 | test_b!(parse_ne(Vec::::new().write_vtk_ne(out1.clone())?) => ne(&out1)); 166 | test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => le(&out1)); 167 | test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); 168 | Ok(()) 169 | } 170 | 171 | #[test] 172 | fn legacy_binary() -> Result { 173 | let input = include_bytes!("../assets/pygmsh/binary.vtk"); 174 | let out1 = make_test_file(true); 175 | assert!(parse_be(input).is_ok()); 176 | test_ignore_rem!(parse_be(input) => out1); 177 | let mut outtest = String::new(); 178 | outtest.write_vtk_ne(out1.clone())?; 179 | // println!("{}", outtest); 180 | test_b!(parse_ne(String::new().write_vtk_ne(out1.clone())?.as_bytes()) => ne(&out1)); 181 | test_b!(parse_ne(Vec::::new().write_vtk_ne(out1.clone())?) => ne(&out1)); 182 | test_b!(parse_le(Vec::::new().write_vtk_le(out1.clone())?) => le(&out1)); 183 | test_b!(parse_be(Vec::::new().write_vtk_be(out1.clone())?) => out1); 184 | Ok(()) 185 | } 186 | 187 | /// Ensures that points from the two given vtk files are equivalent up to floating point error, and then overwrites 188 | /// the first input to match exactly to the points in the second input, so the can be compared using `PartialEq` later. 189 | #[cfg(feature = "xml")] 190 | fn compare_points_in_float_and_overwrite(vtu: &mut Vtk, expected: &Vtk) { 191 | let expected_points = if let DataSet::UnstructuredGrid { ref pieces, .. } = expected.data { 192 | pieces[0] 193 | .load_piece_data(None) 194 | .unwrap() 195 | .points 196 | .cast_into::() 197 | .unwrap() 198 | } else { 199 | panic!("Wring vtk data type"); 200 | }; 201 | 202 | // Compare positions via floating point comparisons. 203 | if let DataSet::UnstructuredGrid { pieces, .. } = &mut vtu.data { 204 | let piece = &mut pieces[0]; 205 | if let Piece::Inline(piece_data) = piece { 206 | let mut points = piece_data 207 | .points 208 | .cast_into::() 209 | .expect("Point have the wrong type."); 210 | for (i, (point, &expected_point)) in 211 | points.iter_mut().zip(expected_points.iter()).enumerate() 212 | { 213 | if (*point - expected_point).abs() > 1e-6 * expected_point.abs() { 214 | eprintln!("{}: actual {} vs. expected {}", i, *point, expected_point); 215 | } 216 | assert!((*point - expected_point).abs() <= 1e-6 * expected_point.abs()); 217 | *point = expected_point; // match test data for full comparison later. 218 | } 219 | piece_data.points = points.into(); 220 | } else { 221 | panic!("Loaded vtk file has no inline unstructured grid piece"); 222 | } 223 | } else { 224 | panic!("Loaded vtk file is not an unstructured grid"); 225 | } 226 | } 227 | 228 | /// Ensures the given xml based vtk file has the right values and overwrites them to match 229 | /// the asset returned by make_test_file. 230 | #[cfg(feature = "xml")] 231 | fn assert_and_fix_xml_vtu(vtu: &mut Vtk) { 232 | vtu.file_path = None; // Reset file path to satisfy comparison 233 | assert_eq!(vtu.version, Version::new_xml(0, 1)); // XML file version is ignored. 234 | vtu.version = Version::new_legacy(5, 1); // Explicitly set version to satisfy comparison. 235 | assert_eq!(vtu.title, String::new()); // Default empty title 236 | vtu.title = "written by meshio v5.3.0".into(); // Match test file 237 | vtu.byte_order = ByteOrder::BigEndian; // Match test file 238 | } 239 | 240 | #[test] 241 | #[cfg(feature = "xml")] 242 | fn xml_ascii() -> Result { 243 | let mut vtu = Vtk::import("./assets/pygmsh/ascii.vtu")?; 244 | assert_and_fix_xml_vtu(&mut vtu); 245 | let expected = make_test_file(false); 246 | compare_points_in_float_and_overwrite(&mut vtu, &expected); 247 | assert_eq!(vtu, expected); 248 | Ok(()) 249 | } 250 | 251 | #[test] 252 | #[cfg(feature = "xml")] 253 | #[cfg(feature = "xz2")] 254 | fn xml_lzma() -> Result { 255 | let mut vtu = Vtk::import("./assets/pygmsh/lzma.vtu")?; 256 | assert_and_fix_xml_vtu(&mut vtu); 257 | let expected = make_test_file(false); 258 | compare_points_in_float_and_overwrite(&mut vtu, &expected); 259 | assert_eq!(vtu, expected); 260 | Ok(()) 261 | } 262 | 263 | #[test] 264 | #[cfg(feature = "xml")] 265 | fn xml_no_compression() -> Result { 266 | let mut vtu = Vtk::import("./assets/pygmsh/no-compression.vtu")?; 267 | assert_and_fix_xml_vtu(&mut vtu); 268 | let expected = make_test_file(false); 269 | compare_points_in_float_and_overwrite(&mut vtu, &expected); 270 | assert_eq!(vtu, expected); 271 | Ok(()) 272 | } 273 | 274 | #[test] 275 | #[cfg(feature = "xml")] 276 | #[cfg(feature = "flate2")] 277 | fn xml_zlib() -> Result { 278 | let mut vtu = Vtk::import("./assets/pygmsh/zlib.vtu")?; 279 | assert_and_fix_xml_vtu(&mut vtu); 280 | let expected = make_test_file(false); 281 | compare_points_in_float_and_overwrite(&mut vtu, &expected); 282 | assert_eq!(vtu, expected); 283 | Ok(()) 284 | } 285 | -------------------------------------------------------------------------------- /tests/xml.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "xml")] 2 | use pretty_assertions::assert_eq; 3 | use std::io::BufReader; 4 | use vtkio::{model::*, Error}; 5 | 6 | type Result = std::result::Result<(), Error>; 7 | 8 | fn init() { 9 | let _ = env_logger::builder().is_test(true).try_init(); 10 | } 11 | 12 | fn make_box_vtu() -> Vtk { 13 | Vtk { 14 | version: Version::new_xml(4, 2), 15 | title: String::new(), 16 | byte_order: ByteOrder::BigEndian, 17 | file_path: None, 18 | data: DataSet::inline(UnstructuredGridPiece { 19 | points: IOBuffer::F64(vec![ 20 | 0.5208333134651184, 21 | -0.5, 22 | 0.5, 23 | -0.5208333134651184, 24 | -0.5, 25 | 0.5, 26 | 0.5208333134651184, 27 | 0.5, 28 | 0.5, 29 | -0.5208333134651184, 30 | 0.5, 31 | 0.5, 32 | -0.5208333134651184, 33 | -0.5, 34 | -0.5, 35 | 0.5208333134651184, 36 | -0.5, 37 | -0.5, 38 | -0.5208333134651184, 39 | 0.5, 40 | -0.5, 41 | 0.5208333134651184, 42 | 0.5, 43 | -0.5, 44 | ]), 45 | cells: Cells { 46 | cell_verts: VertexNumbers::XML { 47 | connectivity: vec![ 48 | 0, 1, 3, 2, 4, 5, 7, 6, 6, 7, 2, 3, 5, 4, 1, 0, 5, 0, 2, 7, 1, 4, 6, 3, 49 | ], 50 | offsets: vec![4, 8, 12, 16, 20, 24], 51 | }, 52 | types: vec![CellType::Polygon; 6], 53 | }, 54 | data: Attributes { 55 | point: vec![ 56 | Attribute::DataArray(DataArrayBase { 57 | name: String::from("pressure"), 58 | elem: ElementType::Scalars { 59 | num_comp: 1, 60 | lookup_table: None, 61 | }, 62 | data: IOBuffer::F32(vec![-0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5]), 63 | }), 64 | Attribute::DataArray(DataArrayBase { 65 | name: String::from("Cd"), 66 | elem: ElementType::Vectors, 67 | data: IOBuffer::F32(vec![ 68 | 0.2, 0.0, 1.0, 0.2, 0.0, 1.0, 0.0, 1.0, 0.1, 0.0, 1.0, 0.1, 0.2, 0.0, 69 | 1.0, 0.2, 0.0, 1.0, 0.0, 1.0, 0.1, 0.0, 1.0, 0.1, 70 | ]), 71 | }), 72 | Attribute::DataArray(DataArrayBase { 73 | name: String::from("mtl_id"), 74 | elem: ElementType::Generic(1), 75 | data: IOBuffer::I32(vec![1, 1, 1, 1, 1, 1, 1, 1]), 76 | }), 77 | ], 78 | cell: vec![Attribute::DataArray(DataArrayBase { 79 | name: String::from("mtl_id"), 80 | elem: ElementType::Scalars { 81 | num_comp: 1, 82 | lookup_table: None, 83 | }, 84 | data: IOBuffer::I32(vec![0, 0, 0, 0, 0, 0]), 85 | })], 86 | }, 87 | }), 88 | } 89 | } 90 | 91 | #[test] 92 | fn box_parse_xml() -> Result { 93 | let input: &[u8] = include_bytes!("../assets/box.vtu"); 94 | let vtk = Vtk::parse_xml(BufReader::new(input))?; 95 | assert_eq!(vtk, make_box_vtu()); 96 | Ok(()) 97 | } 98 | 99 | #[test] 100 | fn box_import() -> Result { 101 | let mut vtk = Vtk::import("./assets/box.vtu")?; 102 | vtk.file_path = None; // erase file path before comparison. 103 | assert_eq!(vtk, make_box_vtu()); 104 | Ok(()) 105 | } 106 | 107 | fn make_box_para_vtu() -> Vtk { 108 | Vtk { 109 | version: Version::new_xml(1, 0), 110 | title: String::new(), 111 | byte_order: ByteOrder::LittleEndian, 112 | file_path: None, 113 | data: DataSet::inline(UnstructuredGridPiece { 114 | points: IOBuffer::F64(vec![ 115 | 0.5208333134651184, 116 | -0.5, 117 | 0.5, 118 | -0.5208333134651184, 119 | -0.5, 120 | 0.5, 121 | 0.5208333134651184, 122 | 0.5, 123 | 0.5, 124 | -0.5208333134651184, 125 | 0.5, 126 | 0.5, 127 | -0.5208333134651184, 128 | -0.5, 129 | -0.5, 130 | 0.5208333134651184, 131 | -0.5, 132 | -0.5, 133 | -0.5208333134651184, 134 | 0.5, 135 | -0.5, 136 | 0.5208333134651184, 137 | 0.5, 138 | -0.5, 139 | ]), 140 | cells: Cells { 141 | cell_verts: VertexNumbers::XML { 142 | connectivity: vec![ 143 | 0, 1, 3, 2, 4, 5, 7, 6, 6, 7, 2, 3, 5, 4, 1, 0, 5, 0, 2, 7, 1, 4, 6, 3, 144 | ], 145 | offsets: vec![4, 8, 12, 16, 20, 24], 146 | }, 147 | types: vec![CellType::Polygon; 6], 148 | }, 149 | data: Attributes { 150 | point: vec![ 151 | Attribute::scalars("mtl_id", 1) 152 | .with_data(IOBuffer::I32(vec![1, 1, 1, 1, 1, 1, 1, 1])), 153 | Attribute::DataArray(DataArrayBase { 154 | name: String::from("Cd"), 155 | elem: ElementType::Vectors, 156 | data: IOBuffer::F32(vec![ 157 | 0.2, 0.0, 1.0, 0.2, 0.0, 1.0, 0.0, 1.0, 0.1, 0.0, 1.0, 0.1, 0.2, 0.0, 158 | 1.0, 0.2, 0.0, 1.0, 0.0, 1.0, 0.1, 0.0, 1.0, 0.1, 159 | ]), 160 | }), 161 | Attribute::generic("pressure", 1).with_data(IOBuffer::F32(vec![ 162 | -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 163 | ])), 164 | ], 165 | cell: vec![Attribute::DataArray(DataArrayBase { 166 | name: String::from("mtl_id"), 167 | elem: ElementType::Scalars { 168 | num_comp: 1, 169 | lookup_table: None, 170 | }, 171 | data: IOBuffer::I32(vec![0, 0, 0, 0, 0, 0]), 172 | })], 173 | }, 174 | }), 175 | } 176 | } 177 | 178 | #[test] 179 | fn box_para_parse_xml() -> Result { 180 | let input: &[u8] = include_bytes!("../assets/box_para.vtu"); 181 | let vtk = Vtk::parse_xml(BufReader::new(input))?; 182 | assert_eq!(vtk, make_box_para_vtu()); 183 | Ok(()) 184 | } 185 | 186 | fn make_hexahedron_vtu() -> Vtk { 187 | Vtk { 188 | version: Version::new_xml(1, 0), 189 | title: String::new(), 190 | byte_order: ByteOrder::LittleEndian, 191 | file_path: None, 192 | data: DataSet::inline(UnstructuredGridPiece { 193 | #[rustfmt::skip] 194 | points: IOBuffer::F32(vec![ 195 | 0.0, 0.0, 0.0, 196 | 0.0, 0.0, -1.0, 197 | 0.0, 1.0, 0.0, 198 | 0.0, 1.0, -1.0, 199 | 1.0, 0.0, 0.0, 200 | 1.0, 0.0, -1.0, 201 | 1.0, 1.0, 0.0, 202 | 1.0, 1.0, -1.0 203 | ]), 204 | cells: Cells { 205 | cell_verts: VertexNumbers::XML { 206 | connectivity: vec![0, 4, 5, 1, 2, 6, 7, 3], 207 | offsets: vec![8], 208 | }, 209 | types: vec![CellType::Hexahedron; 1], 210 | }, 211 | data: Attributes { 212 | point: vec![], 213 | cell: vec![], 214 | }, 215 | }), 216 | } 217 | } 218 | 219 | #[test] 220 | fn hexahedron_appended() -> Result { 221 | let mut vtu = Vtk::import("./assets/hexahedron.vtu")?; 222 | vtu.file_path = None; // Reset file path to satisfy comparison 223 | assert_eq!(vtu, make_hexahedron_vtu()); 224 | Ok(()) 225 | } 226 | 227 | #[cfg(feature = "binary")] 228 | #[test] 229 | fn hexahedron_pvtu() -> Result { 230 | let mut vtu = Vtk::import("./assets/hexahedron_parallel.pvtu")?; 231 | vtu.load_all_pieces().unwrap(); 232 | vtu.file_path = None; // Reset file path to satisfy comparison 233 | assert_eq!(vtu, make_hexahedron_vtu()); 234 | Ok(()) 235 | } 236 | 237 | #[cfg(feature = "xz2")] 238 | #[test] 239 | fn hexahedron_lzma_pvtu() -> Result { 240 | let mut vtu = Vtk::import("./assets/hexahedron_parallel_lzma.pvtu")?; 241 | vtu.load_all_pieces().unwrap(); 242 | vtu.file_path = None; // Reset file path to satisfy comparison 243 | assert_eq!(vtu, make_hexahedron_vtu()); 244 | Ok(()) 245 | } 246 | 247 | #[cfg(feature = "flate2")] 248 | #[test] 249 | fn hexahedron_zlib() -> Result { 250 | let mut vtu = Vtk::import("./assets/hexahedron_zlib_para.vtu")?; 251 | vtu.clone() 252 | .try_into_xml_format(vtkio::xml::Compressor::ZLib, 1) 253 | .unwrap(); 254 | // vtkfile.export("./assets/hexahedron_zlib_out.vtu").unwrap(); 255 | vtu.file_path = None; // Reset file path to satisfy comparison 256 | assert_eq!(vtu, make_hexahedron_vtu()); 257 | Ok(()) 258 | } 259 | 260 | #[cfg(feature = "binary")] 261 | #[cfg(feature = "flate2")] 262 | #[test] 263 | fn hexahedron_zlib_binary() -> Result { 264 | let path = "./assets/hexahedron_zlib_binary.vtu"; 265 | let mut vtu = Vtk::import(path)?; 266 | vtu.file_path = None; // Reset file path to satisfy comparison 267 | assert_eq!(vtu, make_hexahedron_vtu()); 268 | // TODO: Implement a config for converting to XML files and add the following round trip test. 269 | // Write back into a vtkfile to check compression consistency. 270 | //let vtkfile = vtu 271 | // .clone() 272 | // .try_into_xml_format(vtkio::xml::Compressor::ZLib, 9) 273 | // .unwrap(); 274 | //let orig_vtkfile = vtkio::xml::VTKFile::import(path)?; 275 | //assert_eq!(vtkfile, orig_vtkfile); // Checks that compression is the same. 276 | Ok(()) 277 | } 278 | 279 | #[cfg(feature = "flate2")] 280 | #[test] 281 | fn hexahedron_zlib_inline_binary() -> Result { 282 | init(); 283 | let path = "./assets/hexahedron_zlib_inline_binary.vtu"; 284 | let mut vtu = Vtk::import(path)?; 285 | vtu.file_path = None; // Reset file path to satisfy comparison 286 | assert_eq!(vtu, make_hexahedron_vtu()); 287 | // Write back into a vtkfile to check compression consistency. 288 | let vtkfile = vtu 289 | .clone() 290 | .try_into_xml_format(vtkio::xml::Compressor::ZLib, 6) 291 | .unwrap(); 292 | // vtkfile.export("./assets/hexahedron_zlib_inline_binary_alt.vtu")?; 293 | let orig_vtkfile = vtkio::xml::VTKFile::import(path)?; 294 | assert_eq!(vtkfile, orig_vtkfile); // Checks that compression is the same. 295 | Ok(()) 296 | } 297 | 298 | #[cfg(feature = "lz4")] 299 | #[test] 300 | fn hexahedron_lz4() -> Result { 301 | init(); 302 | let path = "./assets/hexahedron_lz4_inline_binary.vtu"; 303 | let mut vtu = Vtk::import(path)?; 304 | vtu.file_path = None; // Reset file path to satisfy comparison 305 | assert_eq!(vtu, make_hexahedron_vtu()); 306 | // Write back into a vtkfile to check compression consistency. 307 | let vtkfile = vtu 308 | .clone() 309 | .try_into_xml_format(vtkio::xml::Compressor::LZ4, 9) 310 | .unwrap(); 311 | // vtkfile.export("./assets/hexahedron_lz4_inline_binary_alt.vtu")?; 312 | let orig_vtkfile = vtkio::xml::VTKFile::import(path)?; 313 | assert_eq!(vtkfile, orig_vtkfile); // Checks that compression is the same. 314 | Ok(()) 315 | } 316 | 317 | #[cfg(feature = "xz2")] 318 | #[test] 319 | fn hexahedron_lzma_inline_binary() -> Result { 320 | init(); 321 | let path = "./assets/hexahedron_lzma_inline_binary.vtu"; 322 | let mut vtu = Vtk::import(path)?; 323 | vtu.file_path = None; // Reset file path to satisfy comparison 324 | assert_eq!(vtu, make_hexahedron_vtu()); 325 | // Write back into a vtkfile to check compression consistency. 326 | let vtkfile = vtu 327 | .clone() 328 | .try_into_xml_format(vtkio::xml::Compressor::LZMA, 9) 329 | .unwrap(); 330 | // vtkfile.export("./assets/hexahedron_lzma_inline_binary_alt.vtu")?; 331 | let orig_vtkfile = vtkio::xml::VTKFile::import(path)?; 332 | assert_eq!(vtkfile, orig_vtkfile); // Checks that compression is the same. 333 | Ok(()) 334 | } 335 | 336 | #[test] 337 | fn point_cloud() -> Result { 338 | init(); 339 | let vtk = Vtk { 340 | version: Version::new_xml(1, 0), 341 | byte_order: ByteOrder::BigEndian, 342 | title: String::from("PointCloud example"), 343 | file_path: None, 344 | data: DataSet::inline(PolyDataPiece { 345 | points: vec![0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0].into(), 346 | verts: Some(VertexNumbers::Legacy { 347 | num_cells: 3, 348 | vertices: vec![1, 0, 1, 1, 1, 2], 349 | }), 350 | data: Attributes::new(), 351 | ..Default::default() 352 | }), 353 | }; 354 | let path = "./assets/point_cloud.vtp"; 355 | let orig_vtkfile = vtkio::xml::VTKFile::import(path)?; 356 | assert_eq!( 357 | vtk.try_into_xml_format(vtkio::xml::Compressor::None, 0)?, 358 | orig_vtkfile 359 | ); // Checks that compression is the same. 360 | Ok(()) 361 | } 362 | 363 | #[cfg(feature = "binary")] 364 | #[test] 365 | fn hexahedron_binary() -> Result { 366 | let mut vtu = Vtk::import("./assets/hexahedron_binary.vtu")?; 367 | vtu.file_path = None; // Reset file path to satisfy comparison 368 | assert_eq!(vtu, make_hexahedron_vtu()); 369 | Ok(()) 370 | } 371 | 372 | #[cfg(feature = "binary")] 373 | fn make_tet_vtu() -> Vtk { 374 | Vtk { 375 | version: Version::new_xml(1, 0), 376 | title: String::new(), 377 | byte_order: ByteOrder::LittleEndian, 378 | file_path: None, 379 | data: DataSet::inline(UnstructuredGridPiece { 380 | #[rustfmt::skip] 381 | points: IOBuffer::F64(vec![ 382 | 0.0, 1.0, 0.0, 383 | -0.9428102970123291, -0.3333297073841095, 0.0, 384 | 0.47140514850616455, -0.3333297073841095, 0.8164976239204407, 385 | 0.47140514850616455, -0.3333297073841095, -0.8164976239204407, 386 | ]), 387 | cells: Cells { 388 | cell_verts: VertexNumbers::XML { 389 | connectivity: vec![3, 1, 0, 2], 390 | offsets: vec![4], 391 | }, 392 | types: vec![CellType::Tetra; 1], 393 | }, 394 | data: Attributes { 395 | point: vec![Attribute::DataArray(DataArrayBase { 396 | name: String::from("pressure"), 397 | elem: ElementType::Scalars { 398 | num_comp: 1, 399 | lookup_table: None, 400 | }, 401 | data: IOBuffer::F32(vec![0.0, -0.9428103, 0.47140515, 0.47140515]), 402 | })], 403 | cell: vec![Attribute::DataArray(DataArrayBase { 404 | name: String::from("mtl_id"), 405 | elem: ElementType::Scalars { 406 | num_comp: 1, 407 | lookup_table: None, 408 | }, 409 | data: IOBuffer::I32(vec![1]), 410 | })], 411 | }, 412 | }), 413 | } 414 | } 415 | 416 | #[cfg(feature = "binary")] 417 | #[test] 418 | fn single_tet_vtu() -> Result { 419 | let mut vtu = Vtk::import("./assets/tet.vtu")?; 420 | vtu.file_path = None; // Reset file path to satisfy comparison 421 | assert_eq!(vtu, make_tet_vtu()); 422 | Ok(()) 423 | } 424 | --------------------------------------------------------------------------------