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
parent
1e5bfb4252
commit
baba1d18b2
Binary file not shown.
|
@ -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.")
|
||||||
|
|
Loading…
Reference in New Issue