Skip to content

Commit cdaf501

Browse files
AntonBallmaierkilacoda-oldbehackl
authored
Change Brace from Tex to SVG (#1258) (#1260)
* test * revert test changes * Rework Brace to use SVG (#1258) * change import order * make import relative * add graphics unit tests * black tests * Try to fix flake problem Co-authored-by: kilacoda <[email protected]> Co-authored-by: Benjamin Hackl <[email protected]>
1 parent 3e8615d commit cdaf501

File tree

4 files changed

+65
-20
lines changed

4 files changed

+65
-20
lines changed

manim/mobject/svg/brace.py

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
from ...animation.growing import GrowFromCenter
1111
from ...constants import *
1212
from ...mobject.geometry import Line
13+
from ...mobject.svg.svg_path import SVGPathMobject
1314
from ...mobject.svg.tex_mobject import MathTex, Tex
1415
from ...mobject.types.vectorized_mobject import VMobject
16+
from ...utils.color import BLACK
1517
from ...utils.space_ops import get_norm
1618

1719

18-
class Brace(MathTex):
20+
class Brace(SVGPathMobject):
1921
"""Takes a mobject and draws a brace adjacent to it.
2022
2123
Passing a direction vector determines the direction from which the
@@ -51,35 +53,43 @@ def __init__(
5153
mobject,
5254
direction=DOWN,
5355
buff=0.2,
54-
width_multiplier=2,
55-
max_num_quads=15,
56-
min_num_quads=0,
56+
sharpness=2,
57+
stroke_width=0,
58+
fill_opacity=1.0,
5759
background_stroke_width=0,
60+
background_stroke_color=BLACK,
5861
**kwargs
5962
):
60-
self.width_multiplier = width_multiplier
61-
self.max_num_quads = max_num_quads
62-
self.min_num_quads = min_num_quads
63+
path_string_template = "m0.01216 0c-0.01152 0-0.01216 6.103e-4 -0.01216 0.01311v0.007762c0.06776 0.122 0.1799 0.1455 0.2307 0.1455h{0}c0.03046 3.899e-4 0.07964 0.00449 0.1246 0.02636 0.0537 0.02695 0.07418 0.05816 0.08648 0.07769 0.001562 0.002538 0.004539 0.002563 0.01098 0.002563 0.006444-2e-8 0.009421-2.47e-5 0.01098-0.002563 0.0123-0.01953 0.03278-0.05074 0.08648-0.07769 0.04491-0.02187 0.09409-0.02597 0.1246-0.02636h{0}c0.05077 0 0.1629-0.02346 0.2307-0.1455v-0.007762c-1.78e-6 -0.0125-6.365e-4 -0.01311-0.01216-0.01311-0.006444-3.919e-8 -0.009348 2.448e-5 -0.01091 0.002563-0.0123 0.01953-0.03278 0.05074-0.08648 0.07769-0.04491 0.02187-0.09416 0.02597-0.1246 0.02636h{1}c-0.04786 0-0.1502 0.02094-0.2185 0.1256-0.06833-0.1046-0.1706-0.1256-0.2185-0.1256h{1}c-0.03046-3.899e-4 -0.07972-0.004491-0.1246-0.02636-0.0537-0.02695-0.07418-0.05816-0.08648-0.07769-0.001562-0.002538-0.004467-0.002563-0.01091-0.002563z"
64+
default_min_width = 0.90552
65+
6366
self.buff = buff
67+
6468
angle = -np.arctan2(*direction[:2]) + np.pi
6569
mobject.rotate(-angle, about_point=ORIGIN)
6670
left = mobject.get_corner(DOWN + LEFT)
6771
right = mobject.get_corner(DOWN + RIGHT)
6872
target_width = right[0] - left[0]
73+
linear_section_length = max(
74+
0, (target_width * sharpness - default_min_width) / 2
75+
)
6976

70-
# Adding int(target_width) qquads gives approximately the right width
71-
num_quads = np.clip(
72-
int(self.width_multiplier * target_width),
73-
self.min_num_quads,
74-
self.max_num_quads,
77+
path = path_string_template.format(
78+
linear_section_length, -linear_section_length
7579
)
76-
tex_string = "\\underbrace{%s}" % (num_quads * "\\qquad")
77-
MathTex.__init__(
78-
self, tex_string, background_stroke_width=background_stroke_width, **kwargs
80+
81+
SVGPathMobject.__init__(
82+
self,
83+
path_string=path,
84+
stroke_width=stroke_width,
85+
fill_opacity=fill_opacity,
86+
background_stroke_width=background_stroke_width,
87+
background_stroke_color=background_stroke_color,
88+
**kwargs
7989
)
80-
self.tip_point_index = np.argmin(self.get_all_points()[:, 1])
8190
self.stretch_to_fit_width(target_width)
8291
self.shift(left - self.get_corner(UP + LEFT) + self.buff * DOWN)
92+
8393
for mob in mobject, self:
8494
mob.rotate(angle, about_point=ORIGIN)
8595

@@ -104,10 +114,8 @@ def get_tex(self, *tex, **kwargs):
104114
return tex_mob
105115

106116
def get_tip(self):
107-
# Very specific to the LaTeX representation
108-
# of a brace, but it's the only way I can think
109-
# of to get the tip regardless of orientation.
110-
return self.get_all_points()[self.tip_point_index]
117+
# Returns the position of the seventh point in the path, which is the tip.
118+
return self.points[28] # = 7*4
111119

112120
def get_direction(self):
113121
vect = self.get_tip() - self.get_center()
Binary file not shown.
Binary file not shown.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import pytest
2+
3+
from manim import *
4+
5+
from ..utils.GraphicalUnitTester import GraphicalUnitTester
6+
from ..utils.testing_utils import get_scenes_to_test
7+
8+
9+
class BraceSharpnessTest(Scene):
10+
def construct(self):
11+
line = Line(LEFT * 3, RIGHT * 3).shift(UP * 4)
12+
13+
for sharpness in [0, 0.25, 0.5, 0.75, 1, 2, 3, 5]:
14+
self.add(Brace(line, sharpness=sharpness))
15+
line.shift(DOWN)
16+
self.wait()
17+
18+
19+
class BraceTipTest(Scene):
20+
def construct(self):
21+
line = Line().shift(LEFT * 3).rotate(PI / 2)
22+
steps = 8
23+
for i in range(steps):
24+
brace = Brace(line, direction=line.copy().rotate(PI / 2).get_unit_vector())
25+
dot = Dot()
26+
brace.put_at_tip(dot)
27+
line.rotate_about_origin(TAU / steps)
28+
self.add(brace, dot)
29+
self.wait()
30+
31+
32+
MODULE_NAME = "brace"
33+
34+
35+
@pytest.mark.parametrize("scene_to_test", get_scenes_to_test(__name__), indirect=False)
36+
def test_scene(scene_to_test, tmpdir, show_diff):
37+
GraphicalUnitTester(scene_to_test[1], MODULE_NAME, tmpdir).test(show_diff=show_diff)

0 commit comments

Comments
 (0)