manim 0.18.0.post0__py3-none-any.whl → 0.19.0__py3-none-any.whl

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.

Potentially problematic release.


This version of manim might be problematic. Click here for more details.

Files changed (146) hide show
  1. manim/__init__.py +3 -6
  2. manim/__main__.py +61 -20
  3. manim/_config/__init__.py +6 -3
  4. manim/_config/cli_colors.py +16 -8
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +14 -8
  7. manim/_config/utils.py +651 -472
  8. manim/animation/animation.py +152 -5
  9. manim/animation/composition.py +80 -39
  10. manim/animation/creation.py +196 -14
  11. manim/animation/fading.py +5 -9
  12. manim/animation/indication.py +103 -47
  13. manim/animation/movement.py +22 -5
  14. manim/animation/rotation.py +3 -2
  15. manim/animation/specialized.py +4 -6
  16. manim/animation/speedmodifier.py +10 -5
  17. manim/animation/transform.py +4 -5
  18. manim/animation/transform_matching_parts.py +1 -1
  19. manim/animation/updaters/mobject_update_utils.py +17 -14
  20. manim/camera/camera.py +15 -6
  21. manim/cli/__init__.py +17 -0
  22. manim/cli/cfg/group.py +70 -44
  23. manim/cli/checkhealth/checks.py +93 -75
  24. manim/cli/checkhealth/commands.py +14 -5
  25. manim/cli/default_group.py +157 -25
  26. manim/cli/init/commands.py +32 -24
  27. manim/cli/plugins/commands.py +16 -3
  28. manim/cli/render/commands.py +72 -60
  29. manim/cli/render/ease_of_access_options.py +4 -3
  30. manim/cli/render/global_options.py +51 -15
  31. manim/cli/render/output_options.py +6 -5
  32. manim/cli/render/render_options.py +97 -32
  33. manim/constants.py +65 -19
  34. manim/gui/gui.py +2 -0
  35. manim/mobject/frame.py +0 -1
  36. manim/mobject/geometry/arc.py +112 -78
  37. manim/mobject/geometry/boolean_ops.py +32 -25
  38. manim/mobject/geometry/labeled.py +300 -77
  39. manim/mobject/geometry/line.py +132 -64
  40. manim/mobject/geometry/polygram.py +126 -30
  41. manim/mobject/geometry/shape_matchers.py +35 -15
  42. manim/mobject/geometry/tips.py +38 -29
  43. manim/mobject/graph.py +414 -133
  44. manim/mobject/graphing/coordinate_systems.py +126 -64
  45. manim/mobject/graphing/functions.py +25 -15
  46. manim/mobject/graphing/number_line.py +24 -10
  47. manim/mobject/graphing/probability.py +2 -10
  48. manim/mobject/graphing/scale.py +6 -5
  49. manim/mobject/matrix.py +17 -19
  50. manim/mobject/mobject.py +314 -165
  51. manim/mobject/opengl/opengl_compatibility.py +2 -0
  52. manim/mobject/opengl/opengl_geometry.py +30 -9
  53. manim/mobject/opengl/opengl_image_mobject.py +2 -0
  54. manim/mobject/opengl/opengl_mobject.py +509 -343
  55. manim/mobject/opengl/opengl_point_cloud_mobject.py +5 -7
  56. manim/mobject/opengl/opengl_surface.py +3 -2
  57. manim/mobject/opengl/opengl_three_dimensions.py +2 -0
  58. manim/mobject/opengl/opengl_vectorized_mobject.py +46 -79
  59. manim/mobject/svg/brace.py +63 -13
  60. manim/mobject/svg/svg_mobject.py +4 -3
  61. manim/mobject/table.py +11 -13
  62. manim/mobject/text/code_mobject.py +186 -548
  63. manim/mobject/text/numbers.py +9 -7
  64. manim/mobject/text/tex_mobject.py +23 -14
  65. manim/mobject/text/text_mobject.py +70 -24
  66. manim/mobject/three_d/polyhedra.py +98 -1
  67. manim/mobject/three_d/three_d_utils.py +4 -4
  68. manim/mobject/three_d/three_dimensions.py +62 -34
  69. manim/mobject/types/image_mobject.py +42 -24
  70. manim/mobject/types/point_cloud_mobject.py +105 -67
  71. manim/mobject/types/vectorized_mobject.py +496 -228
  72. manim/mobject/value_tracker.py +5 -4
  73. manim/mobject/vector_field.py +5 -5
  74. manim/opengl/__init__.py +3 -3
  75. manim/plugins/__init__.py +14 -1
  76. manim/plugins/plugins_flags.py +14 -8
  77. manim/renderer/cairo_renderer.py +20 -10
  78. manim/renderer/opengl_renderer.py +21 -23
  79. manim/renderer/opengl_renderer_window.py +2 -0
  80. manim/renderer/shader.py +2 -3
  81. manim/renderer/shader_wrapper.py +5 -2
  82. manim/renderer/vectorized_mobject_rendering.py +5 -0
  83. manim/scene/moving_camera_scene.py +23 -0
  84. manim/scene/scene.py +90 -43
  85. manim/scene/scene_file_writer.py +316 -165
  86. manim/scene/section.py +17 -15
  87. manim/scene/three_d_scene.py +13 -21
  88. manim/scene/vector_space_scene.py +22 -9
  89. manim/typing.py +830 -70
  90. manim/utils/bezier.py +1667 -399
  91. manim/utils/caching.py +13 -5
  92. manim/utils/color/AS2700.py +2 -0
  93. manim/utils/color/BS381.py +3 -0
  94. manim/utils/color/DVIPSNAMES.py +96 -0
  95. manim/utils/color/SVGNAMES.py +179 -0
  96. manim/utils/color/X11.py +3 -0
  97. manim/utils/color/XKCD.py +3 -0
  98. manim/utils/color/__init__.py +8 -5
  99. manim/utils/color/core.py +844 -309
  100. manim/utils/color/manim_colors.py +7 -9
  101. manim/utils/commands.py +48 -20
  102. manim/utils/config_ops.py +18 -13
  103. manim/utils/debug.py +8 -7
  104. manim/utils/deprecation.py +90 -40
  105. manim/utils/docbuild/__init__.py +17 -0
  106. manim/utils/docbuild/autoaliasattr_directive.py +234 -0
  107. manim/utils/docbuild/autocolor_directive.py +21 -17
  108. manim/utils/docbuild/manim_directive.py +50 -35
  109. manim/utils/docbuild/module_parsing.py +245 -0
  110. manim/utils/exceptions.py +6 -0
  111. manim/utils/family.py +5 -3
  112. manim/utils/family_ops.py +17 -4
  113. manim/utils/file_ops.py +26 -16
  114. manim/utils/hashing.py +9 -7
  115. manim/utils/images.py +10 -4
  116. manim/utils/ipython_magic.py +14 -8
  117. manim/utils/iterables.py +161 -119
  118. manim/utils/module_ops.py +57 -19
  119. manim/utils/opengl.py +83 -24
  120. manim/utils/parameter_parsing.py +32 -0
  121. manim/utils/paths.py +21 -23
  122. manim/utils/polylabel.py +168 -0
  123. manim/utils/qhull.py +218 -0
  124. manim/utils/rate_functions.py +74 -39
  125. manim/utils/simple_functions.py +24 -15
  126. manim/utils/sounds.py +7 -1
  127. manim/utils/space_ops.py +125 -69
  128. manim/utils/testing/__init__.py +17 -0
  129. manim/utils/testing/_frames_testers.py +13 -8
  130. manim/utils/testing/_show_diff.py +5 -3
  131. manim/utils/testing/_test_class_makers.py +33 -18
  132. manim/utils/testing/frames_comparison.py +27 -19
  133. manim/utils/tex.py +127 -197
  134. manim/utils/tex_file_writing.py +47 -45
  135. manim/utils/tex_templates.py +2 -1
  136. manim/utils/unit.py +6 -5
  137. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE.community +1 -1
  138. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/METADATA +40 -39
  139. manim-0.19.0.dist-info/RECORD +221 -0
  140. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
  141. manim/cli/new/__init__.py +0 -0
  142. manim/cli/new/group.py +0 -189
  143. manim/plugins/import_plugins.py +0 -43
  144. manim-0.18.0.post0.dist-info/RECORD +0 -217
  145. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
  146. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/entry_points.txt +0 -0
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import functools
4
4
  import inspect
5
5
  from pathlib import Path
6
- from typing import Callable
6
+ from typing import Any, Callable
7
7
 
8
8
  import cairo
9
9
  import pytest
@@ -14,7 +14,9 @@ from manim._config import tempconfig
14
14
  from manim._config.utils import ManimConfig
15
15
  from manim.camera.three_d_camera import ThreeDCamera
16
16
  from manim.renderer.cairo_renderer import CairoRenderer
17
+ from manim.renderer.opengl_renderer import OpenGLRenderer
17
18
  from manim.scene.three_d_scene import ThreeDScene
19
+ from manim.typing import StrPath
18
20
 
19
21
  from ._frames_testers import _ControlDataWriter, _FramesTester
20
22
  from ._test_class_makers import (
@@ -31,13 +33,13 @@ MIN_CAIRO_VERSION = 11800
31
33
 
32
34
 
33
35
  def frames_comparison(
34
- func=None,
36
+ func: Callable | None = None,
35
37
  *,
36
38
  last_frame: bool = True,
37
- renderer_class=CairoRenderer,
38
- base_scene=Scene,
39
- **custom_config,
40
- ):
39
+ renderer_class: type[CairoRenderer | OpenGLRenderer] = CairoRenderer,
40
+ base_scene: type[Scene] = Scene,
41
+ **custom_config: Any,
42
+ ) -> Callable:
41
43
  """Compares the frames generated by the test with control frames previously registered.
42
44
 
43
45
  If there is no control frames for this test, the test will fail. To generate
@@ -60,7 +62,7 @@ def frames_comparison(
60
62
  If the scene has a moving animation, then the test must set last_frame to False.
61
63
  """
62
64
 
63
- def decorator_maker(tested_scene_construct):
65
+ def decorator_maker(tested_scene_construct: Callable) -> Callable:
64
66
  if (
65
67
  SCENE_PARAMETER_NAME
66
68
  not in inspect.getfullargspec(tested_scene_construct).args
@@ -79,11 +81,14 @@ def frames_comparison(
79
81
  "There is no module test name indicated for the graphical unit test. You have to declare __module_test__ in the test file.",
80
82
  )
81
83
  module_name = tested_scene_construct.__globals__.get("__module_test__")
84
+ assert isinstance(module_name, str)
82
85
  test_name = tested_scene_construct.__name__[len("test_") :]
83
86
 
84
87
  @functools.wraps(tested_scene_construct)
85
88
  # The "request" parameter is meant to be used as a fixture by pytest. See below.
86
- def wrapper(*args, request: FixtureRequest, tmp_path, **kwargs):
89
+ def wrapper(
90
+ *args: Any, request: FixtureRequest, tmp_path: StrPath, **kwargs: Any
91
+ ) -> None:
87
92
  # check for cairo version
88
93
  if (
89
94
  renderer_class is CairoRenderer
@@ -146,13 +151,13 @@ def frames_comparison(
146
151
  inspect.Parameter("tmp_path", inspect.Parameter.KEYWORD_ONLY),
147
152
  ]
148
153
  new_sig = old_sig.replace(parameters=parameters)
149
- wrapper.__signature__ = new_sig
154
+ wrapper.__signature__ = new_sig # type: ignore[attr-defined]
150
155
 
151
156
  # Reach a bit into pytest internals to hoist the marks from our wrapped
152
157
  # function.
153
- setattr(wrapper, "pytestmark", [])
158
+ wrapper.pytestmark = [] # type: ignore[attr-defined]
154
159
  new_marks = getattr(tested_scene_construct, "pytestmark", [])
155
- wrapper.pytestmark = new_marks
160
+ wrapper.pytestmark = new_marks # type: ignore[attr-defined]
156
161
  return wrapper
157
162
 
158
163
  # Case where the decorator is called with and without parentheses.
@@ -192,9 +197,10 @@ def _make_test_comparing_frames(
192
197
  Callable[[], None]
193
198
  The pytest test.
194
199
  """
195
-
196
200
  if is_set_test_data_test:
197
- frames_tester = _ControlDataWriter(file_path, size_frame=size_frame)
201
+ frames_tester: _FramesTester = _ControlDataWriter(
202
+ file_path, size_frame=size_frame
203
+ )
198
204
  else:
199
205
  frames_tester = _FramesTester(file_path, show_diff=show_diff)
200
206
 
@@ -205,7 +211,7 @@ def _make_test_comparing_frames(
205
211
  )
206
212
  testRenderer = _make_test_renderer_class(renderer_class)
207
213
 
208
- def real_test():
214
+ def real_test() -> None:
209
215
  with frames_tester.testing():
210
216
  sceneTested = _make_test_scene_class(
211
217
  base_scene=base_scene,
@@ -214,11 +220,13 @@ def _make_test_comparing_frames(
214
220
  # If you pass a custom renderer to the Scene, the Camera class given as an argument in the Scene
215
221
  # is not passed to the renderer. See __init__ of Scene.
216
222
  # This potentially prevents OpenGL testing.
217
- test_renderer=testRenderer(file_writer_class=file_writer_class)
218
- if base_scene is not ThreeDScene
219
- else testRenderer(
220
- file_writer_class=file_writer_class,
221
- camera_class=ThreeDCamera,
223
+ test_renderer=(
224
+ testRenderer(file_writer_class=file_writer_class)
225
+ if base_scene is not ThreeDScene
226
+ else testRenderer(
227
+ file_writer_class=file_writer_class,
228
+ camera_class=ThreeDCamera,
229
+ )
222
230
  ), # testRenderer(file_writer_class=file_writer_class),
223
231
  )
224
232
  scene_tested = sceneTested(skip_animations=True)
manim/utils/tex.py CHANGED
@@ -4,162 +4,135 @@ from __future__ import annotations
4
4
 
5
5
  __all__ = [
6
6
  "TexTemplate",
7
- "TexTemplateFromFile",
8
7
  ]
9
8
 
10
9
  import copy
11
- import os
12
10
  import re
11
+ import warnings
12
+ from dataclasses import dataclass, field
13
13
  from pathlib import Path
14
+ from typing import TYPE_CHECKING, Any
14
15
 
16
+ if TYPE_CHECKING:
17
+ from typing_extensions import Self
15
18
 
16
- class TexTemplate:
17
- """TeX templates are used for creating Tex() and MathTex() objects.
18
-
19
- Parameters
20
- ----------
21
- tex_compiler
22
- The TeX compiler to be used, e.g. ``latex``, ``pdflatex`` or ``lualatex``
23
- output_format
24
- The output format resulting from compilation, e.g. ``.dvi`` or ``.pdf``
25
- documentclass
26
- The command defining the documentclass, e.g. ``\\documentclass[preview]{standalone}``
27
- preamble
28
- The document's preamble, i.e. the part between ``\\documentclass`` and ``\\begin{document}``
29
- placeholder_text
30
- Text in the document that will be replaced by the expression to be rendered
31
- post_doc_commands
32
- Text (definitions, commands) to be inserted at right after ``\\begin{document}``, e.g. ``\\boldmath``
33
-
34
- Attributes
35
- ----------
36
- tex_compiler : :class:`str`
37
- The TeX compiler to be used, e.g. ``latex``, ``pdflatex`` or ``lualatex``
38
- output_format : :class:`str`
39
- The output format resulting from compilation, e.g. ``.dvi`` or ``.pdf``
40
- documentclass : :class:`str`
41
- The command defining the documentclass, e.g. ``\\documentclass[preview]{standalone}``
42
- preamble : :class:`str`
43
- The document's preamble, i.e. the part between ``\\documentclass`` and ``\\begin{document}``
44
- placeholder_text : :class:`str`
45
- Text in the document that will be replaced by the expression to be rendered
46
- post_doc_commands : :class:`str`
47
- Text (definitions, commands) to be inserted at right after ``\\begin{document}``, e.g. ``\\boldmath``
48
- """
19
+ from manim.typing import StrPath
49
20
 
50
- default_documentclass = r"\documentclass[preview]{standalone}"
51
- default_preamble = r"""
52
- \usepackage[english]{babel}
21
+ _DEFAULT_PREAMBLE = r"""\usepackage[english]{babel}
53
22
  \usepackage{amsmath}
54
- \usepackage{amssymb}
55
- """
56
- default_placeholder_text = "YourTextHere"
57
- default_tex_compiler = "latex"
58
- default_output_format = ".dvi"
59
- default_post_doc_commands = ""
60
-
61
- def __init__(
62
- self,
63
- tex_compiler: str | None = None,
64
- output_format: str | None = None,
65
- documentclass: str | None = None,
66
- preamble: str | None = None,
67
- placeholder_text: str | None = None,
68
- post_doc_commands: str | None = None,
69
- **kwargs,
70
- ):
71
- self.tex_compiler = (
72
- tex_compiler
73
- if tex_compiler is not None
74
- else TexTemplate.default_tex_compiler
75
- )
76
- self.output_format = (
77
- output_format
78
- if output_format is not None
79
- else TexTemplate.default_output_format
80
- )
81
- self.documentclass = (
82
- documentclass
83
- if documentclass is not None
84
- else TexTemplate.default_documentclass
85
- )
86
- self.preamble = (
87
- preamble if preamble is not None else TexTemplate.default_preamble
88
- )
89
- self.placeholder_text = (
90
- placeholder_text
91
- if placeholder_text is not None
92
- else TexTemplate.default_placeholder_text
93
- )
94
- self.post_doc_commands = (
95
- post_doc_commands
96
- if post_doc_commands is not None
97
- else TexTemplate.default_post_doc_commands
98
- )
99
- self._rebuild()
100
-
101
- def __eq__(self, other: TexTemplate) -> bool:
102
- return (
103
- self.body == other.body
104
- and self.tex_compiler == other.tex_compiler
105
- and self.output_format == other.output_format
106
- and self.post_doc_commands == other.post_doc_commands
107
- )
23
+ \usepackage{amssymb}"""
24
+
25
+ _BEGIN_DOCUMENT = r"\begin{document}"
26
+ _END_DOCUMENT = r"\end{document}"
27
+
108
28
 
109
- def _rebuild(self):
110
- """Rebuilds the entire TeX template text from ``\\documentclass`` to ``\\end{document}`` according to all settings and choices."""
111
- self.body = (
112
- self.documentclass
113
- + "\n"
114
- + self.preamble
115
- + "\n"
116
- + r"\begin{document}"
117
- + "\n"
118
- + self.post_doc_commands
119
- + "\n"
120
- + self.placeholder_text
121
- + "\n"
122
- + "\n"
123
- + r"\end{document}"
124
- + "\n"
29
+ @dataclass(eq=True)
30
+ class TexTemplate:
31
+ """TeX templates are used to create ``Tex`` and ``MathTex`` objects."""
32
+
33
+ _body: str = field(default="", init=False)
34
+ """A custom body, can be set from a file."""
35
+
36
+ tex_compiler: str = "latex"
37
+ """The TeX compiler to be used, e.g. ``latex``, ``pdflatex`` or ``lualatex``."""
38
+
39
+ description: str = ""
40
+ """A description of the template"""
41
+
42
+ output_format: str = ".dvi"
43
+ """The output format resulting from compilation, e.g. ``.dvi`` or ``.pdf``."""
44
+
45
+ documentclass: str = r"\documentclass[preview]{standalone}"
46
+ r"""The command defining the documentclass, e.g. ``\documentclass[preview]{standalone}``."""
47
+
48
+ preamble: str = _DEFAULT_PREAMBLE
49
+ r"""The document's preamble, i.e. the part between ``\documentclass`` and ``\begin{document}``."""
50
+
51
+ placeholder_text: str = "YourTextHere"
52
+ """Text in the document that will be replaced by the expression to be rendered."""
53
+
54
+ post_doc_commands: str = ""
55
+ r"""Text (definitions, commands) to be inserted at right after ``\begin{document}``, e.g. ``\boldmath``."""
56
+
57
+ @property
58
+ def body(self) -> str:
59
+ """The entire TeX template."""
60
+ return self._body or "\n".join(
61
+ filter(
62
+ None,
63
+ [
64
+ self.documentclass,
65
+ self.preamble,
66
+ _BEGIN_DOCUMENT,
67
+ self.post_doc_commands,
68
+ self.placeholder_text,
69
+ _END_DOCUMENT,
70
+ ],
71
+ )
125
72
  )
126
73
 
127
- def add_to_preamble(self, txt: str, prepend: bool = False):
128
- """Adds stuff to the TeX template's preamble (e.g. definitions, packages). Text can be inserted at the beginning or at the end of the preamble.
74
+ @body.setter
75
+ def body(self, value: str) -> None:
76
+ self._body = value
77
+
78
+ @classmethod
79
+ def from_file(cls, file: StrPath = "tex_template.tex", **kwargs: Any) -> Self:
80
+ """Create an instance by reading the content of a file.
81
+
82
+ Using the ``add_to_preamble`` and ``add_to_document`` methods on this instance
83
+ will have no effect, as the body is read from the file.
84
+ """
85
+ instance = cls(**kwargs)
86
+ instance.body = Path(file).read_text(encoding="utf-8")
87
+ return instance
88
+
89
+ def add_to_preamble(self, txt: str, prepend: bool = False) -> Self:
90
+ r"""Adds text to the TeX template's preamble (e.g. definitions, packages). Text can be inserted at the beginning or at the end of the preamble.
129
91
 
130
92
  Parameters
131
93
  ----------
132
94
  txt
133
- String containing the text to be added, e.g. ``\\usepackage{hyperref}``
95
+ String containing the text to be added, e.g. ``\usepackage{hyperref}``.
134
96
  prepend
135
- Whether the text should be added at the beginning of the preamble, i.e. right after ``\\documentclass``. Default is to add it at the end of the preamble, i.e. right before ``\\begin{document}``
97
+ Whether the text should be added at the beginning of the preamble, i.e. right after ``\documentclass``.
98
+ Default is to add it at the end of the preamble, i.e. right before ``\begin{document}``.
136
99
  """
100
+ if self._body:
101
+ warnings.warn(
102
+ "This TeX template was created with a fixed body, trying to add text the preamble will have no effect.",
103
+ UserWarning,
104
+ stacklevel=2,
105
+ )
137
106
  if prepend:
138
107
  self.preamble = txt + "\n" + self.preamble
139
108
  else:
140
109
  self.preamble += "\n" + txt
141
- self._rebuild()
142
110
  return self
143
111
 
144
- def add_to_document(self, txt: str):
145
- """Adds txt to the TeX template just after \\begin{document}, e.g. ``\\boldmath``
112
+ def add_to_document(self, txt: str) -> Self:
113
+ r"""Adds text to the TeX template just after \begin{document}, e.g. ``\boldmath``.
146
114
 
147
115
  Parameters
148
116
  ----------
149
117
  txt
150
118
  String containing the text to be added.
151
119
  """
152
- self.post_doc_commands += "\n" + txt + "\n"
153
- self._rebuild()
120
+ if self._body:
121
+ warnings.warn(
122
+ "This TeX template was created with a fixed body, trying to add text the document will have no effect.",
123
+ UserWarning,
124
+ stacklevel=2,
125
+ )
126
+ self.post_doc_commands += txt
154
127
  return self
155
128
 
156
- def get_texcode_for_expression(self, expression: str):
157
- """Inserts expression verbatim into TeX template.
129
+ def get_texcode_for_expression(self, expression: str) -> str:
130
+ r"""Inserts expression verbatim into TeX template.
158
131
 
159
132
  Parameters
160
133
  ----------
161
134
  expression
162
- The string containing the expression to be typeset, e.g. ``$\\sqrt{2}$``
135
+ The string containing the expression to be typeset, e.g. ``$\sqrt{2}$``
163
136
 
164
137
  Returns
165
138
  -------
@@ -168,102 +141,59 @@ class TexTemplate:
168
141
  """
169
142
  return self.body.replace(self.placeholder_text, expression)
170
143
 
171
- def _texcode_for_environment(self, environment: str):
172
- """Processes the tex_environment string to return the correct ``\\begin{environment}[extra]{extra}`` and
173
- ``\\end{environment}`` strings
174
-
175
- Parameters
176
- ----------
177
- environment
178
- The tex_environment as a string. Acceptable formats include:
179
- ``{align*}``, ``align*``, ``{tabular}[t]{cccl}``, ``tabular}{cccl``, ``\\begin{tabular}[t]{cccl}``.
180
-
181
- Returns
182
- -------
183
- Tuple[:class:`str`, :class:`str`]
184
- A pair of strings representing the opening and closing of the tex environment, e.g.
185
- ``\\begin{tabular}{cccl}`` and ``\\end{tabular}``
186
- """
187
-
188
- # If the environment starts with \begin, remove it
189
- if environment[0:6] == r"\begin":
190
- environment = environment[6:]
191
-
192
- # If environment begins with { strip it
193
- if environment[0] == r"{":
194
- environment = environment[1:]
195
-
196
- # The \begin command takes everything and closes with a brace
197
- begin = r"\begin{" + environment
198
- if (
199
- begin[-1] != r"}" and begin[-1] != r"]"
200
- ): # If it doesn't end on } or ], assume missing }
201
- begin += r"}"
202
-
203
- # While the \end command terminates at the first closing brace
204
- split_at_brace = re.split(r"}", environment, 1)
205
- end = r"\end{" + split_at_brace[0] + r"}"
206
-
207
- return begin, end
208
-
209
- def get_texcode_for_expression_in_env(self, expression: str, environment: str):
210
- r"""Inserts expression into TeX template wrapped in \begin{environment} and \end{environment}
144
+ def get_texcode_for_expression_in_env(
145
+ self, expression: str, environment: str
146
+ ) -> str:
147
+ r"""Inserts expression into TeX template wrapped in ``\begin{environment}`` and ``\end{environment}``.
211
148
 
212
149
  Parameters
213
150
  ----------
214
151
  expression
215
- The string containing the expression to be typeset, e.g. ``$\\sqrt{2}$``
152
+ The string containing the expression to be typeset, e.g. ``$\sqrt{2}$``.
216
153
  environment
217
- The string containing the environment in which the expression should be typeset, e.g. ``align*``
154
+ The string containing the environment in which the expression should be typeset, e.g. ``align*``.
218
155
 
219
156
  Returns
220
157
  -------
221
158
  :class:`str`
222
159
  LaTeX code based on template, containing the given expression inside its environment, ready for typesetting
223
160
  """
224
- begin, end = self._texcode_for_environment(environment)
225
- return self.body.replace(self.placeholder_text, f"{begin}\n{expression}\n{end}")
161
+ begin, end = _texcode_for_environment(environment)
162
+ return self.body.replace(
163
+ self.placeholder_text, "\n".join([begin, expression, end])
164
+ )
226
165
 
227
- def copy(self) -> TexTemplate:
166
+ def copy(self) -> Self:
167
+ """Create a deep copy of the TeX template instance."""
228
168
  return copy.deepcopy(self)
229
169
 
230
170
 
231
- class TexTemplateFromFile(TexTemplate):
232
- """A TexTemplate object created from a template file (default: tex_template.tex)
171
+ def _texcode_for_environment(environment: str) -> tuple[str, str]:
172
+ r"""Processes the tex_environment string to return the correct ``\begin{environment}[extra]{extra}`` and
173
+ ``\end{environment}`` strings.
233
174
 
234
175
  Parameters
235
176
  ----------
236
- tex_filename
237
- Path to a valid TeX template file
238
- kwargs
239
- Arguments for :class:`~.TexTemplate`.
240
-
241
- Attributes
242
- ----------
243
- template_file : :class:`str`
244
- Path to a valid TeX template file
245
- body : :class:`str`
246
- Content of the TeX template file
247
- tex_compiler : :class:`str`
248
- The TeX compiler to be used, e.g. ``latex``, ``pdflatex`` or ``lualatex``
249
- output_format : :class:`str`
250
- The output format resulting from compilation, e.g. ``.dvi`` or ``.pdf``
177
+ environment
178
+ The tex_environment as a string. Acceptable formats include:
179
+ ``{align*}``, ``align*``, ``{tabular}[t]{cccl}``, ``tabular}{cccl``, ``\begin{tabular}[t]{cccl}``.
180
+
181
+ Returns
182
+ -------
183
+ Tuple[:class:`str`, :class:`str`]
184
+ A pair of strings representing the opening and closing of the tex environment, e.g.
185
+ ``\begin{tabular}{cccl}`` and ``\end{tabular}``
251
186
  """
187
+ environment.removeprefix(r"\begin").removeprefix("{")
252
188
 
253
- def __init__(
254
- self, *, tex_filename: str | os.PathLike = "tex_template.tex", **kwargs
255
- ):
256
- self.template_file = Path(tex_filename)
257
- super().__init__(**kwargs)
258
-
259
- def _rebuild(self):
260
- self.body = self.template_file.read_text()
261
-
262
- def file_not_mutable(self):
263
- raise Exception("Cannot modify TexTemplate when using a template file.")
189
+ # The \begin command takes everything and closes with a brace
190
+ begin = r"\begin{" + environment
191
+ # If it doesn't end on } or ], assume missing }
192
+ if not begin.endswith(("}", "]")):
193
+ begin += "}"
264
194
 
265
- def add_to_preamble(self, txt, prepend=False):
266
- self.file_not_mutable()
195
+ # While the \end command terminates at the first closing brace
196
+ split_at_brace = re.split("}", environment, maxsplit=1)
197
+ end = r"\end{" + split_at_brace[0] + "}"
267
198
 
268
- def add_to_document(self, txt):
269
- self.file_not_mutable()
199
+ return begin, end