Skip to content

Fix Write() stroke width to scale with font_size or object scale #4222

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions manim/animation/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def construct(self):
from manim.mobject.text.text_mobject import Text
from manim.scene.scene import Scene

from manim.constants import RIGHT, TAU
from manim.constants import DEFAULT_FONT_SIZE, DEFAULT_STROKE_WIDTH, RIGHT, TAU
from manim.mobject.opengl.opengl_surface import OpenGLSurface
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
from manim.utils.color import ManimColor
Expand All @@ -94,6 +94,7 @@ def construct(self):
from ..animation.animation import Animation
from ..animation.composition import Succession
from ..mobject.mobject import Group, Mobject
from ..mobject.svg.svg_mobject import SVGMobject
from ..mobject.types.vectorized_mobject import VMobject
from ..utils.bezier import integer_interpolate
from ..utils.rate_functions import double_smooth, linear
Expand Down Expand Up @@ -293,7 +294,23 @@ def interpolate_submobject(


class Write(DrawBorderThenFill):
"""Simulate hand-writing a :class:`~.Text` or hand-drawing a :class:`~.VMobject`.
"""Simulate hand-writing a text-based mobject, such as :class:`~.Text`
and :class:`~.Tex`, or simulate hand-drawing a :class:`~.VMobject`.

Parameters
----------
vmobject
The VMobject to animate.
stroke_width
Stroke width for drawing. If not provided, it is scaled based on font size
for text-based mobjects when the font is very small or very large;
otherwise defaults to 2.0.
rate_func
The function defining the animation progress.
reverse
If ``True``, plays the animation in reverse.
**kwargs
Additional arguments passed to the parent class :class:`~.DrawBorderThenFill`.

Examples
--------
Expand Down Expand Up @@ -322,6 +339,7 @@ def construct(self):
def __init__(
self,
vmobject: VMobject | OpenGLVMobject,
stroke_width: float | None = None,
rate_func: Callable[[float], float] = linear,
reverse: bool = False,
**kwargs,
Expand All @@ -333,11 +351,13 @@ def __init__(
run_time,
lag_ratio,
)
stroke_width = self._adjust_stroke_width_for_text(vmobject, stroke_width)
self.reverse = reverse
if "remover" not in kwargs:
kwargs["remover"] = reverse
super().__init__(
vmobject,
stroke_width=stroke_width,
rate_func=rate_func,
run_time=run_time,
lag_ratio=lag_ratio,
Expand All @@ -358,6 +378,23 @@ def _set_default_config_from_length(
lag_ratio = min(4.0 / max(1.0, length), 0.2)
return run_time, lag_ratio

def _adjust_stroke_width_for_text(
self,
vmobject: VMobject | OpenGLVMobject,
stroke_width: float | None,
scale_factor: float = 0.25,
) -> float:
if stroke_width is not None:
return stroke_width
if not isinstance(vmobject, SVGMobject) or vmobject.height == 0:
return 2
font_size = getattr(vmobject, "font_size", DEFAULT_FONT_SIZE)
if font_size < 20 or font_size > 6 * DEFAULT_FONT_SIZE:
# adjust stroke_width if font_size is too small or too big
return (font_size / DEFAULT_FONT_SIZE) * DEFAULT_STROKE_WIDTH * scale_factor
else:
return 2

def reverse_submobjects(self) -> None:
self.mobject.invert(recursive=True)

Expand Down