arc transform fixed (#98)

* arc transform fixed

* Update svgpathtools/path.py

Co-authored-by: Matthijs Kooijman <matthijs@stdin.nl>

* Fixed rotation bug in transformation of arcs

* Made compatible with python2.7

Changed the shorthand @ for np matrix multiplication to matmul

Co-authored-by: vrroom <vrroomc@github.com>
Co-authored-by: Matthijs Kooijman <matthijs@stdin.nl>
hashable-paths
Vrroom 2021-06-23 08:58:24 +05:30 committed by GitHub
parent 1e5bfb4252
commit baba1d18b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 8 deletions

Binary file not shown.

View File

@ -13,6 +13,7 @@ from warnings import warn
from operator import itemgetter from operator import itemgetter
import numpy as np import numpy as np
from itertools import tee from itertools import tee
from functools import reduce
# these imports were originally from math and cmath, now are from numpy # these imports were originally from math and cmath, now are from numpy
# in order to encourage code that generalizes to vector inputs # in order to encourage code that generalizes to vector inputs
@ -288,7 +289,6 @@ def scale(curve, sx, sy=None, origin=0j):
raise TypeError("Input `curve` should be a Path, Line, " raise TypeError("Input `curve` should be a Path, Line, "
"QuadraticBezier, CubicBezier, or Arc object.") "QuadraticBezier, CubicBezier, or Arc object.")
def transform(curve, tf): def transform(curve, tf):
"""Transforms the curve by the homogeneous transformation matrix tf""" """Transforms the curve by the homogeneous transformation matrix tf"""
def to_point(p): def to_point(p):
@ -310,13 +310,32 @@ def transform(curve, tf):
elif isinstance(curve, Arc): elif isinstance(curve, Arc):
new_start = to_complex(tf.dot(to_point(curve.start))) new_start = to_complex(tf.dot(to_point(curve.start)))
new_end = to_complex(tf.dot(to_point(curve.end))) new_end = to_complex(tf.dot(to_point(curve.end)))
new_radius = to_complex(tf.dot(to_vector(curve.radius)))
if tf[0][0] * tf[1][1] >= 0.0: # Based on https://math.stackexchange.com/questions/2349726/compute-the-major-and-minor-axis-of-an-ellipse-after-linearly-transforming-it
new_sweep = curve.sweep rx2 = curve.radius.real ** 2
else: ry2 = curve.radius.imag ** 2
new_sweep = not curve.sweep
return Arc(new_start, radius=new_radius, rotation=curve.rotation, Q = np.array([[1/rx2, 0], [0, 1/ry2]])
large_arc=curve.large_arc, sweep=new_sweep, end=new_end) invT = np.linalg.inv(tf[:2,:2])
D = reduce(np.matmul, [invT.T, Q, invT])
eigvals, eigvecs = np.linalg.eig(D)
rx = 1 / np.sqrt(eigvals[0])
ry = 1 / np.sqrt(eigvals[1])
new_radius = complex(rx, ry)
xeigvec = eigvecs[:, 0]
rot = np.degrees(np.arccos(xeigvec[0]))
if new_radius.real == 0 or new_radius.imag == 0 :
return Line(new_start, new_end)
else :
return Arc(new_start, radius=new_radius, rotation=curve.rotation + rot,
large_arc=curve.large_arc, sweep=curve.sweep, end=new_end,
autoscale_radius=False)
else: else:
raise TypeError("Input `curve` should be a Path, Line, " raise TypeError("Input `curve` should be a Path, Line, "
"QuadraticBezier, CubicBezier, or Arc object.") "QuadraticBezier, CubicBezier, or Arc object.")