From b2084ee68dc0beeaae26abad00eadf6c3212cab6 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Fri, 26 Nov 2021 00:06:32 +0100 Subject: [PATCH 01/14] Make color, stroke_color and fill_color properties --- manim/mobject/mobject.py | 5 +++- manim/mobject/types/vectorized_mobject.py | 28 +++++++++++------------ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 7ea9dbec39..457158b008 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -91,7 +91,6 @@ def __init_subclass__(cls, **kwargs): cls._original__init__ = cls.__init__ def __init__(self, color=WHITE, name=None, dim=3, target=None, z_index=0): - self.color = Color(color) if color else None self.name = self.__class__.__name__ if name is None else name self.dim = dim self.target = target @@ -100,6 +99,10 @@ def __init__(self, color=WHITE, name=None, dim=3, target=None, z_index=0): self.submobjects = [] self.updaters = [] self.updating_suspended = False + + if color: + self.color = Color(color) + self.reset_points() self.generate_points() self.init_colors() diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index 2f1d34a39a..ea6f5c5ff9 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -92,9 +92,7 @@ def __init__( n_points_per_cubic_curve=4, **kwargs, ): - self.fill_color = fill_color self.fill_opacity = fill_opacity - self.stroke_color = stroke_color self.stroke_opacity = stroke_opacity self.stroke_width = stroke_width self.background_stroke_color = background_stroke_color @@ -113,18 +111,23 @@ def __init__( self.n_points_per_cubic_curve = n_points_per_cubic_curve super().__init__(**kwargs) + if fill_color: + self.fill_color = fill_color + if stroke_color: + self.stroke_color = stroke_color + def get_group_class(self): return VGroup # Colors def init_colors(self, propagate_colors=True): self.set_fill( - color=self.fill_color or self.color, + color=self.fill_color, opacity=self.fill_opacity, family=propagate_colors, ) self.set_stroke( - color=self.stroke_color or self.color, + color=self.stroke_color, width=self.stroke_width, opacity=self.stroke_opacity, family=propagate_colors, @@ -239,8 +242,6 @@ def construct(self): self.update_rgbas_array("fill_rgbas", color, opacity) if opacity is not None: self.fill_opacity = opacity - if color is not None: - self.fill_color = color return self def set_stroke( @@ -263,13 +264,12 @@ def set_stroke( array_name = "stroke_rgbas" width_name = "stroke_width" opacity_name = "stroke_opacity" - color_name = "stroke_color" self.update_rgbas_array(array_name, color, opacity) if width is not None: setattr(self, width_name, width) if opacity is not None: setattr(self, opacity_name, opacity) - if color is not None: + if background: setattr(self, color_name, color) return self @@ -357,12 +357,6 @@ def match_style(self, vmobject, family=True): def set_color(self, color, family=True): self.set_fill(color, family=family) self.set_stroke(color, family=family) - - # check if a list of colors is passed to color - if isinstance(color, str): - self.color = colour.Color(color) - else: - self.color = color return self def set_opacity(self, opacity, family=True): @@ -395,6 +389,8 @@ def get_fill_color(self): """ return self.get_fill_colors()[0] + fill_color = property(get_fill_color, set_fill) + def get_fill_opacity(self): """ If there are multiple opacities, this returns the @@ -421,6 +417,8 @@ def get_stroke_rgbas(self, background=False): def get_stroke_color(self, background=False): return self.get_stroke_colors(background)[0] + stroke_color = property(get_stroke_color, set_stroke) + def get_stroke_width(self, background=False): if background: width = self.background_stroke_width @@ -446,6 +444,8 @@ def get_color(self): return self.get_stroke_color() return self.get_fill_color() + color = property(get_color, set_color) + def set_sheen_direction(self, direction: np.ndarray, family=True): """Sets the direction of the applied sheen. From ad829066ecb8c6b3d478a06d8703858d8dc1e0a2 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Fri, 26 Nov 2021 21:49:05 +0100 Subject: [PATCH 02/14] Update code to properly use the stroke- fill- and color properties --- manim/mobject/mobject.py | 4 ++-- manim/mobject/svg/svg_mobject.py | 4 ++-- manim/mobject/svg/text_mobject.py | 4 ++-- manim/mobject/types/opengl_vectorized_mobject.py | 4 ++-- manim/mobject/types/vectorized_mobject.py | 10 ++++++++-- tests/test_stroke.py | 5 +++-- tests/test_svg_mobject.py | 8 ++++---- 7 files changed, 23 insertions(+), 16 deletions(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 457158b008..927412c6e3 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -78,6 +78,7 @@ class Mobject: """ animation_overrides = {} + submobjects = [] @classmethod def __init_subclass__(cls, **kwargs): @@ -100,8 +101,7 @@ def __init__(self, color=WHITE, name=None, dim=3, target=None, z_index=0): self.updaters = [] self.updating_suspended = False - if color: - self.color = Color(color) + self.color = Color(color) if color else None self.reset_points() self.generate_points() diff --git a/manim/mobject/svg/svg_mobject.py b/manim/mobject/svg/svg_mobject.py index 3781cff55c..ccd5a4f5a8 100644 --- a/manim/mobject/svg/svg_mobject.py +++ b/manim/mobject/svg/svg_mobject.py @@ -238,9 +238,9 @@ def generate_style(self): if self.color: style["fill"] = style["stroke"] = self.color.get_hex_l() if self.fill_color: - style["fill"] = self.fill_color + style["fill"] = self.fill_color.hex_l if self.stroke_color: - style["stroke"] = self.stroke_color + style["stroke"] = self.stroke_color.hex_l return style def _path_string_to_mobject(self, path_string: str, style: dict): diff --git a/manim/mobject/svg/text_mobject.py b/manim/mobject/svg/text_mobject.py index 9b0767e1d9..8461996903 100644 --- a/manim/mobject/svg/text_mobject.py +++ b/manim/mobject/svg/text_mobject.py @@ -1100,7 +1100,7 @@ def __init__( **kwargs, ): self.text = text - self.color = color + self.color = Color(color) if color else None self.line_spacing = line_spacing self.font = font self._font_size = float(font_size) @@ -1215,7 +1215,7 @@ def font_size(self, font_val): def _text2hash(self): """Generates ``sha256`` hash for file name.""" settings = ( - "MARKUPPANGO" + self.font + self.slant + self.weight + self.color + "MARKUPPANGO" + self.font + self.slant + self.weight + self.color.hex_l ) # to differentiate from classical Pango Text settings += str(self.line_spacing) + str(self._font_size) settings += str(self.disable_ligatures) diff --git a/manim/mobject/types/opengl_vectorized_mobject.py b/manim/mobject/types/opengl_vectorized_mobject.py index a88929bcc7..63e2338c99 100644 --- a/manim/mobject/types/opengl_vectorized_mobject.py +++ b/manim/mobject/types/opengl_vectorized_mobject.py @@ -95,9 +95,9 @@ def __init__( **kwargs, ): self.data = {} - self.fill_color = fill_color + self.fill_color = Color(fill_color) if fill_color else None self.fill_opacity = fill_opacity - self.stroke_color = stroke_color + self.stroke_color = Color(stroke_color) if stroke_color else None self.stroke_opacity = stroke_opacity self.stroke_width = stroke_width self.draw_stroke_behind_fill = draw_stroke_behind_fill diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index ea6f5c5ff9..6e1e7c6181 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -70,6 +70,8 @@ class VMobject(Mobject): This is within a pixel """ + sheen_factor = 0.0 + def __init__( self, fill_color=None, @@ -399,7 +401,10 @@ def get_fill_opacity(self): return self.get_fill_opacities()[0] def get_fill_colors(self): - return [colour.Color(rgb=rgba[:3]) for rgba in self.get_fill_rgbas()] + return [ + colour.Color(rgb=rgba[:3]) if rgba.any() else None + for rgba in self.get_fill_rgbas() + ] def get_fill_opacities(self): return self.get_fill_rgbas()[:, 3] @@ -433,7 +438,8 @@ def get_stroke_opacity(self, background=False): def get_stroke_colors(self, background=False): return [ - colour.Color(rgb=rgba[:3]) for rgba in self.get_stroke_rgbas(background) + colour.Color(rgb=rgba[:3]) if rgba.any() else None + for rgba in self.get_stroke_rgbas(background) ] def get_stroke_opacities(self, background=False): diff --git a/tests/test_stroke.py b/tests/test_stroke.py index ad6b0edac8..24f9dfab5c 100644 --- a/tests/test_stroke.py +++ b/tests/test_stroke.py @@ -1,4 +1,5 @@ from __future__ import annotations +from colour import Color import manim.utils.color as C from manim import VMobject @@ -6,7 +7,7 @@ def test_stroke_props_in_ctor(): m = VMobject(stroke_color=C.ORANGE, stroke_width=10) - assert m.stroke_color == C.ORANGE + assert m.stroke_color == Color(C.ORANGE) assert m.stroke_width == 10 @@ -15,7 +16,7 @@ def test_set_stroke(): m.set_stroke(color=C.ORANGE, width=2, opacity=0.8) assert m.stroke_width == 2 assert m.stroke_opacity == 0.8 - assert m.stroke_color == C.ORANGE + assert m.stroke_color == Color(C.ORANGE) def test_set_background_stroke(): diff --git a/tests/test_svg_mobject.py b/tests/test_svg_mobject.py index d6fb81adbc..41c6e15dc6 100644 --- a/tests/test_svg_mobject.py +++ b/tests/test_svg_mobject.py @@ -10,13 +10,13 @@ def test_set_fill_color(): expected_color = "#FF862F" svg = SVGMobject(get_svg_resource("heart.svg"), fill_color=expected_color) - assert svg.fill_color == expected_color + assert svg.fill_color == Color(expected_color) def test_set_stroke_color(): expected_color = "#FFFDDD" svg = SVGMobject(get_svg_resource("heart.svg"), stroke_color=expected_color) - assert svg.stroke_color == expected_color + assert svg.stroke_color == Color(expected_color) def test_set_color_sets_fill_and_stroke(): @@ -46,7 +46,7 @@ def test_fill_overrides_color(): color="#123123", fill_color=expected_color, ) - assert svg.fill_color == expected_color + assert svg.fill_color == Color(expected_color) def test_stroke_overrides_color(): @@ -56,7 +56,7 @@ def test_stroke_overrides_color(): color="#334433", stroke_color=expected_color, ) - assert svg.stroke_color == expected_color + assert svg.stroke_color == Color(expected_color) def test_string_to_numbers(): From c83e45f47e806aed991c22e062a82ce499fc90aa Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Fri, 3 Dec 2021 16:06:05 +0100 Subject: [PATCH 03/14] Make colors properties in OpenGL VMobject --- manim/mobject/opengl_mobject.py | 15 +++-- .../types/opengl_vectorized_mobject.py | 40 ++++++------- manim/utils/color.py | 2 +- manim/utils/config_ops.py | 4 +- tests/opengl/test_color_opengl.py | 60 +++++++++---------- 5 files changed, 63 insertions(+), 58 deletions(-) diff --git a/manim/mobject/opengl_mobject.py b/manim/mobject/opengl_mobject.py index 51fab13e82..3104e0b684 100644 --- a/manim/mobject/opengl_mobject.py +++ b/manim/mobject/opengl_mobject.py @@ -96,7 +96,6 @@ def __init__( self.data = getattr(self, "data", {}) self.uniforms = getattr(self, "uniforms", {}) - self.color = Color(color) if color else None self.opacity = opacity self.dim = dim # TODO, get rid of this # Lighting parameters @@ -129,6 +128,7 @@ def __init__( self.init_updaters() # self.init_event_listners() self.init_points() + self.color = Color(color) if color else None self.init_colors() self.shader_indices = None @@ -646,7 +646,7 @@ def assemble_family(self): return self def get_family(self, recurse=True): - if recurse: + if recurse and hasattr(self, "family"): return self.family else: return [self] @@ -1874,13 +1874,18 @@ def set_rgba_array(self, color=None, opacity=None, name="rgbas", recurse=True): # Color only if color is not None and opacity is None: for mob in self.get_family(recurse): - mob.data[name] = resize_array(mob.data[name], len(rgbs)) + mob.data[name] = resize_array( + mob.data[name] if name in mob.data else np.empty((1, 3)), len(rgbs) + ) mob.data[name][:, :3] = rgbs # Opacity only if color is None and opacity is not None: for mob in self.get_family(recurse): - mob.data[name] = resize_array(mob.data[name], len(opacities)) + mob.data[name] = resize_array( + mob.data[name] if name in mob.data else np.empty((1, 3)), + len(opacities), + ) mob.data[name][:, 3] = opacities # Color and opacity @@ -2619,7 +2624,7 @@ def get_shader_vert_indices(self): @property def submobjects(self): - return self._submobjects + return self._submobjects if hasattr(self, "_submobjects") else [] @submobjects.setter def submobjects(self, submobject_list): diff --git a/manim/mobject/types/opengl_vectorized_mobject.py b/manim/mobject/types/opengl_vectorized_mobject.py index 63e2338c99..b6e5c919f1 100644 --- a/manim/mobject/types/opengl_vectorized_mobject.py +++ b/manim/mobject/types/opengl_vectorized_mobject.py @@ -65,6 +65,7 @@ class OpenGLVMobject(OpenGLMobject): stroke_rgba = _Data() stroke_width = _Data() unit_normal = _Data() + data = {} def __init__( self, @@ -94,12 +95,11 @@ def __init__( triangulation_locked: bool = False, **kwargs, ): - self.data = {} - self.fill_color = Color(fill_color) if fill_color else None + self.data = OpenGLVMobject.data + OpenGLVMobject.data = {} self.fill_opacity = fill_opacity - self.stroke_color = Color(stroke_color) if stroke_color else None self.stroke_opacity = stroke_opacity - self.stroke_width = stroke_width + self.stroke_width = [stroke_width] self.draw_stroke_behind_fill = draw_stroke_behind_fill # Indicates that it will not be displayed, but # that it should count in parent mobject's path @@ -129,6 +129,11 @@ def __init__( super().__init__(**kwargs) self.refresh_unit_normal() + if fill_color: + self.fill_color = Color(fill_color) + if stroke_color: + self.stroke_color = Color(stroke_color) + def get_group_class(self): return OpenGLVGroup @@ -197,11 +202,6 @@ def construct(self): -------- :meth:`~.OpenGLVMobject.set_style` """ - if color is not None: - if isinstance(color, str): - self.fill_color = Color(color) - else: - self.fill_color = color if opacity is not None: self.fill_opacity = opacity if recurse: @@ -219,11 +219,6 @@ def set_stroke( background=None, recurse=True, ): - if color is not None: - if isinstance(color, str): - self.stroke_color = Color(color) - else: - self.stroke_color = color if opacity is not None: self.stroke_opacity = opacity if recurse: @@ -309,10 +304,6 @@ def match_style(self, vmobject, recurse=True): return self def set_color(self, color, opacity=None, recurse=True): - if isinstance(color, str): - self.color = Color(color) - else: - self.color = color if opacity is not None: self.opacity = opacity @@ -339,13 +330,13 @@ def fade(self, darkness=0.5, recurse=True): return self def get_fill_colors(self): - return [rgb_to_hex(rgba[:3]) for rgba in self.fill_rgba] + return [Color(rgb_to_hex(rgba[:3])) for rgba in self.fill_rgba] def get_fill_opacities(self): return self.fill_rgba[:, 3] def get_stroke_colors(self): - return [rgb_to_hex(rgba[:3]) for rgba in self.stroke_rgba] + return [Color(rgb_to_hex(rgba[:3])) for rgba in self.stroke_rgba] def get_stroke_opacities(self): return self.stroke_rgba[:, 3] @@ -383,6 +374,15 @@ def get_color(self): return self.get_stroke_color() return self.get_fill_color() + def get_colors(self): + if self.has_stroke(): + return self.get_stroke_colors() + return self.get_fill_colors() + + stroke_color = property(get_stroke_color, set_stroke) + color = property(get_color, set_color) + fill_color = property(get_fill_color, set_fill) + def has_stroke(self): return any(self.get_stroke_widths()) and any(self.get_stroke_opacities()) diff --git a/manim/utils/color.py b/manim/utils/color.py index 0b024c1faa..3842508d24 100644 --- a/manim/utils/color.py +++ b/manim/utils/color.py @@ -473,7 +473,7 @@ def rgba_to_color(rgba: Iterable[float]) -> Color: def rgb_to_hex(rgb: Iterable[float]) -> str: - return "#" + "".join("%02x" % int(255 * x) for x in rgb) + return "#" + "".join("%02x" % round(255 * x) for x in rgb) def hex_to_rgb(hex_code: str) -> np.ndarray: diff --git a/manim/utils/config_ops.py b/manim/utils/config_ops.py index 354cc2e679..86a66a8e61 100644 --- a/manim/utils/config_ops.py +++ b/manim/utils/config_ops.py @@ -58,10 +58,10 @@ def __set_name__(self, obj, name): self.name = name def __get__(self, obj, owner): - return obj.__dict__["data"][self.name] + return obj.data[self.name] if self.name in obj.data else [] def __set__(self, obj, array: np.ndarray): - obj.__dict__["data"][self.name] = array + obj.data[self.name] = array class _Uniforms: diff --git a/tests/opengl/test_color_opengl.py b/tests/opengl/test_color_opengl.py index 61d9273072..3e9e29b80d 100644 --- a/tests/opengl/test_color_opengl.py +++ b/tests/opengl/test_color_opengl.py @@ -102,67 +102,67 @@ def test_set_color_handles_lists_of_strs(using_opengl_renderer): m = OpenGLVMobject() assert m.color.hex == "#fff" m.set_color([BLACK, BLUE, GREEN]) - assert m.color[0] == BLACK - assert m.color[1] == BLUE - assert m.color[2] == GREEN + assert m.get_colors()[0] == Color(BLACK) + assert m.get_colors()[1] == Color(BLUE) + assert m.get_colors()[2] == Color(GREEN) - assert m.fill_color[0] == BLACK - assert m.fill_color[1] == BLUE - assert m.fill_color[2] == GREEN + assert m.get_fill_colors()[0] == Color(BLACK) + assert m.get_fill_colors()[1] == Color(BLUE) + assert m.get_fill_colors()[2] == Color(GREEN) - assert m.stroke_color[0] == BLACK - assert m.stroke_color[1] == BLUE - assert m.stroke_color[2] == GREEN + assert m.get_stroke_colors()[0] == Color(BLACK) + assert m.get_stroke_colors()[1] == Color(BLUE) + assert m.get_stroke_colors()[2] == Color(GREEN) def test_set_color_handles_lists_of_color_objects(using_opengl_renderer): m = OpenGLVMobject() assert m.color.hex == "#fff" m.set_color([Color(PURE_BLUE), Color(PURE_GREEN), Color(PURE_RED)]) - assert m.color[0].hex == "#00f" - assert m.color[1].hex == "#0f0" - assert m.color[2].hex == "#f00" + assert m.get_colors()[0].hex == "#00f" + assert m.get_colors()[1].hex == "#0f0" + assert m.get_colors()[2].hex == "#f00" - assert m.fill_color[0].hex == "#00f" - assert m.fill_color[1].hex == "#0f0" - assert m.fill_color[2].hex == "#f00" + assert m.get_fill_colors()[0].hex == "#00f" + assert m.get_fill_colors()[1].hex == "#0f0" + assert m.get_fill_colors()[2].hex == "#f00" - assert m.stroke_color[0].hex == "#00f" - assert m.stroke_color[1].hex == "#0f0" - assert m.stroke_color[2].hex == "#f00" + assert m.get_stroke_colors()[0].hex == "#00f" + assert m.get_stroke_colors()[1].hex == "#0f0" + assert m.get_stroke_colors()[2].hex == "#f00" def test_set_fill_handles_lists_of_strs(using_opengl_renderer): m = OpenGLVMobject() assert m.fill_color.hex == "#fff" m.set_fill([BLACK, BLUE, GREEN]) - assert m.fill_color[0] == BLACK - assert m.fill_color[1] == BLUE - assert m.fill_color[2] == GREEN + assert m.get_fill_colors()[0] == Color(BLACK) + assert m.get_fill_colors()[1] == Color(BLUE) + assert m.get_fill_colors()[2] == Color(GREEN) def test_set_fill_handles_lists_of_color_objects(using_opengl_renderer): m = OpenGLVMobject() assert m.fill_color.hex == "#fff" m.set_fill([Color(PURE_BLUE), Color(PURE_GREEN), Color(PURE_RED)]) - assert m.fill_color[0].hex == "#00f" - assert m.fill_color[1].hex == "#0f0" - assert m.fill_color[2].hex == "#f00" + assert m.get_fill_colors()[0].hex == "#00f" + assert m.get_fill_colors()[1].hex == "#0f0" + assert m.get_fill_colors()[2].hex == "#f00" def test_set_stroke_handles_lists_of_strs(using_opengl_renderer): m = OpenGLVMobject() assert m.stroke_color.hex == "#fff" m.set_stroke([BLACK, BLUE, GREEN]) - assert m.stroke_color[0] == BLACK - assert m.stroke_color[1] == BLUE - assert m.stroke_color[2] == GREEN + assert m.get_stroke_colors()[0] == Color(BLACK) + assert m.get_stroke_colors()[1] == Color(BLUE) + assert m.get_stroke_colors()[2] == Color(GREEN) def test_set_stroke_handles_lists_of_color_objects(using_opengl_renderer): m = OpenGLVMobject() assert m.stroke_color.hex == "#fff" m.set_stroke([Color(PURE_BLUE), Color(PURE_GREEN), Color(PURE_RED)]) - assert m.stroke_color[0].hex == "#00f" - assert m.stroke_color[1].hex == "#0f0" - assert m.stroke_color[2].hex == "#f00" + assert m.get_stroke_colors()[0].hex == "#00f" + assert m.get_stroke_colors()[1].hex == "#0f0" + assert m.get_stroke_colors()[2].hex == "#f00" From 290dac644782e320838897d2610340b87f66a04c Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Fri, 3 Dec 2021 18:57:34 +0100 Subject: [PATCH 04/14] Initialize svg mobjects with correct style --- manim/mobject/svg/svg_mobject.py | 50 ++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/manim/mobject/svg/svg_mobject.py b/manim/mobject/svg/svg_mobject.py index ccd5a4f5a8..318e6dae1f 100644 --- a/manim/mobject/svg/svg_mobject.py +++ b/manim/mobject/svg/svg_mobject.py @@ -9,11 +9,12 @@ import re import string import warnings -from typing import Dict, List +from typing import Dict, List, Optional from xml.dom.minidom import Element as MinidomElement from xml.dom.minidom import parse as minidom_parse import numpy as np +from colour import Color from ... import config, logger from ...constants import * @@ -76,6 +77,10 @@ def __init__( should_subdivide_sharp_curves=False, should_remove_null_curves=False, color=None, + *, + fill_color=None, + stroke_color=None, + stroke_opacity=1.0, **kwargs, ): self.def_map = {} @@ -91,8 +96,21 @@ def __init__( if config.renderer == "opengl" else {} ) + self._initial_svg_style = self.generate_style( + Color(color) if color else None, + Color(fill_color) if fill_color else None, + Color(stroke_color) if stroke_color else None, + fill_opacity, + stroke_opacity, + ) super().__init__( - color=color, fill_opacity=fill_opacity, stroke_width=stroke_width, **kwargs + color=color, + fill_opacity=fill_opacity, + stroke_width=stroke_width, + fill_color=fill_color, + stroke_opacity=stroke_opacity, + stroke_color=stroke_color, + **kwargs, ) self._move_into_position(width, height) @@ -136,7 +154,7 @@ def generate_points(self): for node in doc.childNodes: if not isinstance(node, MinidomElement) or node.tagName != "svg": continue - mobjects = self._get_mobjects_from(node, self.generate_style()) + mobjects = self._get_mobjects_from(node, self._initial_svg_style) if self.unpack_groups: self.add(*mobjects) else: @@ -230,17 +248,25 @@ def _get_mobjects_from( return result - def generate_style(self): + def generate_style( + self, + color: Optional[Color], + fill_color: Optional[Color], + stroke_color: Optional[Color], + fill_opacity: float, + stroke_opacity: float, + ): style = { - "fill-opacity": self.fill_opacity, - "stroke-opacity": self.stroke_opacity, + "fill-opacity": fill_opacity, + "stroke-opacity": stroke_opacity, } - if self.color: - style["fill"] = style["stroke"] = self.color.get_hex_l() - if self.fill_color: - style["fill"] = self.fill_color.hex_l - if self.stroke_color: - style["stroke"] = self.stroke_color.hex_l + if color: + style["fill"] = style["stroke"] = color.get_hex_l() + if fill_color: + style["fill"] = fill_color.hex_l + if stroke_color: + style["stroke"] = stroke_color.hex_l + return style def _path_string_to_mobject(self, path_string: str, style: dict): From 09ec113f6e83b9e1ef62cb4dae2096b01066b5c3 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Wed, 22 Dec 2021 22:00:37 +0100 Subject: [PATCH 05/14] Pass the color to hash calculating function directly --- manim/mobject/svg/text_mobject.py | 13 ++++++------- manim/mobject/types/opengl_vectorized_mobject.py | 4 +--- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/manim/mobject/svg/text_mobject.py b/manim/mobject/svg/text_mobject.py index 8461996903..99afe0b84d 100644 --- a/manim/mobject/svg/text_mobject.py +++ b/manim/mobject/svg/text_mobject.py @@ -1100,7 +1100,6 @@ def __init__( **kwargs, ): self.text = text - self.color = Color(color) if color else None self.line_spacing = line_spacing self.font = font self._font_size = float(font_size) @@ -1133,7 +1132,7 @@ def __init__( else: self.line_spacing = self._font_size + self._font_size * self.line_spacing - file_name = self._text2svg() + file_name = self._text2svg(Color(color) if color else None) PangoUtils.remove_last_M(file_name) super().__init__( file_name, @@ -1212,10 +1211,10 @@ def font_size(self, font_val): else: self.scale(font_val / self.font_size) - def _text2hash(self): + def _text2hash(self, color: Color): """Generates ``sha256`` hash for file name.""" settings = ( - "MARKUPPANGO" + self.font + self.slant + self.weight + self.color.hex_l + "MARKUPPANGO" + self.font + self.slant + self.weight + color.hex_l ) # to differentiate from classical Pango Text settings += str(self.line_spacing) + str(self._font_size) settings += str(self.disable_ligatures) @@ -1225,7 +1224,7 @@ def _text2hash(self): hasher.update(id_str.encode()) return hasher.hexdigest()[:16] - def _text2svg(self): + def _text2svg(self, color: Color): """Convert the text to SVG using Pango.""" size = self._font_size line_spacing = self.line_spacing @@ -1235,14 +1234,14 @@ def _text2svg(self): dir_name = config.get_dir("text_dir") if not os.path.exists(dir_name): os.makedirs(dir_name) - hash_name = self._text2hash() + hash_name = self._text2hash(color) file_name = os.path.join(dir_name, hash_name) + ".svg" if os.path.exists(file_name): svg_file = file_name else: logger.debug(f"Setting Text {self.text}") svg_file = MarkupUtils.text2svg( - f'{self.text}', + f'{self.text}', self.font, self.slant, self.weight, diff --git a/manim/mobject/types/opengl_vectorized_mobject.py b/manim/mobject/types/opengl_vectorized_mobject.py index b6e5c919f1..00324efc35 100644 --- a/manim/mobject/types/opengl_vectorized_mobject.py +++ b/manim/mobject/types/opengl_vectorized_mobject.py @@ -65,7 +65,6 @@ class OpenGLVMobject(OpenGLMobject): stroke_rgba = _Data() stroke_width = _Data() unit_normal = _Data() - data = {} def __init__( self, @@ -95,8 +94,7 @@ def __init__( triangulation_locked: bool = False, **kwargs, ): - self.data = OpenGLVMobject.data - OpenGLVMobject.data = {} + self.data = {} self.fill_opacity = fill_opacity self.stroke_opacity = stroke_opacity self.stroke_width = [stroke_width] From a1475288cfec9e32005ff0fe98237518c5c2436f Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sun, 26 Dec 2021 11:11:51 +0100 Subject: [PATCH 06/14] Remove workaround with submobjects --- manim/mobject/geometry.py | 2 +- manim/mobject/mobject.py | 1 - manim/mobject/shape_matchers.py | 7 +++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/manim/mobject/geometry.py b/manim/mobject/geometry.py index f0f7d07690..db31f8c689 100644 --- a/manim/mobject/geometry.py +++ b/manim/mobject/geometry.py @@ -2558,8 +2558,8 @@ def construct(self): """ def __init__(self, corner_radius=0.5, **kwargs): - self.corner_radius = corner_radius super().__init__(**kwargs) + self.corner_radius = corner_radius self.round_corners(self.corner_radius) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 927412c6e3..8de334186c 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -78,7 +78,6 @@ class Mobject: """ animation_overrides = {} - submobjects = [] @classmethod def __init_subclass__(cls, **kwargs): diff --git a/manim/mobject/shape_matchers.py b/manim/mobject/shape_matchers.py index c17f2b9783..4e8be84bfb 100644 --- a/manim/mobject/shape_matchers.py +++ b/manim/mobject/shape_matchers.py @@ -44,15 +44,14 @@ def construct(self): def __init__( self, mobject, color=YELLOW, buff=SMALL_BUFF, corner_radius=0.0, **kwargs ): - self.color = color - self.buff = buff super().__init__( color=color, - width=mobject.width + 2 * self.buff, - height=mobject.height + 2 * self.buff, + width=mobject.width + 2 * buff, + height=mobject.height + 2 * buff, corner_radius=corner_radius, **kwargs ) + self.buff = buff self.move_to(mobject) From a705845af29210df045bcbc7b3ceb07cc59b2ee8 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sun, 26 Dec 2021 11:19:01 +0100 Subject: [PATCH 07/14] Make text2svg handling of None color explicit --- manim/mobject/svg/text_mobject.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/manim/mobject/svg/text_mobject.py b/manim/mobject/svg/text_mobject.py index 99afe0b84d..3e8794b3b9 100644 --- a/manim/mobject/svg/text_mobject.py +++ b/manim/mobject/svg/text_mobject.py @@ -1224,7 +1224,7 @@ def _text2hash(self, color: Color): hasher.update(id_str.encode()) return hasher.hexdigest()[:16] - def _text2svg(self, color: Color): + def _text2svg(self, color: Optional[Color]): """Convert the text to SVG using Pango.""" size = self._font_size line_spacing = self.line_spacing @@ -1239,9 +1239,14 @@ def _text2svg(self, color: Color): if os.path.exists(file_name): svg_file = file_name else: + final_text = ( + f'{self.text}' + if color is not None + else self.text + ) logger.debug(f"Setting Text {self.text}") svg_file = MarkupUtils.text2svg( - f'{self.text}', + final_text, self.font, self.slant, self.weight, From e38d30c1466e83041f711b1ff4510d458a28937d Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sun, 26 Dec 2021 11:54:08 +0100 Subject: [PATCH 08/14] Fix background_stroke_color updating --- manim/mobject/types/vectorized_mobject.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index 6e1e7c6181..51d13d8b72 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -261,7 +261,6 @@ def set_stroke( array_name = "background_stroke_rgbas" width_name = "background_stroke_width" opacity_name = "background_stroke_opacity" - color_name = "background_stroke_color" else: array_name = "stroke_rgbas" width_name = "stroke_width" @@ -271,8 +270,8 @@ def set_stroke( setattr(self, width_name, width) if opacity is not None: setattr(self, opacity_name, opacity) - if background: - setattr(self, color_name, color) + if color is not None and background: + self.background_stroke_color = color return self def set_background_stroke(self, **kwargs): From b388aa58114cb880bd93d1749306d49339572ffb Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sun, 26 Dec 2021 11:56:53 +0100 Subject: [PATCH 09/14] Remove in data check --- manim/utils/config_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/utils/config_ops.py b/manim/utils/config_ops.py index 86a66a8e61..f67c543565 100644 --- a/manim/utils/config_ops.py +++ b/manim/utils/config_ops.py @@ -58,7 +58,7 @@ def __set_name__(self, obj, name): self.name = name def __get__(self, obj, owner): - return obj.data[self.name] if self.name in obj.data else [] + return obj.data[self.name] def __set__(self, obj, array: np.ndarray): obj.data[self.name] = array From 9128e56c65d0b12fd34968e579e5498055ee4df2 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sun, 26 Dec 2021 12:02:41 +0100 Subject: [PATCH 10/14] Remove typo ? --- manim/mobject/types/opengl_vectorized_mobject.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/mobject/types/opengl_vectorized_mobject.py b/manim/mobject/types/opengl_vectorized_mobject.py index 00324efc35..550da8cd2b 100644 --- a/manim/mobject/types/opengl_vectorized_mobject.py +++ b/manim/mobject/types/opengl_vectorized_mobject.py @@ -97,7 +97,7 @@ def __init__( self.data = {} self.fill_opacity = fill_opacity self.stroke_opacity = stroke_opacity - self.stroke_width = [stroke_width] + self.stroke_width = stroke_width self.draw_stroke_behind_fill = draw_stroke_behind_fill # Indicates that it will not be displayed, but # that it should count in parent mobject's path From 7a88f25e6dfeff3d93cf32862ae3093c3afd8075 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sun, 26 Dec 2021 12:06:20 +0100 Subject: [PATCH 11/14] Remove odd spacing --- manim/mobject/mobject.py | 1 - 1 file changed, 1 deletion(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 8de334186c..226400d397 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -99,7 +99,6 @@ def __init__(self, color=WHITE, name=None, dim=3, target=None, z_index=0): self.submobjects = [] self.updaters = [] self.updating_suspended = False - self.color = Color(color) if color else None self.reset_points() From aeca1fa0de434d3564bf1285b6492c5654186d24 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Fri, 28 Jan 2022 11:39:44 +0100 Subject: [PATCH 12/14] Fix issues with new text color settings --- manim/mobject/svg/text_mobject.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/manim/mobject/svg/text_mobject.py b/manim/mobject/svg/text_mobject.py index 3e8794b3b9..ba95d35822 100644 --- a/manim/mobject/svg/text_mobject.py +++ b/manim/mobject/svg/text_mobject.py @@ -427,7 +427,6 @@ def __init__( disable_ligatures: bool = False, **kwargs, ): - self.color = color self.line_spacing = line_spacing self.font = font self._font_size = float(font_size) @@ -471,7 +470,7 @@ def __init__( ) else: self.line_spacing = self._font_size + self._font_size * self.line_spacing - file_name = self._text2svg() + file_name = self._text2svg(color) PangoUtils.remove_last_M(file_name) super().__init__( file_name, @@ -594,10 +593,10 @@ def _set_color_by_t2g(self, t2g=None): for start, end in self._find_indexes(word, self.text): self.chars[start:end].set_color_by_gradient(*gradient) - def _text2hash(self): + def _text2hash(self, color: Color): """Generates ``sha256`` hash for file name.""" settings = ( - "PANGO" + self.font + self.slant + self.weight + self.color + "PANGO" + self.font + self.slant + self.weight + color ) # to differentiate Text and CairoText settings += str(self.t2f) + str(self.t2s) + str(self.t2w) + str(self.t2c) settings += str(self.line_spacing) + str(self._font_size) @@ -671,7 +670,7 @@ def _get_settings_from_gradient( settings.append(TextSetting(i, i + 1, **args)) return settings - def _text2settings(self): + def _text2settings(self, color: Color): """Converts the texts and styles to a setting for parsing.""" t2xs = [ (self.t2f, "font"), @@ -679,7 +678,9 @@ def _text2settings(self): (self.t2w, "weight"), (self.t2c, "color"), ] - setting_args = {arg: getattr(self, arg) for _, arg in t2xs} + setting_args = { + arg: getattr(self, arg) if arg != "color" else color for _, arg in t2xs + } settings = self._get_settings_from_t2xs(t2xs) settings.extend(self._get_settings_from_gradient(setting_args)) @@ -733,7 +734,7 @@ def _text2settings(self): setting.line_num = 0 return settings - def _text2svg(self): + def _text2svg(self, color: Color): """Convert the text to SVG using Pango.""" size = self._font_size line_spacing = self.line_spacing @@ -743,13 +744,13 @@ def _text2svg(self): dir_name = config.get_dir("text_dir") if not os.path.exists(dir_name): os.makedirs(dir_name) - hash_name = self._text2hash() + hash_name = self._text2hash(color) file_name = os.path.join(dir_name, hash_name) + ".svg" if os.path.exists(file_name): svg_file = file_name else: - settings = self._text2settings() + settings = self._text2settings(color) width = config["pixel_width"] height = config["pixel_height"] From 7b956a2c04b63b365caf263eced8512ebe0cdbba Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 28 Jan 2022 10:40:46 +0000 Subject: [PATCH 13/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- manim/mobject/svg/svg_mobject.py | 6 +++--- tests/test_stroke.py | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/manim/mobject/svg/svg_mobject.py b/manim/mobject/svg/svg_mobject.py index 318e6dae1f..98bb46964b 100644 --- a/manim/mobject/svg/svg_mobject.py +++ b/manim/mobject/svg/svg_mobject.py @@ -250,9 +250,9 @@ def _get_mobjects_from( def generate_style( self, - color: Optional[Color], - fill_color: Optional[Color], - stroke_color: Optional[Color], + color: Color | None, + fill_color: Color | None, + stroke_color: Color | None, fill_opacity: float, stroke_opacity: float, ): diff --git a/tests/test_stroke.py b/tests/test_stroke.py index 24f9dfab5c..5a25f34661 100644 --- a/tests/test_stroke.py +++ b/tests/test_stroke.py @@ -1,4 +1,5 @@ from __future__ import annotations + from colour import Color import manim.utils.color as C From 035ae57f84c312a7b11d61340fee4af1dec32aa5 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Fri, 28 Jan 2022 20:04:52 +0100 Subject: [PATCH 14/14] Implement suggestions --- manim/mobject/svg/svg_mobject.py | 1 - manim/mobject/svg/text_mobject.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/manim/mobject/svg/svg_mobject.py b/manim/mobject/svg/svg_mobject.py index 98bb46964b..430eef120f 100644 --- a/manim/mobject/svg/svg_mobject.py +++ b/manim/mobject/svg/svg_mobject.py @@ -9,7 +9,6 @@ import re import string import warnings -from typing import Dict, List, Optional from xml.dom.minidom import Element as MinidomElement from xml.dom.minidom import parse as minidom_parse diff --git a/manim/mobject/svg/text_mobject.py b/manim/mobject/svg/text_mobject.py index ba95d35822..ea4e87ad3f 100644 --- a/manim/mobject/svg/text_mobject.py +++ b/manim/mobject/svg/text_mobject.py @@ -1225,7 +1225,7 @@ def _text2hash(self, color: Color): hasher.update(id_str.encode()) return hasher.hexdigest()[:16] - def _text2svg(self, color: Optional[Color]): + def _text2svg(self, color: Color | None): """Convert the text to SVG using Pango.""" size = self._font_size line_spacing = self.line_spacing