709 lines
25 KiB
Python
709 lines
25 KiB
Python
# coding=utf-8
|
|
"""
|
|
Test Inkex path parsing functionality.
|
|
"""
|
|
|
|
import math
|
|
from inkex.tester import TestCase
|
|
from inkex.paths import (
|
|
PathCommand,
|
|
Quadratic,
|
|
quadratic,
|
|
line,
|
|
move,
|
|
vert,
|
|
horz,
|
|
zoneClose,
|
|
Line,
|
|
Move,
|
|
Horz,
|
|
Vert,
|
|
ZoneClose,
|
|
Curve,
|
|
curve,
|
|
Arc,
|
|
arc,
|
|
Smooth,
|
|
smooth,
|
|
TepidQuadratic,
|
|
tepidQuadratic,
|
|
LengthSettings,
|
|
)
|
|
from inkex.transforms import Vector2d
|
|
|
|
# pylint: disable=invalid-name
|
|
|
|
|
|
class PointTests(TestCase):
|
|
"""Some more complex cubic and quadratic beziers"""
|
|
|
|
def compare_points(self, start, curve: Curve, pts):
|
|
"""Helper function to compare the point() function of a bezier curve"""
|
|
for s, x, y in pts:
|
|
self.assertAlmostTuple(
|
|
curve.point(Vector2d(), Vector2d(start), Vector2d(), s),
|
|
Vector2d(x, y),
|
|
precision=8,
|
|
)
|
|
|
|
def test_point(self):
|
|
"""Check the point computation for cubic beziers.
|
|
|
|
See https://github.com/mathandy/svgpathtools/blob/f7e074339d579d0e44d455b92ecf57f24d799fb5/test/test_path.py#L290
|
|
"""
|
|
self.compare_points(
|
|
(100, 200),
|
|
Curve(100, 100, 250, 100, 250, 200),
|
|
[
|
|
(0, 100, 200),
|
|
(0.3, 132.4, 137),
|
|
(0.5, 175, 125),
|
|
(0.9, 245.8, 173),
|
|
(1, 250, 200),
|
|
],
|
|
)
|
|
self.compare_points(
|
|
(600, 800),
|
|
Curve(625, 700, 725, 700, 750, 800),
|
|
[
|
|
(0, 600, 800),
|
|
(0.3, 638.7, 737),
|
|
(0.5, 675, 725),
|
|
(0.9, 740.4, 773),
|
|
(1, 750, 800),
|
|
],
|
|
)
|
|
|
|
def test_point_quadratic(self):
|
|
self.compare_points(
|
|
(200, 300),
|
|
Quadratic(400, 50, 600, 300),
|
|
[
|
|
(0, 200, 300),
|
|
(0.3, 320, 195),
|
|
(0.5, 400, 175),
|
|
(0.9, 560, 255),
|
|
(1, 600, 300),
|
|
],
|
|
)
|
|
self.compare_points(
|
|
(600, 300),
|
|
Quadratic(800, 550, 1000, 300),
|
|
[
|
|
(0, 600, 300),
|
|
(0.3, 720, 405),
|
|
(0.5, 800, 425),
|
|
(0.9, 960, 345),
|
|
(1, 1000, 300),
|
|
],
|
|
)
|
|
|
|
|
|
class DerivativeTests(TestCase):
|
|
"""Test derivative computation"""
|
|
|
|
def compare_derivative(
|
|
self, command, t, expected, n=1, first=Vector2d(0, 0), prev=Vector2d(0, 0)
|
|
):
|
|
"""Derivative comparison function"""
|
|
result = command.derivative(Vector2d(first), Vector2d(prev), Vector2d(), t, n)
|
|
self.assertAlmostTuple(result, expected, precision=12)
|
|
|
|
def test_line_first_derivative(self):
|
|
"""Compute derivatives of some lines"""
|
|
prev = (2, 1)
|
|
first = (5, 5)
|
|
for t in [None, 0, 0.5, 0.7, 1]:
|
|
self.compare_derivative(Line(5, 5), t, (3, 4), n=1, prev=prev)
|
|
self.compare_derivative(line(5, 5), t, (5, 5), 1, prev=prev)
|
|
self.compare_derivative(horz(5), t, (5, 0), n=1, prev=prev)
|
|
self.compare_derivative(Horz(5), t, (3, 0), n=1, prev=prev)
|
|
self.compare_derivative(vert(5), t, (0, 5), n=1, prev=prev)
|
|
self.compare_derivative(Vert(5), t, (0, 4), n=1, prev=prev)
|
|
self.compare_derivative(zoneClose(), t, (3, 4), n=1, prev=prev, first=first)
|
|
self.compare_derivative(ZoneClose(), t, (3, 4), n=1, prev=prev, first=first)
|
|
|
|
def test_line_higher_derivative(self):
|
|
"""The higher derivative is 0 for all line-like commands"""
|
|
for t in [None, 0, 0.5, 0.7, 1]:
|
|
for com in [Line(5, 5), line(5, 5), Horz(5), vert(5), ZoneClose()]:
|
|
for n in [2, 4, 200]:
|
|
self.compare_derivative(com, t, (0, 0), n, (5, 5), (2, 1))
|
|
|
|
def test_line_invalid_parameters(self):
|
|
"""Some invalid line parameters"""
|
|
|
|
# Line with zero length: derivative is undefined
|
|
with self.assertRaises(ValueError):
|
|
self.compare_derivative(Line(0, 0), 0.5, (0, 0), n=1)
|
|
|
|
# t not in [0, 1]
|
|
with self.assertRaises(ValueError):
|
|
self.compare_derivative(Line(1, 1), -0.5, (0, 0), n=1)
|
|
|
|
# n is not integer or not positive
|
|
with self.assertRaises(ValueError):
|
|
self.compare_derivative(Line(1, 1), -0.5, (0, 0), n=1.5)
|
|
|
|
with self.assertRaises(ValueError):
|
|
self.compare_derivative(Line(1, 1), -0.5, (0, 0), n=0)
|
|
|
|
|
|
class CurvatureTests(TestCase):
|
|
"""Test curvature computation"""
|
|
|
|
def compare_curvature(
|
|
self, command, t, expected, first=Vector2d(0, 0), prev=Vector2d(0, 0)
|
|
):
|
|
"""Curvature comparison function"""
|
|
result = command.curvature(Vector2d(first), Vector2d(prev), t)
|
|
self.assertAlmostEqual(result, expected, places=12)
|
|
|
|
def test_line_curvature(self):
|
|
"""The curvature is 0 for all line-like commands"""
|
|
for t in [None, 0, 0.5, 0.7, 1]:
|
|
for com in [Line(5, 5), line(5, 5), Horz(5), vert(5), ZoneClose()]:
|
|
self.compare_curvature(com, t, 0, (5, 5), (2, 1))
|
|
|
|
|
|
class NormalTests(TestCase):
|
|
"""Test Normal computation."""
|
|
|
|
def compare_normal(
|
|
self, command, t, expected, first=Vector2d(0, 0), prev=Vector2d(0, 0)
|
|
):
|
|
"""Normal comparison function"""
|
|
result = command.normal(Vector2d(first), Vector2d(prev), t)
|
|
self.assertAlmostTuple(result, expected, precision=12)
|
|
|
|
def test_line_normals(self):
|
|
"""Compute unit normals of some lines"""
|
|
prev = (2, 1)
|
|
first = (5, 5)
|
|
for t in [None, 0, 0.5, 0.7, 1]:
|
|
self.compare_normal(Line(5, 5), t, (-4 / 5, 3 / 5), prev=prev)
|
|
self.compare_normal(line(4, -3), t, (3 / 5, 4 / 5), prev=prev)
|
|
self.compare_normal(horz(5), t, (0, 1), prev=prev)
|
|
self.compare_normal(Horz(5), t, (0, 1), prev=prev)
|
|
self.compare_normal(vert(5), t, (-1, 0), prev=prev)
|
|
self.compare_normal(Vert(5), t, (-1, 0), prev=prev)
|
|
self.compare_normal(zoneClose(), t, (-4 / 5, 3 / 5), prev=prev, first=first)
|
|
self.compare_normal(ZoneClose(), t, (-4 / 5, 3 / 5), prev=prev, first=first)
|
|
|
|
|
|
class SplitTests(TestCase):
|
|
"""Test various splits"""
|
|
|
|
def compare_split(
|
|
self,
|
|
command: PathCommand,
|
|
t,
|
|
command_1,
|
|
command_2,
|
|
first=Vector2d(0, 0),
|
|
prev=Vector2d(0, 0),
|
|
prev_control=Vector2d(0, 0),
|
|
):
|
|
"""Comparison function for splitting"""
|
|
result = command.split(first, prev, prev_control, t)
|
|
print(result[0], result[1])
|
|
self.assertEqual(result[0].letter, command_1.letter)
|
|
self.assertEqual(result[1].letter, command_2.letter)
|
|
self.assertAlmostTuple(result[0].args, command_1.args)
|
|
self.assertAlmostTuple(result[1].args, command_2.args)
|
|
|
|
def test_split_line(self):
|
|
"""Splitting a relative line should return two relative lines"""
|
|
self.compare_split(
|
|
line(4, 8), 0.25, line(1, 2), line(3, 6), prev=Vector2d(5, 5)
|
|
)
|
|
|
|
def test_split_Line(self):
|
|
"""Splitting an absolute Line should return two absolute lines"""
|
|
self.compare_split(
|
|
Line(4, 6), 0.25, Line(2.5, 3), Line(4, 6), prev=Vector2d(2, 2)
|
|
)
|
|
|
|
def test_split_horz(self):
|
|
"""Splitting a relative horz should return two relative horz's"""
|
|
self.compare_split(horz(-8), 0.125, horz(-1), horz(-7), prev=Vector2d(5, 6))
|
|
|
|
def test_split_Horz(self):
|
|
"""Splitting an absolute Horz should return two absolute Horz's"""
|
|
self.compare_split(Horz(10), 0.75, Horz(8), Horz(10), prev=Vector2d(2, 7))
|
|
|
|
def test_split_vert(self):
|
|
"""Splitting a relative vert should return two relative verts"""
|
|
self.compare_split(vert(-8), 0.125, vert(-1), vert(-7), prev=Vector2d(6, 5))
|
|
|
|
def test_split_Vert(self):
|
|
"""Splitting an absolute Vert should return two absolute Verts"""
|
|
self.compare_split(Vert(-3), 0.1, Vert(6), Vert(-3), prev=Vector2d(2, 7))
|
|
|
|
def test_split_ZoneClose(self):
|
|
"""Splitting an absolute ZoneClose should return a Line and a ZoneClose"""
|
|
self.compare_split(
|
|
ZoneClose(), 0.75, Line(1.25, 1), ZoneClose(), 1 - 1j, 2 + 7j
|
|
)
|
|
|
|
def test_split_zoneClose(self):
|
|
"""Splitting a relative zoneClose should return a (relative) line and a
|
|
zoneClose"""
|
|
self.compare_split(
|
|
zoneClose(), 0.75, line(-0.75, -6), zoneClose(), 1 - 1j, 2 + 7j
|
|
)
|
|
|
|
def test_split_move(self):
|
|
"""Splitting a move is not supported, because this would change the number
|
|
of subpaths, i.e. introduce unnecessary complexity."""
|
|
with self.assertRaises(ValueError):
|
|
self.compare_split(Move(6, 6), 0.5, Move(3, 3), Move(6, 6))
|
|
|
|
with self.assertRaises(ValueError):
|
|
self.compare_split(move(6, 6), 0.5, move(3, 3), move(3, 3))
|
|
|
|
def test_split_curves(self):
|
|
"""De Casteljau"""
|
|
|
|
self.compare_split(
|
|
Curve(300 + 0j, -100 - 10j, 250 + 100j),
|
|
0.7,
|
|
Curve(225 + 30j, 81.5 + 4.1j, 99.7 + 32.59j),
|
|
Curve(107.5 + 44.8j, 145 + 67j, 250 + 100j),
|
|
0j,
|
|
50 + 100j,
|
|
)
|
|
# Same for relative
|
|
self.compare_split(
|
|
curve(250 - 100j, -150 - 110j, 200 + 0j),
|
|
0.7,
|
|
curve(175 - 70j, 31.5 - 95.9j, 49.7 - 67.41j),
|
|
curve(7.8 + 12.21j, 45.3 + 34.41j, 150.3 + 67.41j),
|
|
0j,
|
|
50 + 100j,
|
|
)
|
|
|
|
def test_split_smooth(self):
|
|
"""Same for smooth 3rd order. Can only be split into curves"""
|
|
prev = 250 + 100j
|
|
prev_prev = -100 - 10j
|
|
self.compare_split(
|
|
Smooth(0 + 250j, 400 + 150j),
|
|
0.3,
|
|
Curve(355 + 133j, 374.5 + 159.7j, 361.15 + 178.21j),
|
|
Curve(330 + 221.4j, 120 + 220j, 400 + 150j),
|
|
0j,
|
|
prev,
|
|
prev_prev,
|
|
)
|
|
|
|
self.compare_split(
|
|
Smooth(0 + 250j, 400 + 150j).to_relative(prev),
|
|
0.3,
|
|
curve(105 + 33j, 124.5 + 59.7j, 111.15 + 78.21j),
|
|
curve(-31.15 + 43.19j, -241.15 + 41.79j, 38.85 - 28.21j),
|
|
0j,
|
|
prev,
|
|
prev_prev,
|
|
)
|
|
|
|
def test_split_quadratic(self):
|
|
"""De Casteljau for 2nd order curves"""
|
|
self.compare_split(
|
|
Quadratic(300 + 0j, 10 + 10j),
|
|
0.2,
|
|
Quadratic(100 + 80j, 128.4 + 64.4j),
|
|
Quadratic(242 + 2j, 10 + 10j),
|
|
0j,
|
|
50 + 100j,
|
|
)
|
|
self.compare_split(
|
|
quadratic(250 - 100j, -40 - 90j),
|
|
0.2,
|
|
quadratic(50 - 20j, 78.4 - 35.6j),
|
|
quadratic(113.6 - 62.4j, -118.4 - 54.4j),
|
|
0j,
|
|
50 + 100j,
|
|
)
|
|
|
|
def test_split_tepid_quadratic(self):
|
|
"""Smooth 2nd order"""
|
|
self.compare_split(
|
|
TepidQuadratic(100 + 100j),
|
|
0.2,
|
|
Quadratic(-48 + 12j, -79.2 + 16.8j),
|
|
Quadratic(-204 + 36j, 100 + 100j),
|
|
0j,
|
|
10 + 10j,
|
|
300 + 0j,
|
|
)
|
|
self.compare_split(
|
|
tepidQuadratic(100 + 100j),
|
|
0.2,
|
|
quadratic(-58 + 2j, -88.8 + 7.2j),
|
|
quadratic(-123.2 + 20.8j, 188.8 + 92.8j),
|
|
0j,
|
|
10 + 10j,
|
|
300 + 0j,
|
|
)
|
|
|
|
def test_split_arc(self):
|
|
"""Splitting an Arc/arc"""
|
|
|
|
initial = 725.307482225571 - 915.5548199281527j
|
|
arc5 = Arc(
|
|
(202.79421639137703 + 148.77294617167183j),
|
|
225.6910319606926,
|
|
1,
|
|
1,
|
|
(-624.6375539637027 + 896.5483089399895j),
|
|
)
|
|
|
|
a1, a2 = arc5.split(0j, initial, 0j, 0.4)
|
|
print(a1, a2)
|
|
self.assertAlmostEqual(
|
|
arc5.cpoint(0j, initial, 0j, 0.4), a1.cpoint(0j, initial, 0j, 1), delta=TOL
|
|
)
|
|
|
|
self.assertAlmostEqual(
|
|
arc5.cpoint(0j, initial, 0j, 0.4),
|
|
a2.cpoint(0j, a1.cend_point(0j, initial), 0j, 0),
|
|
delta=TOL,
|
|
)
|
|
|
|
self.assertAlmostEqual(
|
|
arc5.cpoint(0j, initial, 0j, 1),
|
|
a2.cpoint(0j, a1.cend_point(0j, initial), 0j, 1),
|
|
delta=TOL,
|
|
)
|
|
|
|
# Same for relative arcs
|
|
|
|
arc5rel = arc(
|
|
(202.79421639137703 + 148.77294617167183j),
|
|
225.6910319606926,
|
|
1,
|
|
1,
|
|
(-624.6375539637027 + 896.5483089399895j) - initial,
|
|
)
|
|
|
|
a1, a2 = arc5rel.split(0j, initial, 0j, 0.4)
|
|
print(a1, a2)
|
|
self.assertAlmostEqual(
|
|
arc5rel.cpoint(0j, initial, 0j, 0.4),
|
|
a1.cpoint(0j, initial, 0j, 1),
|
|
delta=TOL,
|
|
)
|
|
|
|
self.assertAlmostEqual(
|
|
arc5rel.cpoint(0j, initial, 0j, 0.4),
|
|
a2.cpoint(0j, a1.cend_point(0j, initial), 0j, 0),
|
|
delta=TOL,
|
|
)
|
|
|
|
self.assertAlmostEqual(
|
|
arc5rel.cpoint(0j, initial, 0j, 1),
|
|
a2.cpoint(0j, a1.cend_point(0j, initial), 0j, 1),
|
|
delta=TOL,
|
|
)
|
|
|
|
|
|
class LengthTests(TestCase):
|
|
"""Test length computation"""
|
|
|
|
def compare_length(
|
|
self, prev, command: PathCommand, expected, t0=0, t1=1, first=Vector2d(0, 0)
|
|
):
|
|
"""Length comparison function"""
|
|
result = command.length(
|
|
Vector2d(first),
|
|
Vector2d(prev),
|
|
Vector2d(),
|
|
t0,
|
|
t1,
|
|
LengthSettings(error=1e-12),
|
|
)
|
|
self.assertAlmostEqual(result, expected, places=8)
|
|
|
|
def test_length_lines(self):
|
|
"""Test length computation for line-like commands"""
|
|
|
|
prev = (2, 1)
|
|
first = (5, 5)
|
|
|
|
self.compare_length(prev, Line(5, 5), 5)
|
|
self.compare_length(prev, Line(5, 5), 3, 0.2, 0.8)
|
|
self.compare_length(prev, line(-5, 12), 13)
|
|
self.compare_length(prev, horz(5), 4, 0, 0.8)
|
|
self.compare_length(prev, Horz(5), 3)
|
|
self.compare_length(prev, Vert(5), 3, 0.25, 1)
|
|
self.compare_length(prev, vert(5), 5)
|
|
self.compare_length(prev, ZoneClose(), 5, first=first)
|
|
self.compare_length(prev, zoneClose(), 4, 0.2, first=first)
|
|
|
|
def test_length_cubic(self):
|
|
"""based on https://github.com/mathandy/svgpathtools/blob/f7e074339d579d0e44d455b92ecf57f24d799fb5/test/test_path.py#L363"""
|
|
# A straight line
|
|
self.compare_length((0, 0), Curve(0, 0, 0, 100, 0, 100), 100)
|
|
# A diagonal line
|
|
self.compare_length((0, 0), Curve(0, 0, 100, 100, 100, 100), math.sqrt(2) * 100)
|
|
# A cubic bezier that approximates a quarter arc.
|
|
kappa = 4 * (math.sqrt(2) - 1) / 3
|
|
self.compare_length(
|
|
(0, 0),
|
|
Curve(0, 100 * kappa, 100 - 100 * kappa, 100, 100, 100),
|
|
157.10166980361998,
|
|
)
|
|
|
|
def test_length_quadratic(self):
|
|
"""based on https://github.com/mathandy/svgpathtools/blob/f7e074339d579d0e44d455b92ecf57f24d799fb5/test/test_path.py#L450"""
|
|
q1 = [(200, 300), Quadratic(400, 50, 600, 300)]
|
|
q2 = [(200, 300), Quadratic(400, 50, 500, 200)]
|
|
self.compare_length(*q1, 487.77109389525975)
|
|
self.compare_length(*q2, 379.90458193489155)
|
|
# Closed quadratic
|
|
closedq = [(6, 2), Quadratic(5, -1, 6, 2)]
|
|
self.compare_length(*closedq, 3.1622776601683795)
|
|
# lines
|
|
linq1 = [(1, 0), Quadratic(2, 0, 3, 0)]
|
|
linq2 = [(1, 3), Quadratic(2, 5, -9, -17)]
|
|
self.compare_length(*linq1, 2)
|
|
self.compare_length(*linq2, 22.73335777124786)
|
|
# all nodes on the same point
|
|
samepoint = [(1, 0), Quadratic(1, 0, 1, 0)]
|
|
self.compare_length(*samepoint, 0)
|
|
|
|
# partial length tests
|
|
self.compare_length(*q1, 212.34775387566032, 0.25, 0.75)
|
|
self.compare_length(*q2, 166.22170622052397, 0.25, 0.75)
|
|
self.compare_length(*closedq, 0.7905694150420949, 0.25, 0.75)
|
|
self.compare_length(*linq1, 1, 0.25, 0.75)
|
|
self.compare_length(*samepoint, 0, 0.25, 0.75)
|
|
|
|
|
|
class IlengthTests(TestCase):
|
|
"""Test inverse length computation"""
|
|
|
|
def compare_ilength(
|
|
self,
|
|
command,
|
|
s,
|
|
expected,
|
|
first=Vector2d(0, 0),
|
|
prev=Vector2d(0, 0),
|
|
prev_control=Vector2d(0, 0),
|
|
places=8,
|
|
):
|
|
"""Inverse Length comparison function"""
|
|
result = command.ilength(
|
|
Vector2d(first),
|
|
Vector2d(prev),
|
|
Vector2d(prev_control),
|
|
s,
|
|
)
|
|
self.assertAlmostEqual(result, expected, places=places)
|
|
|
|
def test_ilength_lines(self):
|
|
"""Test inverse length computation for line-like commands"""
|
|
prev = (2, 1)
|
|
first = (5, 5)
|
|
|
|
self.compare_ilength(Line(5, 5), 5, 1, prev=prev)
|
|
self.compare_ilength(Line(5, 5), 4, 0.8, prev=prev)
|
|
|
|
self.compare_ilength(line(-5, 12), 6.5, 0.5, prev=prev)
|
|
self.compare_ilength(horz(5), 4, 0.8, prev=prev)
|
|
self.compare_ilength(Horz(5), 2, 2 / 3, prev=prev)
|
|
self.compare_ilength(Vert(5), 3, 0.75, prev=prev)
|
|
self.compare_ilength(vert(5), 5, 1, prev=prev)
|
|
self.compare_ilength(ZoneClose(), 5, 1, prev=prev, first=first)
|
|
self.compare_ilength(zoneClose(), 4, 0.8, prev=prev, first=first)
|
|
|
|
def test_ilength_quadratics(self):
|
|
"""From https://github.com/mathandy/svgpathtools/blob/f7e074339d579d0e44d455b92ecf57f24d799fb5/test/test_path.py#L1214"""
|
|
q1 = [(200, 300), Quadratic(400, 50, 600, 300)]
|
|
q2 = [(200, 300), Quadratic(400, 50, 500, 200)]
|
|
closedq = [(6, 2), Quadratic(5, -1, 6, 2)]
|
|
linq = [(1, 3), Quadratic(2, 5, -9, -17)]
|
|
|
|
tests = [
|
|
(q1, 0.01, 6.364183310105577),
|
|
(q1, 0.1, 60.23857499635088),
|
|
(q1, 0.5, 243.8855469477619),
|
|
(q1, 0.9, 427.53251889917294),
|
|
(q1, 0.99, 481.40691058541813),
|
|
(q2, 0.01, 6.365673533661836),
|
|
(q2, 0.1, 60.31675895732397),
|
|
(q2, 0.5, 233.24592830045907),
|
|
(q2, 0.9, 346.42891253298706),
|
|
(q2, 0.99, 376.32659156736844),
|
|
(closedq, 0.01, 0.06261309767133393),
|
|
(closedq, 0.1, 0.5692099788303084),
|
|
(closedq, 0.5, 1.5811388300841898),
|
|
(closedq, 0.9, 2.5930676813380713),
|
|
(closedq, 0.99, 3.0996645624970456),
|
|
(linq, 0.01, 0.04203807797699605),
|
|
(linq, 0.1, 0.19379255804998186),
|
|
(linq, 0.5, 4.844813951249544),
|
|
(linq, 0.9, 18.0823363780483),
|
|
(linq, 0.99, 22.24410609777091),
|
|
]
|
|
|
|
for q, t, s in tests:
|
|
self.compare_ilength(q[1], s, t, prev=q[0], places=4)
|
|
rel = q[1].to_relative(Vector2d(q[0]))
|
|
self.compare_ilength(rel, s, t, prev=q[0], places=4)
|
|
|
|
def test_ilength_cubics(self):
|
|
"""From https://github.com/mathandy/svgpathtools/blob/f7e074339d579d0e44d455b92ecf57f24d799fb5/test/test_path.py#L1250"""
|
|
c1 = [(200, 300), Curve(400, 50, 600, 100, -200, 0)]
|
|
symc = [(1, -2), Curve(10, -1, 10, 1, 1, 2)]
|
|
closedc = [(1, -2), Curve(10, -1, 10, 1, 1, -2)]
|
|
|
|
tests = [
|
|
(c1, 0.01, 9.53434737943073),
|
|
(c1, 0.1, 88.89941848775852),
|
|
(c1, 0.5, 278.5750942713189),
|
|
(c1, 0.9, 651.4957786584646),
|
|
(c1, 0.99, 840.2010603832538),
|
|
(symc, 0.01, 0.2690118556702902),
|
|
(symc, 0.1, 2.45230693868727),
|
|
(symc, 0.5, 7.256147083644424),
|
|
(symc, 0.9, 12.059987228602886),
|
|
(symc, 0.99, 14.243282311619401),
|
|
(closedc, 0.01, 0.26901140075538765),
|
|
(closedc, 0.1, 2.451722765460998),
|
|
(closedc, 0.5, 6.974058969750422),
|
|
(closedc, 0.9, 11.41781741489913),
|
|
(closedc, 0.99, 13.681324783697782),
|
|
]
|
|
|
|
for q, t, s in tests:
|
|
self.compare_ilength(q[1], s, t, prev=q[0], places=4)
|
|
rel = q[1].to_relative(Vector2d(q[0]))
|
|
self.compare_ilength(rel, s, t, prev=q[0], places=4)
|
|
|
|
|
|
TOL = 1e-4
|
|
|
|
|
|
class ArcTest(TestCase):
|
|
def test_point(self):
|
|
def assert_point(arc: Arc, start: complex, at: float, point: complex):
|
|
self.assertAlmostEqual(arc.cpoint(0j, start, 0j, at), point, delta=TOL)
|
|
|
|
arc1 = Arc(100 + 50j, 0, 0, 0, 100 + 50j)
|
|
_, _, _, center, theta1, deltaTheta = arc1.parametrize(0j)
|
|
self.assertAlmostEqual(center, 100 + 0j, delta=TOL)
|
|
self.assertAlmostEqual(theta1, 180.0, delta=TOL)
|
|
self.assertAlmostEqual(deltaTheta, -90.0, delta=TOL)
|
|
|
|
for at, point in [
|
|
(0.0, 0j),
|
|
(0.1, 1.23116594049 + 7.82172325201j),
|
|
(0.2, 4.89434837048 + 15.4508497187j),
|
|
(0.3, 10.8993475812 + 22.699524987j),
|
|
(0.4, 19.0983005625 + 29.3892626146j),
|
|
(0.5, 29.2893218813 + 35.3553390593j),
|
|
(0.6, 41.2214747708 + 40.4508497187j),
|
|
(0.7, 54.6009500260 + 44.5503262094j),
|
|
(0.8, 69.0983005625 + 47.5528258148j),
|
|
(0.9, 84.3565534960 + 49.3844170298j),
|
|
(1.0, 100 + 50j),
|
|
]:
|
|
assert_point(Arc(100 + 50j, 0, 0, 0, 100 + 50j), 0j, at, point)
|
|
|
|
arc2 = Arc(100 + 50j, 0, 1, 0, 100 + 50j)
|
|
_, _, _, center, theta1, deltaTheta = arc2.parametrize(0j)
|
|
self.assertAlmostEqual(center, 50j, delta=TOL)
|
|
self.assertAlmostEqual(theta1, -90.0, delta=TOL)
|
|
self.assertAlmostEqual(deltaTheta, -270.0, delta=TOL)
|
|
|
|
for at, point in [
|
|
(0.0, 0j),
|
|
(0.1, (-45.399049974 + 5.44967379058j)),
|
|
(0.2, (-80.9016994375 + 20.6107373854j)),
|
|
(0.3, (-98.7688340595 + 42.178276748j)),
|
|
(0.4, (-95.1056516295 + 65.4508497187j)),
|
|
(0.5, (-70.7106781187 + 85.3553390593j)),
|
|
(0.6, (-30.9016994375 + 97.5528258148j)),
|
|
(0.7, (15.643446504 + 99.3844170298j)),
|
|
(0.8, (58.7785252292 + 90.4508497187j)),
|
|
(0.9, (89.1006524188 + 72.699524987j)),
|
|
(1.0, (100 + 50j)),
|
|
]:
|
|
assert_point(Arc(100 + 50j, 0, 1, 0, 100 + 50j), 0j, at, point)
|
|
|
|
arc3 = Arc(100 + 50j, 0, 0, 1, 100 + 50j)
|
|
_, _, _, center, theta1, deltaTheta = arc3.parametrize(0j)
|
|
self.assertAlmostEqual(center, 50j, delta=TOL)
|
|
self.assertAlmostEqual(theta1, -90.0, delta=TOL)
|
|
self.assertAlmostEqual(deltaTheta, 90.0, delta=TOL)
|
|
|
|
for at, point in [
|
|
(0.0, 0j),
|
|
(0.1, (15.643446504 + 0.615582970243j)),
|
|
(0.2, (30.9016994375 + 2.44717418524j)),
|
|
(0.3, (45.399049974 + 5.44967379058j)),
|
|
(0.4, (58.7785252292 + 9.54915028125j)),
|
|
(0.5, (70.7106781187 + 14.6446609407j)),
|
|
(0.6, (80.9016994375 + 20.6107373854j)),
|
|
(0.7, (89.1006524188 + 27.300475013j)),
|
|
(0.8, (95.1056516295 + 34.5491502813j)),
|
|
(0.9, (98.7688340595 + 42.178276748j)),
|
|
(1.0, (100 + 50j)),
|
|
]:
|
|
assert_point(Arc(100 + 50j, 0, 0, 1, 100 + 50j), 0j, at, point)
|
|
|
|
arc4 = Arc(100 + 50j, 0, 1, 1, 100 + 50j)
|
|
_, _, _, center, theta1, deltaTheta = arc4.parametrize(0j)
|
|
self.assertAlmostEqual(center, 100 + 0j, delta=TOL)
|
|
self.assertAlmostEqual(theta1, 180.0, delta=TOL)
|
|
self.assertAlmostEqual(deltaTheta, 270.0, delta=TOL)
|
|
|
|
for at, point in [
|
|
(0.0, 0j),
|
|
(0.1, (10.8993475812 - 22.699524987j)),
|
|
(0.2, (41.2214747708 - 40.4508497187j)),
|
|
(0.3, (84.3565534960 - 49.3844170298j)),
|
|
(0.4, (130.901699437 - 47.5528258148j)),
|
|
(0.5, (170.710678119 - 35.3553390593j)),
|
|
(0.6, (195.105651630 - 15.4508497187j)),
|
|
(0.7, (198.768834060 + 7.82172325201j)),
|
|
(0.8, (180.901699437 + 29.3892626146j)),
|
|
(0.9, (145.399049974 + 44.5503262094j)),
|
|
(1.0, (100 + 50j)),
|
|
]:
|
|
assert_point(Arc(100 + 50j, 0, 1, 1, 100 + 50j), 0j, at, point)
|
|
|
|
initial = 725.307482225571 - 915.5548199281527j
|
|
arc5 = Arc(
|
|
(202.79421639137703 + 148.77294617167183j),
|
|
225.6910319606926,
|
|
1,
|
|
1,
|
|
(-624.6375539637027 + 896.5483089399895j),
|
|
)
|
|
arc5rel = arc(
|
|
(202.79421639137703 + 148.77294617167183j),
|
|
225.6910319606926,
|
|
1,
|
|
1,
|
|
(-624.6375539637027 + 896.5483089399895j) - initial,
|
|
)
|
|
|
|
for at, point in [
|
|
(0.0, initial),
|
|
(0.0909090909091, (1023.47397369 - 597.730444283j)),
|
|
(0.181818181818, (1242.80253007 - 232.251400124j)),
|
|
(0.272727272727, (1365.52445614 + 151.273373978j)),
|
|
(0.363636363636, (1381.69755131 + 521.772981736j)),
|
|
(0.454545454545, (1290.01156757 + 849.231748376j)),
|
|
(0.545454545455, (1097.89435807 + 1107.12091209j)),
|
|
(0.636363636364, (820.910116547 + 1274.54782658j)),
|
|
(0.727272727273, (481.49845896 + 1337.94855893j)),
|
|
(0.818181818182, (107.156499251 + 1292.18675889j)),
|
|
(0.909090909091, (-271.788803303 + 1140.96977533j)),
|
|
]:
|
|
assert_point(arc5, initial, at, point)
|
|
assert_point(arc5rel, initial, at, point)
|