janim 0.1__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 (82) hide show
  1. janim-0.1/.gitignore +12 -0
  2. janim-0.1/.readthedocs.yaml +37 -0
  3. janim-0.1/.vscode/settings.json +16 -0
  4. janim-0.1/.vscode/tasks.json +13 -0
  5. janim-0.1/LICENSE +21 -0
  6. janim-0.1/PKG-INFO +51 -0
  7. janim-0.1/README.md +19 -0
  8. janim-0.1/janim/__init__.py +3 -0
  9. janim-0.1/janim/__main__.py +270 -0
  10. janim-0.1/janim/anims/animation.py +119 -0
  11. janim-0.1/janim/anims/composition.py +167 -0
  12. janim-0.1/janim/anims/creation.py +188 -0
  13. janim-0.1/janim/anims/display.py +29 -0
  14. janim-0.1/janim/anims/fading.py +118 -0
  15. janim-0.1/janim/anims/rotation.py +45 -0
  16. janim-0.1/janim/anims/timeline.py +489 -0
  17. janim-0.1/janim/anims/transform.py +228 -0
  18. janim-0.1/janim/anims/updater.py +131 -0
  19. janim-0.1/janim/camera/camera.py +145 -0
  20. janim-0.1/janim/camera/camera_info.py +79 -0
  21. janim-0.1/janim/components/component.py +266 -0
  22. janim-0.1/janim/components/depth.py +87 -0
  23. janim-0.1/janim/components/image.py +45 -0
  24. janim-0.1/janim/components/points.py +1006 -0
  25. janim-0.1/janim/components/radius.py +105 -0
  26. janim-0.1/janim/components/rgbas.py +254 -0
  27. janim-0.1/janim/components/vpoints.py +510 -0
  28. janim-0.1/janim/constants/__init__.py +5 -0
  29. janim-0.1/janim/constants/alignment.py +7 -0
  30. janim-0.1/janim/constants/colors.py +66 -0
  31. janim-0.1/janim/constants/coord.py +20 -0
  32. janim-0.1/janim/constants/degrees.py +8 -0
  33. janim-0.1/janim/examples.py +32 -0
  34. janim-0.1/janim/gui/anim_viewer.py +746 -0
  35. janim-0.1/janim/gui/application.py +11 -0
  36. janim-0.1/janim/gui/export.png +0 -0
  37. janim-0.1/janim/gui/fixed_ratio_widget.py +36 -0
  38. janim-0.1/janim/gui/glwidget.py +33 -0
  39. janim-0.1/janim/gui/selector.py +222 -0
  40. janim-0.1/janim/imports.py +34 -0
  41. janim-0.1/janim/items/boolean_ops.py +149 -0
  42. janim-0.1/janim/items/geometry/arc.py +313 -0
  43. janim-0.1/janim/items/geometry/arrow.py +210 -0
  44. janim-0.1/janim/items/geometry/line.py +207 -0
  45. janim-0.1/janim/items/geometry/polygon.py +161 -0
  46. janim-0.1/janim/items/image_item.py +171 -0
  47. janim-0.1/janim/items/item.py +540 -0
  48. janim-0.1/janim/items/points.py +110 -0
  49. janim-0.1/janim/items/relation.py +199 -0
  50. janim-0.1/janim/items/svg/svg_item.py +118 -0
  51. janim-0.1/janim/items/svg/typst.py +67 -0
  52. janim-0.1/janim/items/svg/typst_template.typ +3 -0
  53. janim-0.1/janim/items/text/text.py +397 -0
  54. janim-0.1/janim/items/vitem.py +154 -0
  55. janim-0.1/janim/logger.py +13 -0
  56. janim-0.1/janim/render/base.py +103 -0
  57. janim-0.1/janim/render/file_writer.py +122 -0
  58. janim-0.1/janim/render/impl.py +235 -0
  59. janim-0.1/janim/render/shaders/dotcloud.frag.glsl +21 -0
  60. janim-0.1/janim/render/shaders/dotcloud.geom.glsl +50 -0
  61. janim-0.1/janim/render/shaders/dotcloud.vert.glsl +19 -0
  62. janim-0.1/janim/render/shaders/image.frag.glsl +13 -0
  63. janim-0.1/janim/render/shaders/image.vert.glsl +18 -0
  64. janim-0.1/janim/render/shaders/vitem.frag.glsl +254 -0
  65. janim-0.1/janim/render/shaders/vitem.vert.glsl +14 -0
  66. janim-0.1/janim/render/texture.py +32 -0
  67. janim-0.1/janim/typing.py +31 -0
  68. janim-0.1/janim/utils/bezier.py +405 -0
  69. janim-0.1/janim/utils/config.py +156 -0
  70. janim-0.1/janim/utils/data.py +11 -0
  71. janim-0.1/janim/utils/file_ops.py +29 -0
  72. janim-0.1/janim/utils/font.py +79 -0
  73. janim-0.1/janim/utils/font_manager.py +177 -0
  74. janim-0.1/janim/utils/iterables.py +206 -0
  75. janim-0.1/janim/utils/paths.py +60 -0
  76. janim-0.1/janim/utils/rate_functions.py +111 -0
  77. janim-0.1/janim/utils/refresh.py +64 -0
  78. janim-0.1/janim/utils/signal.py +280 -0
  79. janim-0.1/janim/utils/simple_functions.py +81 -0
  80. janim-0.1/janim/utils/space_ops.py +360 -0
  81. janim-0.1/janim/utils/unique_nparray.py +37 -0
  82. janim-0.1/pyproject.toml +49 -0
janim-0.1/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ __pycache__
2
+ /.vscode/launch.json
3
+ /dist
4
+ /refactor_deprecated
5
+ /test_some_code
6
+ /test/test.py
7
+ /doc/build
8
+ /videos
9
+ /.coverage
10
+ /env
11
+ /result.json
12
+ /result2.json
@@ -0,0 +1,37 @@
1
+ # .readthedocs.yaml
2
+ # Read the Docs configuration file
3
+ # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4
+
5
+ # Required
6
+ version: 2
7
+
8
+ # Set the OS, Python version and other tools you might need
9
+ build:
10
+ os: ubuntu-22.04
11
+ tools:
12
+ python: "3.12"
13
+ # You can also specify other tool versions:
14
+ # nodejs: "19"
15
+ # rust: "1.64"
16
+ # golang: "1.19"
17
+
18
+ # Build documentation in the "docs/" directory with Sphinx
19
+ sphinx:
20
+ # configuration: docs/conf.py
21
+ configuration: doc/source/conf.py
22
+
23
+ # Optionally build your docs in additional formats such as PDF and ePub
24
+ # formats:
25
+ # - pdf
26
+ # - epub
27
+
28
+ # Optional but recommended, declare the Python requirements required
29
+ # to build your documentation
30
+ # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
31
+ # python:
32
+ # install:
33
+ # - requirements: doc/source/requirements.txt
34
+ python:
35
+ install:
36
+ - method: pip
37
+ path: .[doc]
@@ -0,0 +1,16 @@
1
+ {
2
+ "todo-tree.tree.scanMode": "workspace only",
3
+ "flake8.args": [
4
+ "--max-line-length=120",
5
+ "--ignore=E701,E704,E731,F821,F811",
6
+ "--exclude=test/*,refactor_deprecated/*"
7
+ ],
8
+ "todo-tree.highlights.customHighlight": {
9
+ "TODO": {
10
+ "iconColour": "yellow"
11
+ },
12
+ "REFACTOR": {
13
+ "icon": "tools"
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
3
+ // for the documentation about the tasks.json format
4
+ "version": "2.0.0",
5
+ "tasks": [
6
+ {
7
+ "label": "pyside6-uic",
8
+ "type": "shell",
9
+ "command": "pyside6-uic ${fileDirname}\\${fileBasename} -o ${fileDirname}\\ui_${fileBasenameNoExtension}.py",
10
+ "problemMatcher": []
11
+ }
12
+ ]
13
+ }
janim-0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 jkjkil4
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.
janim-0.1/PKG-INFO ADDED
@@ -0,0 +1,51 @@
1
+ Metadata-Version: 2.1
2
+ Name: janim
3
+ Version: 0.1
4
+ Summary: a library for simple animation effects
5
+ Author: jkjkil4
6
+ Requires-Python: >=3.12
7
+ Description-Content-Type: text/markdown
8
+ Classifier: Programming Language :: Python
9
+ Requires-Dist: numpy
10
+ Requires-Dist: scipy
11
+ Requires-Dist: colour
12
+ Requires-Dist: rich
13
+ Requires-Dist: glm
14
+ Requires-Dist: moderngl
15
+ Requires-Dist: tqdm
16
+ Requires-Dist: psutil
17
+ Requires-Dist: skia-pathops
18
+ Requires-Dist: fontTools
19
+ Requires-Dist: freetype-py
20
+ Requires-Dist: pillow
21
+ Requires-Dist: svgelements
22
+ Requires-Dist: sphinx ; extra == "doc"
23
+ Requires-Dist: furo ; extra == "doc"
24
+ Requires-Dist: pyside6 ; extra == "gui"
25
+ Requires-Dist: qdarkstyle ; extra == "gui"
26
+ Project-URL: Documentation, https://janim.rtfd.io
27
+ Project-URL: Home, https://github.com/jkjkil4/JAnim
28
+ Project-URL: Source, https://github.com/jkjkil4/JAnim
29
+ Provides-Extra: doc
30
+ Provides-Extra: gui
31
+
32
+ # JAnim
33
+
34
+ #### 介绍
35
+
36
+ - 功能不完善,千万不要用!
37
+ - 代码很混乱,千万不要看!
38
+
39
+ #### 安装教程
40
+
41
+ 1. xxxx
42
+ 2. xxxx
43
+ 3. xxxx
44
+
45
+ #### 使用说明
46
+
47
+ 1. xxxx
48
+ 2. xxxx
49
+ 3. xxxx
50
+
51
+
janim-0.1/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # JAnim
2
+
3
+ #### 介绍
4
+
5
+ - 功能不完善,千万不要用!
6
+ - 代码很混乱,千万不要看!
7
+
8
+ #### 安装教程
9
+
10
+ 1. xxxx
11
+ 2. xxxx
12
+ 3. xxxx
13
+
14
+ #### 使用说明
15
+
16
+ 1. xxxx
17
+ 2. xxxx
18
+ 3. xxxx
19
+
@@ -0,0 +1,3 @@
1
+ '''a library for simple animation effects'''
2
+
3
+ __version__ = '0.1'
@@ -0,0 +1,270 @@
1
+ import importlib
2
+ import time
3
+ import os
4
+ import platform
5
+ import subprocess as sp
6
+ from argparse import ArgumentParser, Namespace
7
+
8
+ from janim.anims.timeline import Timeline
9
+ from janim.logger import log
10
+ from janim.utils.config import Config, default_config
11
+
12
+
13
+ def main() -> None:
14
+ parser = ArgumentParser(description='A library for simple animation effects')
15
+ parser.set_defaults(func=None)
16
+
17
+ sp = parser.add_subparsers()
18
+ run_parser(sp.add_parser('run', help='Run timeline(s) from specific namespace'))
19
+ write_parser(sp.add_parser('write', help='Generate video file(s) of timeline(s) from specific namesapce'))
20
+ examples_parser(sp.add_parser('examples', help='Show examples of janim'))
21
+
22
+ args = parser.parse_args()
23
+ if args.func is None:
24
+ parser.print_help()
25
+ else:
26
+ args.func(args)
27
+
28
+
29
+ def render_args(parser: ArgumentParser) -> None:
30
+ parser.add_argument(
31
+ 'namespace',
32
+ help='Namespace to file holding the python code for the timeline'
33
+ )
34
+ parser.add_argument(
35
+ 'timeline_names',
36
+ nargs='*',
37
+ help='Name of the Timeline class you want to see'
38
+ )
39
+ parser.add_argument(
40
+ '-a', '--all',
41
+ action='store_true',
42
+ help='Render all timelines from a file'
43
+ )
44
+ parser.add_argument(
45
+ '-c', '--config',
46
+ nargs=2,
47
+ metavar=('key', 'value'),
48
+ action='append',
49
+ help='Override config'
50
+ )
51
+
52
+
53
+ def run_parser(parser: ArgumentParser) -> None:
54
+ render_args(parser)
55
+ parser.add_argument(
56
+ '-i', '--interact',
57
+ action='store_true',
58
+ help='Enable the network socket for interacting'
59
+ )
60
+ parser.set_defaults(func=run)
61
+
62
+
63
+ def write_parser(parser: ArgumentParser) -> None:
64
+ render_args(parser)
65
+ parser.add_argument(
66
+ '-o', '--open',
67
+ action='store_true',
68
+ help='Open the file after writing'
69
+ )
70
+ parser.add_argument(
71
+ '--format',
72
+ choices=['mp4', 'mov'],
73
+ default='mp4',
74
+ help='Format of the output file'
75
+ )
76
+ parser.set_defaults(func=write)
77
+
78
+
79
+ def examples_parser(parser: ArgumentParser) -> None:
80
+ parser.set_defaults(namespace='janim.examples')
81
+ parser.add_argument(
82
+ 'timeline_names',
83
+ nargs='*',
84
+ help='Name of the example you want to see'
85
+ )
86
+ parser.set_defaults(all=None)
87
+ parser.set_defaults(config=None)
88
+ parser.set_defaults(func=run)
89
+ parser.set_defaults(interact=False)
90
+
91
+
92
+ def run(args: Namespace) -> None:
93
+ module = get_module(args.namespace)
94
+ if module is None:
95
+ return
96
+ modify_default_config(args)
97
+
98
+ timelines = extract_timelines_from_module(args, module)
99
+ if not timelines:
100
+ return
101
+
102
+ auto_play = len(timelines) == 1
103
+
104
+ from janim.gui.anim_viewer import AnimViewer
105
+ from janim.gui.application import Application
106
+ from PySide6.QtCore import QPoint, QTimer
107
+
108
+ app = Application()
109
+
110
+ log.info('======')
111
+
112
+ widgets: list[AnimViewer] = []
113
+ for timeline in timelines:
114
+ viewer = AnimViewer(timeline().build(), auto_play, args.interact)
115
+ widgets.append(viewer)
116
+
117
+ log.info('======')
118
+ log.info('Constructing window')
119
+
120
+ t = time.time()
121
+
122
+ for i, widget in enumerate(widgets):
123
+ if i != 0:
124
+ widget.move(widgets[i - 1].pos() + QPoint(24, 24))
125
+ widget.show()
126
+
127
+ QTimer.singleShot(200, widgets[-1].activateWindow)
128
+
129
+ log.info(f'Finished constructing in {time.time() - t:.2f} s')
130
+ log.info('======')
131
+
132
+ app.exec()
133
+
134
+
135
+ def write(args: Namespace) -> None:
136
+ module = get_module(args.namespace)
137
+ if module is None:
138
+ return
139
+ modify_default_config(args)
140
+
141
+ timelines = extract_timelines_from_module(args, module)
142
+ if not timelines:
143
+ return
144
+
145
+ from janim.render.file_writer import FileWriter
146
+
147
+ log.info('======')
148
+
149
+ built = [timeline().build() for timeline in timelines]
150
+
151
+ log.info('======')
152
+
153
+ output_dir = os.path.normpath(Config.get.output_dir)
154
+ if not os.path.exists(output_dir):
155
+ os.makedirs(output_dir)
156
+
157
+ log.info(f'fps={Config.get.fps}')
158
+ log.info(f'resolution="{Config.get.pixel_width}x{Config.get.pixel_height}"')
159
+ log.info(f'output_dir="{output_dir}"')
160
+ log.info(f'format="{args.format}"')
161
+
162
+ log.info('======')
163
+
164
+ for anim in built:
165
+ writer = FileWriter(anim)
166
+ writer.write_all(
167
+ os.path.join(Config.get.output_dir,
168
+ f'{anim.timeline.__class__.__name__}.{args.format}')
169
+ )
170
+ if args.open and anim is built[-1]:
171
+ open_file(writer.final_file_path)
172
+
173
+ log.info('======')
174
+
175
+
176
+ def modify_default_config(args: Namespace) -> None:
177
+ if args.config:
178
+ for key, value in args.config:
179
+ dtype = type(getattr(default_config, key))
180
+ setattr(default_config, key, dtype(value))
181
+
182
+
183
+ def get_module(namespace: str):
184
+ try:
185
+ return importlib.import_module(namespace)
186
+ except ModuleNotFoundError:
187
+ log.error(f'No module named "{namespace}"')
188
+ return None
189
+
190
+
191
+ def extract_timelines_from_module(args: Namespace, module) -> list[type[Timeline]]:
192
+ timelines = []
193
+ err = False
194
+
195
+ if not args.all and args.timeline_names:
196
+ for name in args.timeline_names:
197
+ try:
198
+ timelines.append(module.__dict__[name])
199
+ except KeyError:
200
+ log.error(f'No timeline named "{name}"')
201
+ err = True
202
+ else:
203
+ import inspect
204
+
205
+ classes = [
206
+ value
207
+ for value in module.__dict__.values()
208
+ if isinstance(value, type) and issubclass(value, Timeline) and value.__module__ == module.__name__
209
+ ]
210
+ if len(classes) <= 1:
211
+ return classes
212
+ classes.sort(key=lambda x: inspect.getsourcelines(x)[1])
213
+ if args.all:
214
+ return classes
215
+
216
+ max_digits = len(str(len(classes)))
217
+
218
+ name_to_class = {}
219
+ for idx, timeline_class in enumerate(classes, start=1):
220
+ name = timeline_class.__name__
221
+ print(f"{str(idx).zfill(max_digits)}: {name}")
222
+ name_to_class[name] = timeline_class
223
+
224
+ user_input = input(
225
+ "\nThat module has multiple timelines, "
226
+ "which ones would you like to render?"
227
+ "\nTimeline Name or Number: "
228
+ )
229
+ for split_str in user_input.replace(' ', '').split(','):
230
+ if not split_str:
231
+ continue
232
+ if split_str.isnumeric():
233
+ idx = int(split_str) - 1
234
+ if 0 <= idx < len(classes):
235
+ timelines.append(classes[idx])
236
+ else:
237
+ log.error(f'Invaild number {idx + 1}')
238
+ err = True
239
+ else:
240
+ try:
241
+ timelines.append(name_to_class[split_str])
242
+ except KeyError:
243
+ log.error(f'No timeline named {split_str}')
244
+ err = True
245
+
246
+ return [] if err else timelines
247
+
248
+
249
+ def open_file(file_path: str) -> None:
250
+ current_os = platform.system()
251
+ if current_os == "Windows":
252
+ os.startfile(file_path)
253
+ else:
254
+ commands = []
255
+ if current_os == "Linux":
256
+ commands.append("xdg-open")
257
+ elif current_os.startswith("CYGWIN"):
258
+ commands.append("cygstart")
259
+ else: # Assume macOS
260
+ commands.append("open")
261
+
262
+ commands.append(file_path)
263
+
264
+ FNULL = open(os.devnull, 'w')
265
+ sp.call(commands, stdout=FNULL, stderr=sp.STDOUT)
266
+ FNULL.close()
267
+
268
+
269
+ if __name__ == '__main__':
270
+ main()
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ from contextvars import ContextVar
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, Callable
6
+
7
+ from janim.components.depth import Cmpt_Depth
8
+ from janim.utils.rate_functions import RateFunc, smooth
9
+
10
+ if TYPE_CHECKING:
11
+ from janim.anims.composition import AnimGroup
12
+
13
+
14
+ @dataclass
15
+ class TimeRange:
16
+ at: float
17
+ duration: float
18
+
19
+ @property
20
+ def end(self) -> float:
21
+ return self.at + self.duration
22
+
23
+
24
+ @dataclass
25
+ class RenderCall:
26
+ '''
27
+ 绘制调用
28
+
29
+ - ``depth``: 该绘制的深度
30
+ - ``func``: 该绘制所调用的函数
31
+
32
+ 具体机制:
33
+
34
+ - 在每个动画对象中,都会使用 :meth:`~.Animation.set_render_call_list` 来设置该动画进行绘制时所执行的函数
35
+ - 在进行渲染(具体参考 :meth:`~.TimelineAnim.render_all` )时,会按照深度进行排序,依次对 ``func`` 进行调用,深度越高的越先调用
36
+
37
+ 例:
38
+
39
+ - 在 :class:`~.Display` 中,设置了单个 :class:`RenderCall` ,作用是绘制物件
40
+ - 在 :class:`~.Transform` 中,对于每个插值物件都设置了 :class:`RenderCall`,绘制所有的插值物件
41
+ '''
42
+ depth: Cmpt_Depth
43
+ func: Callable[[], None]
44
+
45
+
46
+ class Animation:
47
+ '''
48
+ 动画基类
49
+
50
+ - 创建一个从 ``at`` 持续至 ``at + duration`` 的动画
51
+ - 指定 ``rate_func`` 可以设定插值函数,默认为 :meth:`janim.utils.rate_functions.smooth` 即平滑插值
52
+ '''
53
+ label_color: tuple[float, float, float] = (128, 132, 137)
54
+
55
+ def __init__(
56
+ self,
57
+ *,
58
+ at: float = 0,
59
+ duration: float = 1.0,
60
+ rate_func: RateFunc = smooth,
61
+ ):
62
+ from janim.anims.timeline import Timeline
63
+ self.timeline = Timeline.get_context()
64
+ self.parent: AnimGroup = None
65
+ self.current_alpha = None
66
+
67
+ self.local_range = TimeRange(at, duration)
68
+ self.global_range = None
69
+ self.rate_func = rate_func
70
+
71
+ self.render_call_list: list[RenderCall] = []
72
+
73
+ def set_global_range(self, at: float, duration: float | None = None) -> None:
74
+ '''
75
+ 设置在 :class:`~.Timeline` 上的时间范围
76
+
77
+ 不需要手动设置,该方法是被 :meth:`~.AnimGroup.set_global_range` 调用以计算的
78
+ '''
79
+ if duration is None:
80
+ duration = self.local_range.duration
81
+ self.global_range = TimeRange(at, duration)
82
+
83
+ def set_render_call_list(self, lst: list[RenderCall]) -> None:
84
+ '''
85
+ 设置绘制调用,具体参考 :class:`RenderCall`
86
+ '''
87
+ self.render_call_list = sorted(lst, key=lambda x: x.depth, reverse=True)
88
+
89
+ def anim_pre_init(self) -> None: '''在 :meth:`~.Timeline.detect_changes_of_all` 执行之前调用的初始化方法'''
90
+
91
+ def anim_init(self) -> None: '''在 :meth:`~.Timeline.detect_changes_of_all` 执行之前调用的初始化方法'''
92
+
93
+ def anim_on(self, local_t: float) -> None:
94
+ '''
95
+ 将 ``local_t`` 换算为 ``alpha`` 并调用 :meth:`anim_on_alpha`
96
+ '''
97
+ alpha = self.rate_func(local_t / self.local_range.duration)
98
+ self.anim_on_alpha(alpha)
99
+
100
+ def get_alpha_on_global_t(self, global_t: float) -> float:
101
+ '''
102
+ 传入全局 ``global_t``,得到物件在该时刻应当处于哪个 ``alpha`` 的插值
103
+ '''
104
+ if self.parent is None:
105
+ return self.rate_func((global_t - self.global_range.at) / self.global_range.duration)
106
+
107
+ anim_t = self.parent.get_anim_t(self.parent.get_alpha_on_global_t(global_t), self)
108
+ return self.rate_func(anim_t / self.local_range.duration)
109
+
110
+ global_t_ctx: ContextVar[float] = ContextVar('Animation.global_t_ctx')
111
+ '''
112
+ 对该值进行设置,使得进行 :meth:`anim_on` 和 :meth:`render` 时不需要将 ``global_t`` 作为参数传递也能获取到
113
+ '''
114
+
115
+ def anim_on_alpha(self, alpha: float) -> None:
116
+ '''
117
+ 动画在 ``alpha`` 处的行为
118
+ '''
119
+ pass