diff --git a/manimlib/mobject/geometry.py b/manimlib/mobject/geometry.py index ae54ed7e..1ea875a8 100644 --- a/manimlib/mobject/geometry.py +++ b/manimlib/mobject/geometry.py @@ -208,12 +208,16 @@ class Arc(TipableVMobject): start_angle: float = 0, angle: float = TAU / 4, radius: float = 1.0, - n_components: int = 8, + n_components: Optional[int] = None, arc_center: Vect3 = ORIGIN, **kwargs ): super().__init__(**kwargs) + if n_components is None: + # 16 components for a full circle + n_components = int(15 * (abs(angle) / TAU)) + 1 + self.set_points(quadratic_bezier_points_for_arc(angle, n_components)) self.rotate(start_angle, about_point=ORIGIN) self.scale(radius, about_point=ORIGIN) @@ -597,6 +601,9 @@ class DashedLine(Line): else: return Line.get_end(self) + def get_start_and_end(self) -> Tuple[Vect3, Vect3]: + return self.get_start(), self.get_end() + def get_first_handle(self) -> Vect3: return self.submobjects[0].get_points()[1] diff --git a/manimlib/mobject/numbers.py b/manimlib/mobject/numbers.py index f5c1fc53..7009dee7 100644 --- a/manimlib/mobject/numbers.py +++ b/manimlib/mobject/numbers.py @@ -40,6 +40,7 @@ class DecimalNumber(VMobject): fill_opacity: float = 1.0, fill_border_width: float = 0.5, num_decimal_places: int = 2, + min_total_width: Optional[int] = 0, include_sign: bool = False, group_with_commas: bool = True, digit_buff_per_font_unit: float = 0.001, @@ -54,6 +55,7 @@ class DecimalNumber(VMobject): self.num_decimal_places = num_decimal_places self.include_sign = include_sign self.group_with_commas = group_with_commas + self.min_total_width = min_total_width self.digit_buff_per_font_unit = digit_buff_per_font_unit self.show_ellipsis = show_ellipsis self.unit = unit @@ -167,6 +169,7 @@ class DecimalNumber(VMobject): "include_sign", "group_with_commas", "num_decimal_places", + "min_total_width", ] ]) config.update(kwargs) @@ -176,6 +179,7 @@ class DecimalNumber(VMobject): config.get("field_name", ""), ":", "+" if config["include_sign"] else "", + "0" + str(config.get("min_total_width", "")) if config.get("min_total_width") else "", "," if config["group_with_commas"] else "", f".{ndp}f" if ndp > 0 else "d", "}", diff --git a/manimlib/mobject/shape_matchers.py b/manimlib/mobject/shape_matchers.py index 50cbca98..8faae9c5 100644 --- a/manimlib/mobject/shape_matchers.py +++ b/manimlib/mobject/shape_matchers.py @@ -79,7 +79,8 @@ class BackgroundRectangle(SurroundingRectangle): stroke_width: float | None = None, fill_color: ManimColor | None = None, fill_opacity: float | None = None, - family: bool = True + family: bool = True, + **kwargs ) -> Self: # Unchangeable style, except for fill_opacity VMobject.set_style( diff --git a/manimlib/mobject/svg/brace.py b/manimlib/mobject/svg/brace.py index 4078e687..33e83bf3 100644 --- a/manimlib/mobject/svg/brace.py +++ b/manimlib/mobject/svg/brace.py @@ -6,7 +6,7 @@ import copy import numpy as np from manimlib.constants import DEFAULT_MOBJECT_TO_MOBJECT_BUFF, SMALL_BUFF -from manimlib.constants import DOWN, LEFT, ORIGIN, RIGHT, DL, DR, UL +from manimlib.constants import DOWN, LEFT, ORIGIN, RIGHT, DL, DR, UL, UP from manimlib.constants import PI from manimlib.animation.composition import AnimationGroup from manimlib.animation.fading import FadeIn @@ -174,3 +174,12 @@ class BraceLabel(VMobject): class BraceText(BraceLabel): label_constructor: type = TexText + + +class LineBrace(Brace): + def __init__(self, line: Line, direction=UP, **kwargs): + angle = line.get_angle() + line.rotate(-angle) + super().__init__(line, direction, **kwargs) + line.rotate(angle) + self.rotate(angle, about_point=line.get_center()) diff --git a/manimlib/mobject/svg/tex_mobject.py b/manimlib/mobject/svg/tex_mobject.py index d751ad69..71767f19 100644 --- a/manimlib/mobject/svg/tex_mobject.py +++ b/manimlib/mobject/svg/tex_mobject.py @@ -18,7 +18,7 @@ if TYPE_CHECKING: from manimlib.typing import ManimColor, Span, Selector, Self -SCALE_FACTOR_PER_FONT_POINT = 0.001 +TEX_MOB_SCALE_FACTOR = 0.001 class Tex(StringMobject): @@ -49,7 +49,6 @@ class Tex(StringMobject): if not tex_string.strip(): tex_string = R"\\" - self.font_size = font_size self.tex_string = tex_string self.alignment = alignment self.template = template @@ -64,13 +63,16 @@ class Tex(StringMobject): ) self.set_color_by_tex_to_color_map(self.tex_to_color_map) - self.scale(SCALE_FACTOR_PER_FONT_POINT * font_size) + self.scale(TEX_MOB_SCALE_FACTOR * font_size) + + self.font_size = font_size # Important for this to go after the scale call def get_svg_string_by_content(self, content: str) -> str: return latex_to_svg(content, self.template, self.additional_preamble, short_tex=self.tex_string) def _handle_scale_side_effects(self, scale_factor: float) -> Self: - self.font_size *= scale_factor + if hasattr(self, "font_size"): + self.font_size *= scale_factor return self # Parsing diff --git a/manimlib/mobject/three_dimensions.py b/manimlib/mobject/three_dimensions.py index 32b5bbce..9990c994 100644 --- a/manimlib/mobject/three_dimensions.py +++ b/manimlib/mobject/three_dimensions.py @@ -97,20 +97,27 @@ class Sphere(Surface): v_range: Tuple[float, float] = (0, PI), resolution: Tuple[int, int] = (101, 51), radius: float = 1.0, + true_normals: bool = True, + clockwise=False, **kwargs, ): self.radius = radius + self.clockwise = clockwise super().__init__( u_range=u_range, v_range=v_range, resolution=resolution, **kwargs ) + # Add bespoke normal specification to avoid issue at poles + if true_normals: + self.data['d_normal_point'] = self.data['point'] * ((radius + self.normal_nudge) / radius) def uv_func(self, u: float, v: float) -> np.ndarray: + sign = -1 if self.clockwise else +1 return self.radius * np.array([ - math.cos(u) * math.sin(v), - math.sin(u) * math.sin(v), + math.cos(sign * u) * math.sin(v), + math.sin(sign * u) * math.sin(v), -math.cos(v) ]) diff --git a/manimlib/mobject/types/surface.py b/manimlib/mobject/types/surface.py index 127851b6..914677aa 100644 --- a/manimlib/mobject/types/surface.py +++ b/manimlib/mobject/types/surface.py @@ -30,11 +30,10 @@ class Surface(Mobject): shader_folder: str = "surface" data_dtype: np.dtype = np.dtype([ ('point', np.float32, (3,)), - ('du_point', np.float32, (3,)), - ('dv_point', np.float32, (3,)), + ('d_normal_point', np.float32, (3,)), ('rgba', np.float32, (4,)), ]) - pointlike_data_keys = ['point', 'du_point', 'dv_point'] + pointlike_data_keys = ['point', 'd_normal_point'] def __init__( self, @@ -48,9 +47,11 @@ class Surface(Mobject): # rows/columns of approximating squares resolution: Tuple[int, int] = (101, 101), prefered_creation_axis: int = 1, - # For du and dv steps. Much smaller and numerical error - # can crop up in the shaders. - epsilon: float = 1e-4, + # For du and dv steps. + epsilon: float = 1e-3, + # Step off the surface to a new point which will + # be used to determine the normal direction + normal_nudge: float = 1e-3, **kwargs ): self.u_range = u_range @@ -58,6 +59,7 @@ class Surface(Mobject): self.resolution = resolution self.prefered_creation_axis = prefered_creation_axis self.epsilon = epsilon + self.normal_nudge = normal_nudge super().__init__( **kwargs, @@ -94,9 +96,11 @@ class Surface(Mobject): ).reshape((nu * nv, dim)) for grid in (uv_grid, uv_plus_du, uv_plus_dv) ] + crosses = cross(du_points - points, dv_points - points) + normals = normalize_along_axis(crosses, 1) + self.set_points(points) - self.data['du_point'][:] = du_points - self.data['dv_point'][:] = dv_points + self.data['d_normal_point'] = points + self.normal_nudge * normals def uv_to_point(self, u, v): nu, nv = self.resolution @@ -152,12 +156,8 @@ class Surface(Mobject): return self.triangle_indices def get_unit_normals(self) -> Vect3Array: - points = self.get_points() - crosses = cross( - self.data['du_point'] - points, - self.data['dv_point'] - points, - ) - return normalize_along_axis(crosses, 1) + # TOOD, I could try a more resiliant way to compute this using the neighboring grid values + return normalize_along_axis(self.data['d_normal_point'] - self.data['point'], 1) @Mobject.affects_data def pointwise_become_partial( @@ -276,8 +276,7 @@ class TexturedSurface(Surface): shader_folder: str = "textured_surface" data_dtype: Sequence[Tuple[str, type, Tuple[int]]] = [ ('point', np.float32, (3,)), - ('du_point', np.float32, (3,)), - ('dv_point', np.float32, (3,)), + ('d_normal_point', np.float32, (3,)), ('im_coords', np.float32, (2,)), ('opacity', np.float32, (1,)), ] @@ -321,8 +320,7 @@ class TexturedSurface(Surface): self.resize_points(surf.get_num_points()) self.resolution = surf.resolution self.data['point'][:] = surf.data['point'] - self.data['du_point'][:] = surf.data['du_point'] - self.data['dv_point'][:] = surf.data['dv_point'] + self.data['d_normal_point'][:] = surf.data['d_normal_point'] self.data['opacity'][:, 0] = surf.data["rgba"][:, 3] self.data["im_coords"] = np.array([ [u, v] diff --git a/manimlib/shaders/quadratic_bezier/stroke/geom.glsl b/manimlib/shaders/quadratic_bezier/stroke/geom.glsl index d2ebb98b..fe85b611 100644 --- a/manimlib/shaders/quadratic_bezier/stroke/geom.glsl +++ b/manimlib/shaders/quadratic_bezier/stroke/geom.glsl @@ -33,7 +33,7 @@ const float COS_THRESHOLD = 0.999; // Used to determine how many lines to break the curve into const float POLYLINE_FACTOR = 100; const int MAX_STEPS = 32; -const float MITER_COS_ANGLE_THRESHOLD = -0.9; +const float MITER_COS_ANGLE_THRESHOLD = -0.8; #INSERT emit_gl_Position.glsl #INSERT finalize_color.glsl diff --git a/manimlib/shaders/surface/vert.glsl b/manimlib/shaders/surface/vert.glsl index 8937282a..62303847 100644 --- a/manimlib/shaders/surface/vert.glsl +++ b/manimlib/shaders/surface/vert.glsl @@ -1,8 +1,7 @@ #version 330 in vec3 point; -in vec3 du_point; -in vec3 dv_point; +in vec3 d_normal_point; in vec4 rgba; out vec4 v_color; @@ -15,10 +14,6 @@ const float EPSILON = 1e-10; void main(){ emit_gl_Position(point); - vec3 du = (du_point - point); - vec3 dv = (dv_point - point); - vec3 normal = cross(du, dv); - float mag = length(normal); - vec3 unit_normal = (mag < EPSILON) ? vec3(0, 0, sign(point.z)) : normal / mag; + vec3 unit_normal = normalize(d_normal_point - point); v_color = finalize_color(rgba, point, unit_normal); } \ No newline at end of file diff --git a/manimlib/shaders/textured_surface/vert.glsl b/manimlib/shaders/textured_surface/vert.glsl index 19d9d1e4..91e69b9b 100644 --- a/manimlib/shaders/textured_surface/vert.glsl +++ b/manimlib/shaders/textured_surface/vert.glsl @@ -1,8 +1,7 @@ #version 330 in vec3 point; -in vec3 du_point; -in vec3 dv_point; +in vec3 d_normal_point; in vec2 im_coords; in float opacity; @@ -11,15 +10,17 @@ out vec3 v_unit_normal; out vec2 v_im_coords; out float v_opacity; +uniform float is_sphere; +uniform vec3 center; + #INSERT emit_gl_Position.glsl #INSERT get_unit_normal.glsl +const float EPSILON = 1e-10; + void main(){ v_point = point; - v_unit_normal = normalize(cross( - normalize(du_point - point), - normalize(dv_point - point) - )); + v_unit_normal = normalize(d_normal_point - point);; v_im_coords = im_coords; v_opacity = opacity; emit_gl_Position(point);