parallel-matplotlib-animation 0.1.0__tar.gz → 0.1.2__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 (27) hide show
  1. parallel_matplotlib_animation-0.1.2/.claude/settings.local.json +12 -0
  2. parallel_matplotlib_animation-0.1.2/.gitignore +308 -0
  3. parallel_matplotlib_animation-0.1.0/README.md → parallel_matplotlib_animation-0.1.2/PKG-INFO +36 -5
  4. parallel_matplotlib_animation-0.1.0/PKG-INFO → parallel_matplotlib_animation-0.1.2/README.md +22 -27
  5. parallel_matplotlib_animation-0.1.2/assets/multi_panel_animation.gif +0 -0
  6. parallel_matplotlib_animation-0.1.2/assets/nondeterministic_video_loader.gif +0 -0
  7. parallel_matplotlib_animation-0.1.2/assets/scaling_graph.png +0 -0
  8. parallel_matplotlib_animation-0.1.2/assets/simple_wave_animation.gif +0 -0
  9. parallel_matplotlib_animation-0.1.2/assets/very_complex_animation.gif +0 -0
  10. parallel_matplotlib_animation-0.1.2/dev/mp4_to_gif.sh +31 -0
  11. {parallel_matplotlib_animation-0.1.0 → parallel_matplotlib_animation-0.1.2}/pyproject.toml +9 -6
  12. parallel_matplotlib_animation-0.1.2/src/parallel_animate/__init__.py +1 -0
  13. {parallel_matplotlib_animation-0.1.0 → parallel_matplotlib_animation-0.1.2}/src/parallel_animate/animator.py +150 -96
  14. parallel_matplotlib_animation-0.1.2/src/parallel_animate/examples/nondeterministic_video_loader.py +46 -0
  15. parallel_matplotlib_animation-0.1.2/src/parallel_animate/util.py +64 -0
  16. parallel_matplotlib_animation-0.1.2/tests/__init__.py +0 -0
  17. parallel_matplotlib_animation-0.1.2/tests/test_animator.py +521 -0
  18. parallel_matplotlib_animation-0.1.2/tests/test_examples.py +47 -0
  19. parallel_matplotlib_animation-0.1.2/tests/test_util.py +56 -0
  20. parallel_matplotlib_animation-0.1.2/uv.lock +785 -0
  21. parallel_matplotlib_animation-0.1.0/src/parallel_animate/__init__.py +0 -1
  22. {parallel_matplotlib_animation-0.1.0 → parallel_matplotlib_animation-0.1.2}/LICENSE +0 -0
  23. {parallel_matplotlib_animation-0.1.0 → parallel_matplotlib_animation-0.1.2}/src/parallel_animate/examples/__init__.py +0 -0
  24. {parallel_matplotlib_animation-0.1.0 → parallel_matplotlib_animation-0.1.2}/src/parallel_animate/examples/multi_panel_animation.py +0 -0
  25. {parallel_matplotlib_animation-0.1.0 → parallel_matplotlib_animation-0.1.2}/src/parallel_animate/examples/scaling_test.py +0 -0
  26. {parallel_matplotlib_animation-0.1.0 → parallel_matplotlib_animation-0.1.2}/src/parallel_animate/examples/simple_wave_animation.py +0 -0
  27. {parallel_matplotlib_animation-0.1.0 → parallel_matplotlib_animation-0.1.2}/src/parallel_animate/examples/very_complex_animation.py +0 -0
@@ -0,0 +1,12 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(python *)",
5
+ "Bash(python3.13 -c \"import numpy; print\\(numpy.__version__\\)\")",
6
+ "Bash(.venv/bin/python *)",
7
+ "Bash(gh pr *)",
8
+ "Bash(gh auth *)",
9
+ "WebFetch(domain:github.com)"
10
+ ]
11
+ }
12
+ }
@@ -0,0 +1,308 @@
1
+ example_output/
2
+
3
+ ######################################################################
4
+
5
+ # Created by https://www.toptal.com/developers/gitignore/api/python,linux,macos,windows,git,vim,visualstudiocode
6
+ # Edit at https://www.toptal.com/developers/gitignore?templates=python,linux,macos,windows,git,vim,visualstudiocode
7
+
8
+ ### Git ###
9
+ # Created by git for backups. To disable backups in Git:
10
+ # $ git config --global mergetool.keepBackup false
11
+ *.orig
12
+
13
+ # Created by git when using merge tools for conflicts
14
+ *.BACKUP.*
15
+ *.BASE.*
16
+ *.LOCAL.*
17
+ *.REMOTE.*
18
+ *_BACKUP_*.txt
19
+ *_BASE_*.txt
20
+ *_LOCAL_*.txt
21
+ *_REMOTE_*.txt
22
+
23
+ ### Linux ###
24
+ *~
25
+
26
+ # temporary files which can be created if a process still has a handle open of a deleted file
27
+ .fuse_hidden*
28
+
29
+ # KDE directory preferences
30
+ .directory
31
+
32
+ # Linux trash folder which might appear on any partition or disk
33
+ .Trash-*
34
+
35
+ # .nfs files are created when an open file is removed but is still being accessed
36
+ .nfs*
37
+
38
+ ### macOS ###
39
+ # General
40
+ .DS_Store
41
+ .AppleDouble
42
+ .LSOverride
43
+
44
+ # Icon must end with two \r
45
+ Icon
46
+
47
+
48
+ # Thumbnails
49
+ ._*
50
+
51
+ # Files that might appear in the root of a volume
52
+ .DocumentRevisions-V100
53
+ .fseventsd
54
+ .Spotlight-V100
55
+ .TemporaryItems
56
+ .Trashes
57
+ .VolumeIcon.icns
58
+ .com.apple.timemachine.donotpresent
59
+
60
+ # Directories potentially created on remote AFP share
61
+ .AppleDB
62
+ .AppleDesktop
63
+ Network Trash Folder
64
+ Temporary Items
65
+ .apdisk
66
+
67
+ ### macOS Patch ###
68
+ # iCloud generated files
69
+ *.icloud
70
+
71
+ ### Python ###
72
+ # Byte-compiled / optimized / DLL files
73
+ __pycache__/
74
+ *.py[cod]
75
+ *$py.class
76
+
77
+ # C extensions
78
+ *.so
79
+
80
+ # Distribution / packaging
81
+ .Python
82
+ build/
83
+ develop-eggs/
84
+ dist/
85
+ downloads/
86
+ eggs/
87
+ .eggs/
88
+ lib/
89
+ lib64/
90
+ parts/
91
+ sdist/
92
+ var/
93
+ wheels/
94
+ share/python-wheels/
95
+ *.egg-info/
96
+ .installed.cfg
97
+ *.egg
98
+ MANIFEST
99
+
100
+ # PyInstaller
101
+ # Usually these files are written by a python script from a template
102
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
103
+ *.manifest
104
+ *.spec
105
+
106
+ # Installer logs
107
+ pip-log.txt
108
+ pip-delete-this-directory.txt
109
+
110
+ # Unit test / coverage reports
111
+ htmlcov/
112
+ .tox/
113
+ .nox/
114
+ .coverage
115
+ .coverage.*
116
+ .cache
117
+ nosetests.xml
118
+ coverage.xml
119
+ *.cover
120
+ *.py,cover
121
+ .hypothesis/
122
+ .pytest_cache/
123
+ cover/
124
+
125
+ # Translations
126
+ *.mo
127
+ *.pot
128
+
129
+ # Django stuff:
130
+ *.log
131
+ local_settings.py
132
+ db.sqlite3
133
+ db.sqlite3-journal
134
+
135
+ # Flask stuff:
136
+ instance/
137
+ .webassets-cache
138
+
139
+ # Scrapy stuff:
140
+ .scrapy
141
+
142
+ # Sphinx documentation
143
+ docs/_build/
144
+
145
+ # PyBuilder
146
+ .pybuilder/
147
+ target/
148
+
149
+ # Jupyter Notebook
150
+ .ipynb_checkpoints
151
+
152
+ # IPython
153
+ profile_default/
154
+ ipython_config.py
155
+
156
+ # pyenv
157
+ # For a library or package, you might want to ignore these files since the code is
158
+ # intended to run in multiple environments; otherwise, check them in:
159
+ # .python-version
160
+
161
+ # pipenv
162
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
163
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
164
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
165
+ # install all needed dependencies.
166
+ #Pipfile.lock
167
+
168
+ # poetry
169
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
170
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
171
+ # commonly ignored for libraries.
172
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
173
+ #poetry.lock
174
+
175
+ # pdm
176
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
177
+ #pdm.lock
178
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
179
+ # in version control.
180
+ # https://pdm.fming.dev/#use-with-ide
181
+ .pdm.toml
182
+
183
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
184
+ __pypackages__/
185
+
186
+ # Celery stuff
187
+ celerybeat-schedule
188
+ celerybeat.pid
189
+
190
+ # SageMath parsed files
191
+ *.sage.py
192
+
193
+ # Environments
194
+ .env
195
+ .venv
196
+ env/
197
+ venv/
198
+ ENV/
199
+ env.bak/
200
+ venv.bak/
201
+
202
+ # Spyder project settings
203
+ .spyderproject
204
+ .spyproject
205
+
206
+ # Rope project settings
207
+ .ropeproject
208
+
209
+ # mkdocs documentation
210
+ /site
211
+
212
+ # mypy
213
+ .mypy_cache/
214
+ .dmypy.json
215
+ dmypy.json
216
+
217
+ # Pyre type checker
218
+ .pyre/
219
+
220
+ # pytype static type analyzer
221
+ .pytype/
222
+
223
+ # Cython debug symbols
224
+ cython_debug/
225
+
226
+ # PyCharm
227
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
228
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
229
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
230
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
231
+ #.idea/
232
+
233
+ ### Python Patch ###
234
+ # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
235
+ poetry.toml
236
+
237
+ # ruff
238
+ .ruff_cache/
239
+
240
+ # LSP config files
241
+ pyrightconfig.json
242
+
243
+ ### Vim ###
244
+ # Swap
245
+ [._]*.s[a-v][a-z]
246
+ !*.svg # comment out if you don't need vector files
247
+ [._]*.sw[a-p]
248
+ [._]s[a-rt-v][a-z]
249
+ [._]ss[a-gi-z]
250
+ [._]sw[a-p]
251
+
252
+ # Session
253
+ Session.vim
254
+ Sessionx.vim
255
+
256
+ # Temporary
257
+ .netrwhist
258
+ # Auto-generated tag files
259
+ tags
260
+ # Persistent undo
261
+ [._]*.un~
262
+
263
+ ### VisualStudioCode ###
264
+ .vscode/*
265
+ !.vscode/settings.json
266
+ !.vscode/tasks.json
267
+ !.vscode/launch.json
268
+ !.vscode/extensions.json
269
+ !.vscode/*.code-snippets
270
+
271
+ # Local History for Visual Studio Code
272
+ .history/
273
+
274
+ # Built Visual Studio Code Extensions
275
+ *.vsix
276
+
277
+ ### VisualStudioCode Patch ###
278
+ # Ignore all local history of files
279
+ .history
280
+ .ionide
281
+
282
+ ### Windows ###
283
+ # Windows thumbnail cache files
284
+ Thumbs.db
285
+ Thumbs.db:encryptable
286
+ ehthumbs.db
287
+ ehthumbs_vista.db
288
+
289
+ # Dump file
290
+ *.stackdump
291
+
292
+ # Folder config file
293
+ [Dd]esktop.ini
294
+
295
+ # Recycle Bin used on file shares
296
+ $RECYCLE.BIN/
297
+
298
+ # Windows Installer files
299
+ *.cab
300
+ *.msi
301
+ *.msix
302
+ *.msm
303
+ *.msp
304
+
305
+ # Windows shortcuts
306
+ *.lnk
307
+
308
+ # End of https://www.toptal.com/developers/gitignore/api/python,linux,macos,windows,git,vim,visualstudiocode
@@ -1,3 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: parallel-matplotlib-animation
3
+ Version: 0.1.2
4
+ Summary: Animate matplotlib figures into videos, in parallel but with efficient caching
5
+ Project-URL: Repository, https://github.com/sibocw/parallel-matplotlib-animation
6
+ Author-email: Sibo Wang-Chen <sibo.wang@epfl.ch>
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: av>=14.0
10
+ Requires-Dist: matplotlib>=3.10.0
11
+ Requires-Dist: numpy<3,>=2
12
+ Requires-Dist: pillow>=11.0
13
+ Requires-Dist: tqdm>=4.67
14
+ Description-Content-Type: text/markdown
15
+
1
16
  # parallel-matplotlib-animation
2
17
 
3
18
  Create matplotlib animations rendered to video in parallel, with efficient resources reuse.
@@ -63,7 +78,7 @@ anim = WaveAnimation()
63
78
  anim.make_video("wave.mp4", param_by_frame=params, fps=30, num_workers=4)
64
79
  ```
65
80
 
66
- ![](assets/simple_wave_animation.gif)
81
+ <img src="assets/simple_wave_animation.gif" width="480"/>
67
82
 
68
83
  ## Usage
69
84
  This library has a single class: `parallel_animate.Animator`. To make an animation, you must create your own class inheriting from it and define the following methods:
@@ -75,18 +90,34 @@ This library has a single class: `parallel_animate.Animator`. To make an animati
75
90
  Once you have defined your animator class, there is a single method that you need to call that makes the video: **`.make_video(...)`**. It accepts the following arguments:
76
91
 
77
92
  - `output_file` (Path or str): Output video path
78
- - `param_by_frame` (list): List of parameters. Each element in the list is the `params` argument to be given to the `.update` call for the corresponding frame.
93
+ - `param_by_frame` (Iterable): Iterable of parameters. Each element is the `params` argument to be given to the `.update` call for the corresponding frame. Can be a list, tuple, generator, or any other iterable. Using generators is particularly useful for large data (e.g., bitmaps) to avoid loading everything into memory at once.
79
94
  - `fps` (int): Frame rate of the output video
95
+ - `n_frames` (int or None): Number of frames to render. If None, use the length of `param_by_frame`. If param_by_frame does not have `__len__` implemented and `n_frames` is None, the progress bar won't show completion percentage.
80
96
  - `num_workers` (int): Number of worker processes to be spawned. If -1, use all CPU cores. If -2, use all but one CPU cores, etc. If 1, no child process is created and the video is made in the main process itself. Default is -1.
81
97
  - See the docstring for `parallel_animate.animator` directly for less commonly used, optional parameters. These control logging, rendering quality, etc.
82
98
 
99
+ ### Special case: frame params arriving out-of-order in `param_by_frame`
100
+ In some cases, frames in `param_by_frame` might be out of order. We can handle these scenarios by populating `param_by_frame` with a special `parallel_animate.IndexedFrameParams` dataclass, which specifies the frame index that overrides the ordering in `param_by_frame`. This can be useful when, for example, the animator needs to draw frames that are decoded from a video, and the dataloader for that video might return frames in nondeterministic order because it's parallelized.
101
+
102
+ See `src/parallel_animate/examples/nondeterministic_video_loader.py` for details.
83
103
 
84
104
  ## Examples
85
105
 
86
106
  See [`src/parallel_animate/examples/`](https://github.com/sibocw/parallel-matplotlib-animation/blob/main/src/parallel_animate/examples/):
87
- - `simple_wave_animation.py`: The example above
88
- - `multi_panel_animation.py`: 5 subplots with different plot types
89
- - `very_complex_animation.py`: 14 subplots with GridSpec layout
107
+
108
+ `simple_wave_animation.py`: The example above
109
+
110
+ `multi_panel_animation.py`: 5 subplots with different plot types
111
+
112
+ <img src="assets/multi_panel_animation.gif" width="480"/>
113
+
114
+ `very_complex_animation.py`: 14 subplots with GridSpec layout
115
+
116
+ <img src="assets/very_complex_animation.gif" width="480"/>
117
+
118
+ `nondeterministic_video_loader.py`: handling frames that arrive out of order
119
+
120
+ <img src="assets/nondeterministic_video_loader.gif" width="240"/>
90
121
 
91
122
 
92
123
  ## Performance test
@@ -1,24 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: parallel-matplotlib-animation
3
- Version: 0.1.0
4
- Summary: Animate matplotlib figures into videos, in parallel but with efficient caching
5
- License-File: LICENSE
6
- Author: Sibo Wang-Chen
7
- Author-email: sibo.wang@epfl.ch
8
- Requires-Python: >=3.10
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Programming Language :: Python :: 3.10
11
- Classifier: Programming Language :: Python :: 3.11
12
- Classifier: Programming Language :: Python :: 3.12
13
- Classifier: Programming Language :: Python :: 3.13
14
- Classifier: Programming Language :: Python :: 3.14
15
- Requires-Dist: Pillow (>=11.0)
16
- Requires-Dist: av (>=14.0)
17
- Requires-Dist: matplotlib (>=3.10.0)
18
- Requires-Dist: numpy (>=2,<3)
19
- Requires-Dist: tqdm (>=4.67)
20
- Description-Content-Type: text/markdown
21
-
22
1
  # parallel-matplotlib-animation
23
2
 
24
3
  Create matplotlib animations rendered to video in parallel, with efficient resources reuse.
@@ -84,7 +63,7 @@ anim = WaveAnimation()
84
63
  anim.make_video("wave.mp4", param_by_frame=params, fps=30, num_workers=4)
85
64
  ```
86
65
 
87
- ![](assets/simple_wave_animation.gif)
66
+ <img src="assets/simple_wave_animation.gif" width="480"/>
88
67
 
89
68
  ## Usage
90
69
  This library has a single class: `parallel_animate.Animator`. To make an animation, you must create your own class inheriting from it and define the following methods:
@@ -96,18 +75,34 @@ This library has a single class: `parallel_animate.Animator`. To make an animati
96
75
  Once you have defined your animator class, there is a single method that you need to call that makes the video: **`.make_video(...)`**. It accepts the following arguments:
97
76
 
98
77
  - `output_file` (Path or str): Output video path
99
- - `param_by_frame` (list): List of parameters. Each element in the list is the `params` argument to be given to the `.update` call for the corresponding frame.
78
+ - `param_by_frame` (Iterable): Iterable of parameters. Each element is the `params` argument to be given to the `.update` call for the corresponding frame. Can be a list, tuple, generator, or any other iterable. Using generators is particularly useful for large data (e.g., bitmaps) to avoid loading everything into memory at once.
100
79
  - `fps` (int): Frame rate of the output video
80
+ - `n_frames` (int or None): Number of frames to render. If None, use the length of `param_by_frame`. If param_by_frame does not have `__len__` implemented and `n_frames` is None, the progress bar won't show completion percentage.
101
81
  - `num_workers` (int): Number of worker processes to be spawned. If -1, use all CPU cores. If -2, use all but one CPU cores, etc. If 1, no child process is created and the video is made in the main process itself. Default is -1.
102
82
  - See the docstring for `parallel_animate.animator` directly for less commonly used, optional parameters. These control logging, rendering quality, etc.
103
83
 
84
+ ### Special case: frame params arriving out-of-order in `param_by_frame`
85
+ In some cases, frames in `param_by_frame` might be out of order. We can handle these scenarios by populating `param_by_frame` with a special `parallel_animate.IndexedFrameParams` dataclass, which specifies the frame index that overrides the ordering in `param_by_frame`. This can be useful when, for example, the animator needs to draw frames that are decoded from a video, and the dataloader for that video might return frames in nondeterministic order because it's parallelized.
86
+
87
+ See `src/parallel_animate/examples/nondeterministic_video_loader.py` for details.
104
88
 
105
89
  ## Examples
106
90
 
107
91
  See [`src/parallel_animate/examples/`](https://github.com/sibocw/parallel-matplotlib-animation/blob/main/src/parallel_animate/examples/):
108
- - `simple_wave_animation.py`: The example above
109
- - `multi_panel_animation.py`: 5 subplots with different plot types
110
- - `very_complex_animation.py`: 14 subplots with GridSpec layout
92
+
93
+ `simple_wave_animation.py`: The example above
94
+
95
+ `multi_panel_animation.py`: 5 subplots with different plot types
96
+
97
+ <img src="assets/multi_panel_animation.gif" width="480"/>
98
+
99
+ `very_complex_animation.py`: 14 subplots with GridSpec layout
100
+
101
+ <img src="assets/very_complex_animation.gif" width="480"/>
102
+
103
+ `nondeterministic_video_loader.py`: handling frames that arrive out of order
104
+
105
+ <img src="assets/nondeterministic_video_loader.gif" width="240"/>
111
106
 
112
107
 
113
108
  ## Performance test
@@ -122,4 +117,4 @@ The left-most blue dot indicates serial processing with resources reuse. The bla
122
117
  ## Unit tests
123
118
  ```bash
124
119
  python -m unittest discover -s tests
125
- ```
120
+ ```
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Convert an MP4 video to a GIF with specified scale and optional frame limit.
4
+ # Useful for creating GIFs from rendered videos for documentation.
5
+ # Usage:
6
+ # ./mp4_to_gif.sh input.mp4 output.gif scale max_frames
7
+ # Example:
8
+ # ./mp4_to_gif.sh input.mp4 output.gif 320 100 # limit to 100 frames
9
+ # ./mp4_to_gif.sh input.mp4 output.gif 320 0 # unlimited
10
+
11
+ input_path="$1"
12
+ output_path="$2"
13
+ scale="$3"
14
+ max_frames="$4"
15
+
16
+ # Check arguments
17
+ if [ $# -lt 3 ] || [ $# -gt 4 ]; then
18
+ echo "Usage: $0 <input_path> <output_path> <scale> [max_frames]"
19
+ exit 1
20
+ fi
21
+
22
+ # Set frame limit option
23
+ frame_limit=""
24
+ if [ -n "$max_frames" ] && [ "$max_frames" -gt 0 ]; then
25
+ frame_limit="-vframes $max_frames"
26
+ fi
27
+
28
+ ffmpeg -i "$input_path" $frame_limit \
29
+ -filter_complex "fps=10,scale=${scale}:-1:flags=lanczos,split[s0][s1];[s0]palettegen=stats_mode=single[p];[s1][p]paletteuse=dither=sierra2_4a" \
30
+ -loop 0 "$output_path"
31
+
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "parallel-matplotlib-animation"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "Animate matplotlib figures into videos, in parallel but with efficient caching"
5
5
  authors = [
6
- {name = "Sibo Wang-Chen",email = "sibo.wang@epfl.ch"}
6
+ { name = "Sibo Wang-Chen", email = "sibo.wang@epfl.ch" }
7
7
  ]
8
8
  readme = "README.md"
9
9
  requires-python = ">=3.10"
@@ -15,9 +15,12 @@ dependencies = [
15
15
  "Pillow>=11.0",
16
16
  ]
17
17
 
18
- [tool.poetry]
19
- packages = [{include = "parallel_animate", from = "src"}]
18
+ [project.urls]
19
+ Repository = "https://github.com/sibocw/parallel-matplotlib-animation"
20
20
 
21
21
  [build-system]
22
- requires = ["poetry-core>=2.0.0,<3.0.0"]
23
- build-backend = "poetry.core.masonry.api"
22
+ requires = ["hatchling"]
23
+ build-backend = "hatchling.build"
24
+
25
+ [tool.hatch.build.targets.wheel]
26
+ packages = ["src/parallel_animate"]
@@ -0,0 +1 @@
1
+ from .animator import Animator, IndexedFrameParams