vistools 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. vistools-0.1.0/LICENSE +21 -0
  2. vistools-0.1.0/PKG-INFO +19 -0
  3. vistools-0.1.0/README.md +19 -0
  4. vistools-0.1.0/pyproject.toml +35 -0
  5. vistools-0.1.0/setup.cfg +4 -0
  6. vistools-0.1.0/src/vistools/__init__.py +22 -0
  7. vistools-0.1.0/src/vistools/pyvista/__init__.py +24 -0
  8. vistools-0.1.0/src/vistools/pyvista/camera.py +48 -0
  9. vistools-0.1.0/src/vistools/pyvista/polyline_cross_section.py +86 -0
  10. vistools-0.1.0/src/vistools/pyvista/scalar_bar_to_tikz.py +241 -0
  11. vistools-0.1.0/src/vistools/pyvista/sort_grid.py +172 -0
  12. vistools-0.1.0/src/vistools/pyvista/temporal_interpolator.py +78 -0
  13. vistools-0.1.0/src/vistools/vtk/__init__.py +26 -0
  14. vistools-0.1.0/src/vistools/vtk/compare_grids.py +181 -0
  15. vistools-0.1.0/src/vistools/vtk/geometric_search.py +63 -0
  16. vistools-0.1.0/src/vistools/vtk/merge_polylines.py +503 -0
  17. vistools-0.1.0/src/vistools/vtk/polyline_cross_section.py +189 -0
  18. vistools-0.1.0/src/vistools/vtk/vtk_data_structures_utils.py +29 -0
  19. vistools-0.1.0/src/vistools.egg-info/PKG-INFO +19 -0
  20. vistools-0.1.0/src/vistools.egg-info/SOURCES.txt +28 -0
  21. vistools-0.1.0/src/vistools.egg-info/dependency_links.txt +1 -0
  22. vistools-0.1.0/src/vistools.egg-info/requires.txt +12 -0
  23. vistools-0.1.0/src/vistools.egg-info/top_level.txt +1 -0
  24. vistools-0.1.0/tests/test_pyvista_polyline_cross_section.py +71 -0
  25. vistools-0.1.0/tests/test_pyvista_scalar_bar_to_tikz.py +69 -0
  26. vistools-0.1.0/tests/test_pyvista_sort_grid.py +151 -0
  27. vistools-0.1.0/tests/test_pyvista_temporal_interpolator.py +51 -0
  28. vistools-0.1.0/tests/test_vtk_merge_polylines.py +89 -0
  29. vistools-0.1.0/tests/test_vtk_merge_polylines_create_reference_files.py +199 -0
  30. vistools-0.1.0/tests/test_vtk_polyline_cross_section.py +57 -0
vistools-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023-2025 Ivo Steinbrecher
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
13
+ all 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
21
+ THE SOFTWARE.
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: vistools
3
+ Version: 0.1.0
4
+ Summary: Utility functionality for vtk and pyvista
5
+ Author-email: Ivo Steinbrecher <ivo.steinbrecher@unibw.de>
6
+ Project-URL: Repository, https://github.com/isteinbrecher/vtktools/
7
+ Project-URL: Issues, https://github.com/isteinbrecher/vtktools/issues/
8
+ License-File: LICENSE
9
+ Requires-Dist: numpy
10
+ Requires-Dist: scipy
11
+ Requires-Dist: vtk
12
+ Provides-Extra: pyvista
13
+ Requires-Dist: pyvista<0.45.0; extra == "pyvista"
14
+ Requires-Dist: opencv-python; extra == "pyvista"
15
+ Provides-Extra: dev
16
+ Requires-Dist: pre-commit; extra == "dev"
17
+ Requires-Dist: pytest; extra == "dev"
18
+ Requires-Dist: pytest-cov; extra == "dev"
19
+ Dynamic: license-file
@@ -0,0 +1,19 @@
1
+ # VisTools
2
+
3
+ **VisTools** is a utility library for scientific visualization workflows, focused on the pure VTK data format as well as PyVista.
4
+
5
+ ## Modules
6
+
7
+ - `vistools.vtk`: Utility functions based only on the plain `vtk` python package.
8
+
9
+ - `vistools.pyvista`: Utility functions for PvVista, depends on `vistools.vtk`.
10
+
11
+ ## Installation
12
+
13
+ VisTools is available via `pip` and can be installed with
14
+ ```bash
15
+ # Install only vistools.vtk
16
+ pip install vistools
17
+ # Install all modules
18
+ pip install "vistools[pyvista]"
19
+ ```
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vistools"
7
+ authors = [
8
+ {name = "Ivo Steinbrecher", email = "ivo.steinbrecher@unibw.de"},
9
+ ]
10
+ description = "Utility functionality for vtk and pyvista"
11
+ dependencies = [
12
+ "numpy",
13
+ "scipy",
14
+ "vtk"
15
+ ]
16
+ version = "0.1.0"
17
+
18
+ [project.urls]
19
+ Repository = "https://github.com/isteinbrecher/vtktools/"
20
+ Issues = "https://github.com/isteinbrecher/vtktools/issues/"
21
+
22
+ [project.optional-dependencies]
23
+ pyvista = [
24
+ "pyvista<0.45.0",
25
+ "opencv-python"
26
+ ]
27
+ dev = [
28
+ "pre-commit",
29
+ "pytest",
30
+ "pytest-cov"
31
+ ]
32
+
33
+ [tool.pytest.ini_options]
34
+ testpaths = ["tests"]
35
+ addopts = "-p pytest_cov --cov-report=term --cov-report=html --cov=src/"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,22 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2023-2025 Ivo Steinbrecher
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
13
+ # all 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
21
+ # THE SOFTWARE.
22
+ """Define the main namespace of vistools."""
@@ -0,0 +1,24 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2023-2025 Ivo Steinbrecher
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
13
+ # all 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
21
+ # THE SOFTWARE.
22
+ """Define the main namespace of pvtools."""
23
+
24
+ from vistools.pyvista.sort_grid import sort_grid
@@ -0,0 +1,48 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2023-2025 Ivo Steinbrecher
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
13
+ # all 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
21
+ # THE SOFTWARE.
22
+ """Camera functionality for pyvista."""
23
+
24
+
25
+ def get_camera_settings(camera):
26
+ names = [
27
+ "clipping_range",
28
+ "distance",
29
+ "elevation",
30
+ "focal_point",
31
+ "model_transform_matrix",
32
+ "parallel_projection",
33
+ "parallel_scale",
34
+ "position",
35
+ "roll",
36
+ "thickness",
37
+ "up",
38
+ "view_angle",
39
+ ]
40
+ camera_settings = {}
41
+ for name in names:
42
+ camera_settings[name] = getattr(camera, name)
43
+ return camera_settings
44
+
45
+
46
+ def set_camera_settings(camera, settings):
47
+ for name, value in settings.items():
48
+ setattr(camera, name, value)
@@ -0,0 +1,86 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2023-2025 Ivo Steinbrecher
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
13
+ # all 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
21
+ # THE SOFTWARE.
22
+ """Extrude a profile along a polyline."""
23
+
24
+ import pyvista as pv
25
+
26
+ from vistools.vtk.polyline_cross_section import (
27
+ polyline_cross_section as vtk_polyline_cross_section,
28
+ )
29
+
30
+
31
+ def polyline_cross_section(
32
+ grid: pv.PolyData,
33
+ cross_section_points,
34
+ *,
35
+ closed: bool = True,
36
+ separate_surfaces: bool = False,
37
+ ) -> pv.UnstructuredGrid:
38
+ """Extrude a profile defined by the cross section coordinates along a
39
+ polyline.
40
+
41
+ This function calls the vtk version under the hood, but adds some additional
42
+ functionality.
43
+
44
+ Args
45
+ ----
46
+ grid:
47
+ Polyline defining the centerline of the extruded structure
48
+ cross_section_points:
49
+ In-cross-section coordinates defining the profile
50
+ closed:
51
+ Flag if the profile is open or closed
52
+ separate_surfaces:
53
+ Per default all surface cells for a single polyline extrusion are connected.
54
+ If this is true, then the connectivity of the returned surfaces is modified.
55
+ The polygons at the start and end (for closed==True) will not be connected to
56
+ the rest. For the extruded surfaces, each segment between two points in
57
+ cross_section_points will be extruded along the centerline and not be connected
58
+ to the bounding surfaces. This can be useful if sharp edges should be shown in
59
+ the visualization.
60
+ """
61
+
62
+ cross_section_grid = pv.UnstructuredGrid(
63
+ vtk_polyline_cross_section(grid, cross_section_points, closed=closed)
64
+ )
65
+
66
+ if not separate_surfaces:
67
+ return cross_section_grid
68
+ else:
69
+ surface_ids = cross_section_grid.celltypes != pv.CellType.POLYGON
70
+ grid_surfaces = cross_section_grid.extract_cells(surface_ids)
71
+ if closed:
72
+ n_surfaces = len(cross_section_points)
73
+ else:
74
+ n_surfaces = len(cross_section_points) - 1
75
+ n_cells = grid_surfaces.n_cells
76
+ surfaces = [
77
+ grid_surfaces.extract_cells(range(i_start, n_cells, n_surfaces))
78
+ for i_start in range(n_surfaces)
79
+ ]
80
+
81
+ if closed:
82
+ bound_ids = cross_section_grid.celltypes == pv.CellType.POLYGON
83
+ grid_bound = cross_section_grid.extract_cells(bound_ids)
84
+ surfaces.append(grid_bound)
85
+
86
+ return pv.merge(surfaces, merge_points=False)
@@ -0,0 +1,241 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2023-2025 Ivo Steinbrecher
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
13
+ # all 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
21
+ # THE SOFTWARE.
22
+ """Create a screenshot and the TikZ code to create the scalar bar."""
23
+
24
+ import os
25
+ from pathlib import Path
26
+
27
+ import cv2
28
+ import pyvista as pv
29
+
30
+ # Inch to centimeter conversion
31
+ INCH = 2.54
32
+
33
+
34
+ def _dots_to_inch(pt, dpi):
35
+ """Convert a distance in pt to cm."""
36
+ return float(pt) / dpi * INCH
37
+
38
+
39
+ def _hide_scalar_bar(scalar_bar):
40
+ """Hide the scalar bar and return the data needed to reset it."""
41
+ data = {"title": scalar_bar.GetTitle()}
42
+ scalar_bar.SetTitle("")
43
+ scalar_bar.DrawTickLabelsOff()
44
+ return data
45
+
46
+
47
+ def _show_scalar_bar(scalar_bar, data):
48
+ """Show the scalar bar and reset the options."""
49
+ scalar_bar.SetTitle(data["title"])
50
+ scalar_bar.DrawTickLabelsOn()
51
+
52
+
53
+ def _get_scalar_bar_rectangles(scalar_bars, image_pixel_data, dpi):
54
+ """Get the position of the TikZ color bars.
55
+
56
+ We return the distance in cm where the scalar bar starts and the width
57
+ and the height:
58
+ [pos_x, pos_y, width, heighth]
59
+
60
+ For now we do this with image detection, but this could also be done by
61
+ directly interpreting the data from the scalar bar. There seems to be a
62
+ bug in pyvista such that the width of the scalar bar needs to be scaled
63
+ with 1/INCH.
64
+ """
65
+
66
+ shape = image_pixel_data.shape
67
+
68
+ # Convert to grayscale
69
+ gray = cv2.cvtColor(image_pixel_data, cv2.COLOR_RGB2GRAY)
70
+
71
+ # Apply edge detection
72
+ edges = cv2.Canny(gray, threshold1=50, threshold2=150)
73
+
74
+ # Find external contours
75
+ contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
76
+
77
+ rectangles = []
78
+
79
+ for contour in contours:
80
+ # Approximate contour to polygon
81
+ epsilon = 0.02 * cv2.arcLength(contour, True)
82
+ approx = cv2.approxPolyDP(contour, epsilon, True)
83
+
84
+ # Check for 4 corners and convexity
85
+ if len(approx) == 4 and cv2.isContourConvex(approx):
86
+ x, y, w, h = cv2.boundingRect(approx)
87
+ rectangle_pixel = [x, shape[0] - y - h + 1, w, h - 1]
88
+ rectangles.append([_dots_to_inch(val, dpi) for val in rectangle_pixel])
89
+
90
+ return rectangles
91
+
92
+
93
+ def _get_tikz_data(scalar_bar):
94
+ """Return the data required for TikZ for this scalar bar."""
95
+
96
+ data = {}
97
+
98
+ min_max = list(scalar_bar.GetLookupTable().GetRange())
99
+ data["min_max"] = min_max
100
+
101
+ ticks = []
102
+ ticks.extend(min_max)
103
+ ticks.sort()
104
+ data["ticks"] = ticks
105
+
106
+ data["title"] = scalar_bar.GetTitle()
107
+
108
+ data["orientation"] = "v" if scalar_bar.GetOrientation() else "h"
109
+
110
+ return data
111
+
112
+
113
+ def _get_tikz_string_continuous(rectangle, data, number_format):
114
+ """Get the TikZ code for a continuous color bar."""
115
+
116
+ # Add the code that is valid for all types of labels.
117
+ tikz_code = [
118
+ "\\begin{axis}[",
119
+ "scale only axis,",
120
+ "scaled x ticks=false,",
121
+ "scaled y ticks=false,",
122
+ f"at={{({rectangle[0]}cm,{rectangle[1]}cm)}},",
123
+ "tick label style={font=\\footnotesize},",
124
+ f"title={data['title']},",
125
+ f"xticklabel={number_format},",
126
+ f"yticklabel={number_format},",
127
+ f"xmin={data['min_max'][0]},",
128
+ f"xmax={data['min_max'][1]},",
129
+ f"ymin={data['min_max'][0]},",
130
+ f"ymax={data['min_max'][1]},",
131
+ f"width={rectangle[2]}cm,",
132
+ f"height={rectangle[3]}cm,",
133
+ ]
134
+
135
+ ticks_string = ",".join(map(str, data["ticks"]))
136
+
137
+ if data["orientation"] == "v":
138
+ tikz_code += [
139
+ "xtick=\\empty,",
140
+ f"ytick={{{ticks_string}}},",
141
+ "ytick pos=right,",
142
+ "ytick align=outside,",
143
+ ]
144
+ elif data["orientation"] == "h":
145
+ raise ValueError("For horizontal orientation the rectangles are wrong!")
146
+ tikz_code += [
147
+ "ytick=\\empty,",
148
+ f"xtick={{{ticks_string}}},",
149
+ "xtick pos=right,",
150
+ "xtick align=outside,",
151
+ "title style={{yshift=10pt,}},",
152
+ ]
153
+ else:
154
+ raise ValueError("Got unexpected value for orientation")
155
+
156
+ tikz_code += ["]", "\\end{axis}"]
157
+ return tikz_code
158
+
159
+
160
+ def export_to_tikz(
161
+ name_or_path: Path,
162
+ plotter: pv.Plotter,
163
+ *,
164
+ dpi: int = 300,
165
+ figure_path: str = "",
166
+ number_format: str = "{$\\pgfmathprintnumber[sci,precision=1,sci generic={mantissa sep=,exponent={\\mathrm{e}{##1}}}]{\\tick}$}",
167
+ is_testing: bool = True,
168
+ ):
169
+ """Export a screenshot and also export TikZ code that overlays a TikZ
170
+ scalar bar.
171
+
172
+ We export the image with the raw scalar bar, i.e., no labels, ticks, title, etc..
173
+ We also export TikZ code that can import this image and overlay a TikZ scalar bar
174
+ such that the numbering of the scalar bar can be done directly within LaTeX.
175
+
176
+ Args:
177
+ name_or_path: Name of the output files. Can include directory paths.
178
+ plotter: Pyvista plotter
179
+ dpi: Desired resolution of image in dots per inch.
180
+ figure_path: Relative path to figures in the TeX document structure. Default is empty.
181
+ number_format: Number format to be used for the TeX ticks.
182
+ """
183
+
184
+ # Name of image and TikZ file.
185
+ path_to_name = Path(name_or_path)
186
+ base_dir = path_to_name.parent
187
+ name = path_to_name.name
188
+ image_name = name + ".png"
189
+ image_path = base_dir / image_name
190
+ tikz_path = base_dir / (name + ".tex")
191
+
192
+ # Get all scalar bars in the plotter
193
+ scalar_bars = [plotter.scalar_bars[key] for key in list(plotter.scalar_bars.keys())]
194
+
195
+ # Hide the stuff we don't want in the image
196
+ original_data = [_hide_scalar_bar(scalar_bar) for scalar_bar in scalar_bars]
197
+
198
+ # Save the screenshot
199
+ if not is_testing:
200
+ image_pixel_data = plotter.screenshot(image_path)
201
+ else:
202
+ image_pixel_data = plotter.screenshot(None, return_img=True)
203
+
204
+ # Reset the scalar bar properties
205
+ for scalar_bar, data in zip(scalar_bars, original_data):
206
+ _show_scalar_bar(scalar_bar, data)
207
+
208
+ # Set the header of te TikZ code
209
+ tikz_code = [
210
+ "%% This file was created with vtktools",
211
+ "%% Use the following includes in the LaTeX header:",
212
+ "%\\usepackage{tikz}",
213
+ "%\\usepackage{pgfplots}",
214
+ "%% Optional:",
215
+ "%\\pgfplotsset{compat=1.16}",
216
+ "%\\normalsize % Sometimes in figure environments a smaller font is selected -> uncomment this to activate the default font size",
217
+ "\\begin{tikzpicture}",
218
+ "% The -0.2pt here are needed, so the image is really placed at the origin.",
219
+ f"\\node[anchor=south west,inner sep=-0.2pt] (image) at (0,0) {{\\includegraphics[scale={72.0 / dpi}]{{{os.path.join(figure_path, image_name)}}}}};",
220
+ ]
221
+
222
+ # Get the positions of the scalar bar
223
+ rectangles = _get_scalar_bar_rectangles(scalar_bars, image_pixel_data, dpi)
224
+
225
+ # Get the data we need for the TikZ plot from the scalar bar
226
+ tikz_data_list = [_get_tikz_data(scalar_bar) for scalar_bar in scalar_bars]
227
+
228
+ # For now this only works for 1 rectangle
229
+ if not len(rectangles) == 1 or not len(tikz_data_list) == 1:
230
+ raise ValueError("This function currently only works for 1 rectangle")
231
+
232
+ # Add the TikZ code for each color bar
233
+ for rectangle, data in zip(rectangles, tikz_data_list):
234
+ tikz_code.extend(_get_tikz_string_continuous(rectangle, data, number_format))
235
+
236
+ # Finalize the TikZ code
237
+ tikz_code.append("\\end{tikzpicture}%\n%}%")
238
+
239
+ # Write TikZ code to file.
240
+ with open(tikz_path, "w") as text_file:
241
+ text_file.write("\n".join(tikz_code))