partially fixed scale fcn and improved tests
parent
f932036fb5
commit
fd95b5609f
|
@ -224,25 +224,25 @@ def scale(curve, sx, sy=None, origin=0j):
|
||||||
else:
|
else:
|
||||||
isy = 1j*sy
|
isy = 1j*sy
|
||||||
|
|
||||||
def transform(z, sx=sx, sy=sy, origin=origin):
|
def transform(z, origin=origin):
|
||||||
zeta = z - origin
|
zeta = z - origin
|
||||||
return x*zeta.real + isy*zeta.imag + origin
|
return sx*zeta.real + isy*zeta.imag + origin
|
||||||
|
|
||||||
if isinstance(curve, Path):
|
if isinstance(curve, Path):
|
||||||
return Path(*[scale(seg, sx, sy, origin) for seg in curve])
|
return Path(*[scale(seg, sx, sy, origin) for seg in curve])
|
||||||
elif is_bezier_segment(curve):
|
elif is_bezier_segment(curve):
|
||||||
return bpoints2bezier([transform(z) for z in curve.bpoints()])
|
return bpoints2bezier([transform(z) for z in curve.bpoints()])
|
||||||
elif isinstance(curve, Arc):
|
elif isinstance(curve, Arc):
|
||||||
if y is None or y == x:
|
if sy is None or sy == sx:
|
||||||
return Arc(start=transform(curve.start),
|
return Arc(start=transform(curve.start),
|
||||||
radius=transform(radius, origin=0),
|
radius=transform(curve.radius, origin=0),
|
||||||
rotation=curve.rotation,
|
rotation=curve.rotation,
|
||||||
large_arc=curve.large_arc,
|
large_arc=curve.large_arc,
|
||||||
sweep=curve.sweep,
|
sweep=curve.sweep,
|
||||||
end=transform(curve.end))
|
end=transform(curve.end))
|
||||||
else:
|
else:
|
||||||
raise Excpetion("For `Arc` objects, only scale transforms "
|
raise Exception("For `Arc` objects, only scale transforms "
|
||||||
"with sx==sy are implemenented.")
|
"with sx==sy are implemented.")
|
||||||
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.")
|
||||||
|
@ -2042,9 +2042,9 @@ class Path(MutableSequence):
|
||||||
0) == previous.unit_tangent(1)
|
0) == previous.unit_tangent(1)
|
||||||
|
|
||||||
def T2t(self, T):
|
def T2t(self, T):
|
||||||
"""returns the segment index, seg_idx, and segment parameter, t,
|
"""returns the segment index, `seg_idx`, and segment parameter, `t`,
|
||||||
corresponding to the path parameter T. In other words, this is the
|
corresponding to the path parameter `T`. In other words, this is the
|
||||||
inverse of the Path.t2T() method."""
|
inverse of the `Path.t2T()` method."""
|
||||||
if T == 1:
|
if T == 1:
|
||||||
return len(self)-1, 1
|
return len(self)-1, 1
|
||||||
if T == 0:
|
if T == 0:
|
||||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import division, absolute_import, print_function
|
||||||
import unittest
|
import unittest
|
||||||
from math import sqrt, pi
|
from math import sqrt, pi
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from numpy import poly1d, linspace
|
import numpy as np
|
||||||
|
|
||||||
# Internal dependencies
|
# Internal dependencies
|
||||||
from svgpathtools import *
|
from svgpathtools import *
|
||||||
|
@ -727,6 +727,7 @@ class TestPath(unittest.TestCase):
|
||||||
p_open.cropped(1, 0)
|
p_open.cropped(1, 0)
|
||||||
|
|
||||||
def test_transform_scale(self):
|
def test_transform_scale(self):
|
||||||
|
|
||||||
line1 = Line(600.5 + 350.5j, 650.5 + 325.5j)
|
line1 = Line(600.5 + 350.5j, 650.5 + 325.5j)
|
||||||
arc1 = Arc(650 + 325j, 25 + 25j, -30, 0, 1, 700 + 300j)
|
arc1 = Arc(650 + 325j, 25 + 25j, -30, 0, 1, 700 + 300j)
|
||||||
arc2 = Arc(650 + 325j, 30 + 25j, -30, 0, 0, 700 + 300j)
|
arc2 = Arc(650 + 325j, 30 + 25j, -30, 0, 0, 700 + 300j)
|
||||||
|
@ -744,44 +745,105 @@ class TestPath(unittest.TestCase):
|
||||||
cpath = Path(cub1)
|
cpath = Path(cub1)
|
||||||
apath = Path(arc1, arc2)
|
apath = Path(arc1, arc2)
|
||||||
|
|
||||||
test_curves = ([bezpath, bezpathz, path, pathz, lpath, qpath, cpath, apath] +
|
test_curves = [bezpath, bezpathz, path, pathz, lpath, qpath, cpath,
|
||||||
[line1, arc1, arc2, cub1, cub2, quad3, linez])
|
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)
|
# 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
|
# expected length
|
||||||
len_orig = path_orig.length()
|
len_orig = curve.length()
|
||||||
len_trns = path_trns.length()
|
len_trns = scaled_curve.length()
|
||||||
self.assertAlmostEqual(len_orig * 2.0, len_trns)
|
self.assertAlmostEqual(len_orig * 2.0, len_trns)
|
||||||
|
|
||||||
# expected positions
|
# expected positions
|
||||||
for T in linspace(0.0, 1.0, num=100):
|
for T in np.linspace(0.0, 1.0, num=100):
|
||||||
pt_orig = path_orig.point(T)
|
pt_orig = curve.point(T)
|
||||||
pt_trns = path_trns.point(T)
|
pt_trns = scaled_curve.point(T)
|
||||||
pt_xpct = (pt_orig - complex(100, 100)) * 2.0 + complex(100, 100)
|
pt_xpct = (pt_orig - complex(100, 100)) * 2.0 + complex(100, 100)
|
||||||
self.assertAlmostEqual(pt_xpct, pt_trns)
|
self.assertAlmostEqual(pt_xpct, pt_trns)
|
||||||
|
|
||||||
for path_orig in test_curves:
|
|
||||||
|
|
||||||
# scale by 0.3 around (0, -100)
|
# scale by 0.3 around (0, -100)
|
||||||
# the 'almost equal' test fails at the 7th decimal place for
|
# the 'almost equal' test fails at the 7th decimal place for
|
||||||
# some length and position tests here.
|
# 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
|
# expected length
|
||||||
len_orig = path_orig.length()
|
len_orig = curve.length()
|
||||||
len_trns = path_trns.length()
|
len_trns = scaled_curve.length()
|
||||||
self.assertAlmostEqual(len_orig * 0.3, len_trns, delta = 0.000001)
|
self.assertAlmostEqual(len_orig * 0.3, len_trns, delta=0.000001)
|
||||||
|
|
||||||
# expected positions
|
# expected positions
|
||||||
for T in linspace(0.0, 1.0, num=100):
|
for T in np.linspace(0.0, 1.0, num=100):
|
||||||
pt_orig = path_orig.point(T)
|
pt_orig = curve.point(T)
|
||||||
pt_trns = path_trns.point(T)
|
pt_trns = scaled_curve.point(T)
|
||||||
pt_xpct = (pt_orig - complex(0, -100)) * 0.3 + complex(0, -100)
|
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):
|
class Test_ilength(unittest.TestCase):
|
||||||
|
@ -1136,10 +1198,10 @@ class TestPathTools(unittest.TestCase):
|
||||||
|
|
||||||
# Case: Line
|
# Case: Line
|
||||||
pcoeffs = [(-1.7-2j), (6+2j)]
|
pcoeffs = [(-1.7-2j), (6+2j)]
|
||||||
p = poly1d(pcoeffs)
|
p = np.poly1d(pcoeffs)
|
||||||
correct_bpoints = [(6+2j), (4.3+0j)]
|
correct_bpoints = [(6+2j), (4.3+0j)]
|
||||||
|
|
||||||
# Input poly1d object
|
# Input np.poly1d object
|
||||||
bez = poly2bez(p)
|
bez = poly2bez(p)
|
||||||
bpoints = bez.bpoints()
|
bpoints = bez.bpoints()
|
||||||
self.assertAlmostEqual(distfcn(bpoints, correct_bpoints), 0)
|
self.assertAlmostEqual(distfcn(bpoints, correct_bpoints), 0)
|
||||||
|
@ -1150,10 +1212,10 @@ class TestPathTools(unittest.TestCase):
|
||||||
|
|
||||||
# Case: Quadratic
|
# Case: Quadratic
|
||||||
pcoeffs = [(29.5+15.5j), (-31-19j), (7.5+5.5j)]
|
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)]
|
correct_bpoints = [(7.5+5.5j), (-8-4j), (6+2j)]
|
||||||
|
|
||||||
# Input poly1d object
|
# Input np.poly1d object
|
||||||
bez = poly2bez(p)
|
bez = poly2bez(p)
|
||||||
bpoints = bez.bpoints()
|
bpoints = bez.bpoints()
|
||||||
self.assertAlmostEqual(distfcn(bpoints, correct_bpoints), 0)
|
self.assertAlmostEqual(distfcn(bpoints, correct_bpoints), 0)
|
||||||
|
@ -1164,10 +1226,10 @@ class TestPathTools(unittest.TestCase):
|
||||||
|
|
||||||
# Case: Cubic
|
# Case: Cubic
|
||||||
pcoeffs = [(-18.5-12.5j), (34.5+16.5j), (-18-6j), (6+2j)]
|
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)]
|
correct_bpoints = [(6+2j), 0j, (5.5+3.5j), (4+0j)]
|
||||||
|
|
||||||
# Input poly1d object
|
# Input np.poly1d object
|
||||||
bez = poly2bez(p)
|
bez = poly2bez(p)
|
||||||
bpoints = bez.bpoints()
|
bpoints = bez.bpoints()
|
||||||
self.assertAlmostEqual(distfcn(bpoints, correct_bpoints), 0)
|
self.assertAlmostEqual(distfcn(bpoints, correct_bpoints), 0)
|
||||||
|
|
Loading…
Reference in New Issue