Skip to content

Commit 9b02aea

Browse files
markromanmillerleotrsnaveen521kkjsonvillanueva
authored
SVG engine rewrite and tests (#915)
* added many svg test images * Added test for the Rhomboid * Fixed multi-point lists of L type * Two more svg files * Also ignore pycharm-specific project information * Allow skipping of tests with a pytest_skip class attribute * Stubbed a couple tests out before rewrite * Rewrite of SVG path midlayer * refactored old test name * Added tests for two more files * Bugfix for absolute H/V paths * small refactor of cubics * Enabled correct quadratic bezier functionality. * Black formatting * Test the SVG smoothed curves (S and T) * handful more tests * Add all Discord svg tests * Update manim/mobject/types/vectorized_mobject.py Co-authored-by: Leo Torres <[email protected]> * Apply suggestions from code review 1 Co-authored-by: Leo Torres <[email protected]> * Remove files of questionable origin * rolled back review change due to ValueError * Added entries from the SVG Support github project * smaller fixes from review #1 * Enabled fill and stroke. * Troubles with the use of "currentcolor" - for now we wont support it * Minimize visual effect of downstream changes on Tex * Style changes ordered by Black * fixed background opacity handling * enable coloring of circles * Remove periodic table test (requires text) * Update documentation * refactored bare exception catch * Rolling back the pytest_skip functionality * Document some less obvious unit tests * fixing leo's rec * edit to penrose triangle test * Don't copy the list, copy the objects in the list. * black styling * Include defs in tree parsing * Update default svg values for visibility in test. * black formatting * Interpret RGB percentages * mild fix to for btd rebuild. * Force family update for classes that inherit SVGMobjects in order to preserve behavior. * Apply suggestions from code review 2 Co-authored-by: Naveen M K <[email protected]> Co-authored-by: Jason Villanueva <[email protected]> * make static parsing methods into object methods, and add typing * Add typing to use_to_mobject which misled me earlier, and fix documentation merge error * Apply suggestions from code review 3 Co-authored-by: Naveen M K <[email protected]> * black fix * add types for quadratic bezier * prevent fail after matrix transform * Add UK Flag Test * black style * remove DEFAULT SVG STYLE and converted to inline argument. * Remove useless comments. * Refactor svg_mobject.py into multiple files. * documentation and typings * black styling * allow floats in rgb pct specification * parse floats like 2.4.3 into 2.4 0.3, with test * update typing for SUPPORTED_STYLING_ATTRIBUTES Co-authored-by: Naveen M K <[email protected]> * forgot import List from typings Co-authored-by: Leo Torres <[email protected]> Co-authored-by: Naveen M K <[email protected]> Co-authored-by: Jason Villanueva <[email protected]>
1 parent 4237e00 commit 9b02aea

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1340
-461
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ ipython_config.py
8383
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
8484
__pypackages__/
8585

86+
# PyCharm
87+
/.idea/
88+
8689
# Celery stuff
8790
celerybeat-schedule
8891
celerybeat.pid

manim/mobject/svg/style_utils.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
"""Utility functions for parsing SVG styles."""
2+
3+
4+
__all__ = ["cascade_element_style", "parse_style", "parse_color_string"]
5+
6+
from xml.dom.minidom import Element as MinidomElement
7+
from colour import web2hex
8+
from ...utils.color import rgb_to_hex
9+
10+
from typing import Dict, List
11+
12+
SUPPORTED_STYLING_ATTRIBUTES: List[str] = [
13+
"fill",
14+
"stroke",
15+
"style",
16+
"fill-opacity",
17+
"stroke-opacity",
18+
]
19+
20+
21+
def cascade_element_style(
22+
element: MinidomElement, inherited: Dict[str, str]
23+
) -> Dict[str, str]:
24+
"""Collect the element's style attributes based upon both its inheritance and its own attributes.
25+
26+
SVG uses cascading element styles. A closer ancestor's style takes precedence over a more distant ancestor's
27+
style. In order to correctly calculate the styles, the attributes are passed down through the inheritance tree,
28+
updating where necessary.
29+
30+
Note that this method only copies the values and does not parse them. See :meth:`parse_color_string` for converting
31+
from SVG attributes to manim keyword arguments.
32+
33+
Parameters
34+
----------
35+
element : :class:`MinidomElement`
36+
Element of the SVG parse tree
37+
38+
inherited : :class:`dict`
39+
Dictionary of SVG attributes inherited from the parent element.
40+
41+
Returns
42+
-------
43+
:class:`dict`
44+
Dictionary mapping svg attributes to values with `element`'s values overriding inherited values.
45+
"""
46+
47+
style = inherited.copy()
48+
49+
for attr in SUPPORTED_STYLING_ATTRIBUTES:
50+
entry = element.getAttribute(attr)
51+
if entry:
52+
style[attr] = entry
53+
54+
return style
55+
56+
57+
def parse_color_string(color_spec: str) -> str:
58+
"""Handle the SVG-specific color strings and convert them to HTML #rrggbb format.
59+
60+
Parameters
61+
----------
62+
color_spec : :class:`str`
63+
String in any web-compatible format
64+
65+
Returns
66+
-------
67+
:class:`str`
68+
Hexadecimal color string in the format `#rrggbb`
69+
"""
70+
71+
if color_spec[0:3] == "rgb":
72+
# these are only in integer form, but the Colour module wants them in floats.
73+
splits = color_spec[4:-1].split(",")
74+
if splits[0][-1] == "%":
75+
# if the last character of the first number is a percentage,
76+
# then interpret the number as a percentage
77+
parsed_rgbs = [float(i[:-1]) / 100.0 for i in splits]
78+
else:
79+
parsed_rgbs = [int(i) / 255.0 for i in splits]
80+
81+
hex_color = rgb_to_hex(parsed_rgbs)
82+
83+
elif color_spec[0] == "#":
84+
# its OK, parse as hex color standard.
85+
hex_color = color_spec
86+
87+
else:
88+
# attempt to convert color names like "red" to hex color
89+
hex_color = web2hex(color_spec, force_long=True)
90+
91+
return hex_color
92+
93+
94+
def parse_style(svg_style: Dict[str, str]) -> Dict:
95+
"""Convert a dictionary of SVG attributes to Manim VMobject keyword arguments.
96+
97+
Parameters
98+
----------
99+
svg_style : :class:`dict`
100+
Style attributes as a string-to-string dictionary. Keys are valid SVG element attributes (fill, stroke, etc)
101+
102+
Returns
103+
-------
104+
:class:`dict`
105+
Style attributes, but in manim kwargs form, e.g., keys are fill_color, stroke_color
106+
"""
107+
108+
manim_style = {}
109+
110+
# style attributes trump other element-level attributes,
111+
# see https://www.w3.org/TR/SVG11/styling.html section 6.4, search "priority"
112+
# so overwrite the other attribute dictionary values.
113+
if "style" in svg_style:
114+
for style_spec in svg_style["style"].split(";"):
115+
try:
116+
key, value = style_spec.split(":")
117+
except ValueError as e:
118+
if not style_spec:
119+
# there was just a stray semicolon at the end, producing an emptystring
120+
pass
121+
else:
122+
raise e
123+
else:
124+
svg_style[key] = value
125+
126+
if "fill-opacity" in svg_style:
127+
manim_style["fill_opacity"] = float(svg_style["fill-opacity"])
128+
129+
if "stroke-opacity" in svg_style:
130+
manim_style["stroke_opacity"] = float(svg_style["stroke-opacity"])
131+
132+
# nones need to be handled specially
133+
if "fill" in svg_style:
134+
if svg_style["fill"] == "none":
135+
manim_style["fill_opacity"] = 0
136+
else:
137+
manim_style["fill_color"] = parse_color_string(svg_style["fill"])
138+
139+
if "stroke" in svg_style:
140+
if svg_style["stroke"] == "none":
141+
manim_style["stroke_opacity"] = 0
142+
else:
143+
manim_style["stroke_color"] = parse_color_string(svg_style["stroke"])
144+
145+
return manim_style

0 commit comments

Comments
 (0)