Skip to content

Commit 601d328

Browse files
author
André
authored
Fixed error when processing SVG with omitted elliptical arc command (#1574)
* fixed arc implementation for omitted command * added double elliptical arc test * fixed incorrect startpoint when processing elliptical arcs * added heart.svg test * fixed elliptical arc processing error * added elliptical arc test * merging from elliptical_arc_fix
1 parent e882b18 commit 601d328

File tree

4 files changed

+45
-14
lines changed

4 files changed

+45
-14
lines changed

manim/mobject/svg/svg_path.py

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ def string_to_numbers(num_string: str) -> List[float]:
211211
return float_results
212212

213213

214+
def grouped(iterable, n):
215+
"""Group iterable into arrays of n items."""
216+
return (np.array(v) for v in zip(*[iter(iterable)] * n))
217+
218+
214219
class SVGPathMobject(VMobject, metaclass=ConvertToOpenGL):
215220
def __init__(self, path_string, **kwargs):
216221
self.path_string = path_string
@@ -380,21 +385,37 @@ def string_to_points(self, command, is_relative, coord_string, start_point):
380385

381386
# arcs are weirdest, handle them first.
382387
if command == "A":
383-
# We have to handle offsets here because ellipses are complicated.
384-
if is_relative:
385-
numbers[5] += start_point[0]
386-
numbers[6] += start_point[1]
387-
388-
# If the endpoints (x1, y1) and (x2, y2) are identical, then this
389-
# is equivalent to omitting the elliptical arc segment entirely.
390-
# for more information of where this math came from visit:
391-
# http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
392-
if start_point[0] == numbers[5] and start_point[1] == numbers[6]:
393-
return
388+
result = np.zeros((0, self.dim))
389+
last_end_point = None
390+
for elliptic_numbers in grouped(numbers, 7):
391+
# The startpoint changes with each iteration.
392+
if last_end_point is not None:
393+
start_point = last_end_point
394+
395+
# We have to handle offsets here because ellipses are complicated.
396+
if is_relative:
397+
elliptic_numbers[5] += start_point[0]
398+
elliptic_numbers[6] += start_point[1]
399+
400+
# If the endpoints (x1, y1) and (x2, y2) are identical, then this
401+
# is equivalent to omitting the elliptical arc segment entirely.
402+
# for more information of where this math came from visit:
403+
# http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
404+
if (
405+
start_point[0] == elliptic_numbers[5]
406+
and start_point[1] == elliptic_numbers[6]
407+
):
408+
continue
409+
410+
result = np.append(
411+
result,
412+
elliptical_arc_to_cubic_bezier(*start_point[:2], *elliptic_numbers),
413+
axis=0,
414+
)
394415

395-
result = np.array(
396-
elliptical_arc_to_cubic_bezier(*start_point[:2], *numbers)
397-
)
416+
# We store the endpoint so that it can be the startpoint for the
417+
# next iteration.
418+
last_end_point = elliptic_numbers[5:]
398419

399420
return result
400421

Binary file not shown.
Lines changed: 3 additions & 0 deletions
Loading

tests/test_graphical_units/test_img_and_svg.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@ def construct(self):
107107
self.wait()
108108

109109

110+
class HeartTest(Scene):
111+
def construct(self):
112+
heart = SVGMobject(get_test_resource("heart.svg"))
113+
self.add(heart)
114+
self.wait()
115+
116+
110117
class Arcs01Test(Scene):
111118
# See: https://www.w3.org/TR/SVG11/images/paths/arcs01.svg
112119
def construct(self):

0 commit comments

Comments
 (0)