partially fixed scale fcn and improved tests

pull/60/head
Andy 2018-06-08 22:13:18 -07:00
parent f932036fb5
commit fd95b5609f
2 changed files with 97 additions and 35 deletions

View File

@ -224,25 +224,25 @@ def scale(curve, sx, sy=None, origin=0j):
else:
isy = 1j*sy
def transform(z, sx=sx, sy=sy, origin=origin):
def transform(z, origin=origin):
zeta = z - origin
return x*zeta.real + isy*zeta.imag + origin
return sx*zeta.real + isy*zeta.imag + origin
if isinstance(curve, Path):
return Path(*[scale(seg, sx, sy, origin) for seg in curve])
elif is_bezier_segment(curve):
return bpoints2bezier([transform(z) for z in curve.bpoints()])
elif isinstance(curve, Arc):
if y is None or y == x:
if sy is None or sy == sx:
return Arc(start=transform(curve.start),
radius=transform(radius, origin=0),
radius=transform(curve.radius, origin=0),
rotation=curve.rotation,
large_arc=curve.large_arc,
sweep=curve.sweep,
end=transform(curve.end))
else:
raise Excpetion("For `Arc` objects, only scale transforms "
"with sx==sy are implemenented.")
raise Exception("For `Arc` objects, only scale transforms "
"with sx==sy are implemented.")
else:
raise TypeError("Input `curve` should be a Path, Line, "
"QuadraticBezier, CubicBezier, or Arc object.")
@ -2042,9 +2042,9 @@ class Path(MutableSequence):
0) == previous.unit_tangent(1)
def T2t(self, T):
"""returns the segment index, seg_idx, and segment parameter, t,
corresponding to the path parameter T. In other words, this is the
inverse of the Path.t2T() method."""
"""returns the segment index, `seg_idx`, and segment parameter, `t`,
corresponding to the path parameter `T`. In other words, this is the
inverse of the `Path.t2T()` method."""
if T == 1:
return len(self)-1, 1
if T == 0:

View File

@ -3,7 +3,7 @@ from __future__ import division, absolute_import, print_function
import unittest
from math import sqrt, pi
from operator import itemgetter
from numpy import poly1d, linspace
import numpy as np
# Internal dependencies
from svgpathtools import *
@ -727,6 +727,7 @@ class TestPath(unittest.TestCase):
p_open.cropped(1, 0)
def test_transform_scale(self):
line1 = Line(600.5 + 350.5j, 650.5 + 325.5j)
arc1 = Arc(650 + 325j, 25 + 25j, -30, 0, 1, 700 + 300j)
arc2 = Arc(650 + 325j, 30 + 25j, -30, 0, 0, 700 + 300j)
@ -744,44 +745,105 @@ class TestPath(unittest.TestCase):
cpath = Path(cub1)
apath = Path(arc1, arc2)
test_curves = ([bezpath, bezpathz, path, pathz, lpath, qpath, cpath, apath] +
[line1, arc1, arc2, cub1, cub2, quad3, linez])
test_curves = [bezpath, bezpathz, path, pathz, lpath, qpath, cpath,
apath, line1, arc1, arc2, cub1, cub2, quad3, linez]
for path_orig in test_curves:
def scale_a_point(pt, sx, sy=None, origin=0j):
if sy is None:
sy = sx
zeta = pt - origin
pt_vec = [[zeta.real],
[zeta.imag],
[1]]
transform = [[sx, 0, origin.real],
[0, sy, origin.imag]]
return complex(*np.dot(transform, pt_vec).ravel())
for curve in test_curves:
# generate a random point and a random scaling
t = np.random.rand()
pt = curve.point(t)
# random diagonal transformation
sx = 2 * np.random.rand()
sy = 2 * np.random.rand()
# random origin
origin = (10 * (np.random.rand() - 0.5) +
10j * (np.random.rand() - 0.5))
# Note: `sx != sy` cases are not implemented for `Arc` objects
has_arc = (isinstance(curve, Arc) or
isinstance(curve, Path) and
any(isinstance(seg, Arc) for seg in curve))
# case where no `sy` and no `origin` given
ans = scale_a_point(pt, sx, None)
self.assertAlmostEqual(ans, curve.scaled(sx).point(t))
# case where no `sy` and random `origin` given
ans = scale_a_point(pt, sx, None, origin)
self.assertAlmostEqual(ans,
curve.scaled(sx, origin=origin).point(t))
# the cases with sx != sy are not yet imp
if isinstance(curve, Arc):
continue
# case where `sx != sy`, and no `origin` given
ans = scale_a_point(pt, sx, sy)
if has_arc:
self.assertRaises(Exception, curve.scaled(sx, sy).point(t))
else:
self.assertAlmostEqual(ans, curve.scaled(sx, sy).point(t))
# case where `sx != sy`, and random `origin` given
ans = scale_a_point(pt, sx, sy, origin)
if has_arc:
self.assertRaises(Exception,
curve.scaled(sx, sy, origin).point(t))
else:
self.assertAlmostEqual(ans,
curve.scaled(sx, sy, origin).point(t))
# more tests for scalar (i.e. `sx == sy`) case
for curve in test_curves:
# scale by 2 around (100, 100)
path_trns = path_orig.scaled(2.0, complex(100, 100))
scaled_curve = curve.scaled(2.0, complex(100, 100))
# expected length
len_orig = path_orig.length()
len_trns = path_trns.length()
len_orig = curve.length()
len_trns = scaled_curve.length()
self.assertAlmostEqual(len_orig * 2.0, len_trns)
# expected positions
for T in linspace(0.0, 1.0, num=100):
pt_orig = path_orig.point(T)
pt_trns = path_trns.point(T)
for T in np.linspace(0.0, 1.0, num=100):
pt_orig = curve.point(T)
pt_trns = scaled_curve.point(T)
pt_xpct = (pt_orig - complex(100, 100)) * 2.0 + complex(100, 100)
self.assertAlmostEqual(pt_xpct, pt_trns)
for path_orig in test_curves:
# scale by 0.3 around (0, -100)
# the 'almost equal' test fails at the 7th decimal place for
# some length and position tests here.
path_trns = path_orig.scaled(0.3, complex(0, -100))
scaled_curve = curve.scaled(0.3, complex(0, -100))
# expected length
len_orig = path_orig.length()
len_trns = path_trns.length()
self.assertAlmostEqual(len_orig * 0.3, len_trns, delta = 0.000001)
len_orig = curve.length()
len_trns = scaled_curve.length()
self.assertAlmostEqual(len_orig * 0.3, len_trns, delta=0.000001)
# expected positions
for T in linspace(0.0, 1.0, num=100):
pt_orig = path_orig.point(T)
pt_trns = path_trns.point(T)
for T in np.linspace(0.0, 1.0, num=100):
pt_orig = curve.point(T)
pt_trns = scaled_curve.point(T)
pt_xpct = (pt_orig - complex(0, -100)) * 0.3 + complex(0, -100)
self.assertAlmostEqual(pt_xpct, pt_trns, delta = 0.000001)
self.assertAlmostEqual(pt_xpct, pt_trns, delta=0.000001)
class Test_ilength(unittest.TestCase):
@ -1136,10 +1198,10 @@ class TestPathTools(unittest.TestCase):
# Case: Line
pcoeffs = [(-1.7-2j), (6+2j)]
p = poly1d(pcoeffs)
p = np.poly1d(pcoeffs)
correct_bpoints = [(6+2j), (4.3+0j)]
# Input poly1d object
# Input np.poly1d object
bez = poly2bez(p)
bpoints = bez.bpoints()
self.assertAlmostEqual(distfcn(bpoints, correct_bpoints), 0)
@ -1150,10 +1212,10 @@ class TestPathTools(unittest.TestCase):
# Case: Quadratic
pcoeffs = [(29.5+15.5j), (-31-19j), (7.5+5.5j)]
p = poly1d(pcoeffs)
p = np.poly1d(pcoeffs)
correct_bpoints = [(7.5+5.5j), (-8-4j), (6+2j)]
# Input poly1d object
# Input np.poly1d object
bez = poly2bez(p)
bpoints = bez.bpoints()
self.assertAlmostEqual(distfcn(bpoints, correct_bpoints), 0)
@ -1164,10 +1226,10 @@ class TestPathTools(unittest.TestCase):
# Case: Cubic
pcoeffs = [(-18.5-12.5j), (34.5+16.5j), (-18-6j), (6+2j)]
p = poly1d(pcoeffs)
p = np.poly1d(pcoeffs)
correct_bpoints = [(6+2j), 0j, (5.5+3.5j), (4+0j)]
# Input poly1d object
# Input np.poly1d object
bez = poly2bez(p)
bpoints = bez.bpoints()
self.assertAlmostEqual(distfcn(bpoints, correct_bpoints), 0)