Compare commits
20 Commits
master
...
security-u
Author | SHA1 | Date |
---|---|---|
Andrew Port | 67fd6e885d | |
Andrew Port | f2eb3d0596 | |
Andrew Port | 2422d15251 | |
Andrew Port | 2368627a17 | |
Andrew Port | 0c5dc9de1a | |
Andrew Port | 657a9d6745 | |
Andrew Port | 3e1f8e00a5 | |
Andrew Port | 05408cfa26 | |
Andrew Port | e71d2d4282 | |
Andrew Port | 413a2864f6 | |
Andrew Port | a2b62fc011 | |
Andrew Port | d86c63214b | |
Andrew Port | d2b1ea5770 | |
Andrew Port | da050a2eeb | |
Andrew Port | 0a31f348d6 | |
Andrew Port | 9863e7050a | |
Andrew Port | 11682a3363 | |
Andrew Port | 4f615f9a9d | |
Andrew Port | ace8522c19 | |
Andrew Port | d881b21b47 |
|
@ -1,17 +1,23 @@
|
||||||
"""The goal of this gist is to show how to compute many points on a path
|
""" An example of how to speed up point() calculations with vectorization.
|
||||||
|
|
||||||
|
The goal of this gist is to show how to compute many points on a path
|
||||||
quickly using NumPy arrays. I.e. there's a much faster way than using, say
|
quickly using NumPy arrays. I.e. there's a much faster way than using, say
|
||||||
[some_path.point(t) for t in many_tvals]. The example below assumes the
|
[some_path.point(t) for t in many_tvals]. The example below assumes the
|
||||||
`Path` object is composed entirely of `CubicBezier` objects, but this can
|
`Path` object is composed entirely of `CubicBezier` objects, but this can
|
||||||
easily be generalized to paths containing `Line` and `QuadraticBezier` objects
|
easily be generalized to paths containing `Line` and `QuadraticBezier` objects
|
||||||
also.
|
also.
|
||||||
|
|
||||||
Note: The relevant matrix transformation for quadratics can be found in the
|
Note: The relevant matrix transformation for quadratics can be found in the
|
||||||
svgpathtools.bezier module."""
|
svgpathtools.bezier module.
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from svgpathtools import *
|
from svgpathtools import bezier_point, bpoints2bezier, polynomial2bezier, Path
|
||||||
|
|
||||||
|
|
||||||
class HigherOrderBezier:
|
class HigherOrderBezier:
|
||||||
|
"""Bezier curve of arbitrary degree"""
|
||||||
def __init__(self, bpoints):
|
def __init__(self, bpoints):
|
||||||
self.bpts = bpoints
|
self.bpts = bpoints
|
||||||
|
|
||||||
|
@ -38,7 +44,7 @@ def points_in_each_seg_slow(path, tvals):
|
||||||
|
|
||||||
def points_in_each_seg(path, tvals):
|
def points_in_each_seg(path, tvals):
|
||||||
"""Compute seg.point(t) for each seg in path and each t in tvals."""
|
"""Compute seg.point(t) for each seg in path and each t in tvals."""
|
||||||
A = np.array([[-1, 3, -3, 1], # transforms cubic bez to standard poly
|
A = np.array([[-1, 3, -3, 1], # transforms cubic bez to standard poly
|
||||||
[ 3, -6, 3, 0],
|
[ 3, -6, 3, 0],
|
||||||
[-3, 3, 0, 0],
|
[-3, 3, 0, 0],
|
||||||
[ 1, 0, 0, 0]])
|
[ 1, 0, 0, 0]])
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
numpy
|
numpy
|
||||||
svgwrite
|
svgwrite
|
||||||
|
defusedxml
|
4
setup.py
4
setup.py
|
@ -30,9 +30,9 @@ setup(name='svgpathtools',
|
||||||
download_url='{}/releases/download/{}/svgpathtools-{}-py2.py3-none-any.whl'
|
download_url='{}/releases/download/{}/svgpathtools-{}-py2.py3-none-any.whl'
|
||||||
''.format(GITHUB, VERSION, VERSION),
|
''.format(GITHUB, VERSION, VERSION),
|
||||||
license='MIT',
|
license='MIT',
|
||||||
install_requires=['numpy', 'svgwrite'],
|
install_requires=['numpy', 'svgwrite', 'defusedxml'],
|
||||||
platforms="OS Independent",
|
platforms="OS Independent",
|
||||||
requires=['numpy', 'svgwrite'],
|
requires=['numpy', 'svgwrite', 'defusedxml'],
|
||||||
keywords=['svg', 'svg path', 'svg.path', 'bezier', 'parse svg path', 'display svg'],
|
keywords=['svg', 'svg path', 'svg.path', 'bezier', 'parse svg path', 'display svg'],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Development Status :: 4 - Beta",
|
"Development Status :: 4 - Beta",
|
||||||
|
|
|
@ -31,13 +31,9 @@ def bezier_point(p, t):
|
||||||
Warning: Be concerned about numerical stability when using this function
|
Warning: Be concerned about numerical stability when using this function
|
||||||
with high order curves."""
|
with high order curves."""
|
||||||
|
|
||||||
# begin arc support block ########################
|
# for Arc support
|
||||||
try:
|
if hasattr(p, 'radius'):
|
||||||
p.large_arc
|
|
||||||
return p.point(t)
|
return p.point(t)
|
||||||
except:
|
|
||||||
pass
|
|
||||||
# end arc support block ##########################
|
|
||||||
|
|
||||||
deg = len(p) - 1
|
deg = len(p) - 1
|
||||||
if deg == 3:
|
if deg == 3:
|
||||||
|
@ -145,14 +141,11 @@ def split_bezier(bpoints, t):
|
||||||
|
|
||||||
|
|
||||||
def halve_bezier(p):
|
def halve_bezier(p):
|
||||||
|
"""split path segment into two halves at t=0.5"""
|
||||||
|
|
||||||
# begin arc support block ########################
|
# for Arc support
|
||||||
try:
|
if hasattr(p, 'radius'):
|
||||||
p.large_arc
|
|
||||||
return p.split(0.5)
|
return p.split(0.5)
|
||||||
except:
|
|
||||||
pass
|
|
||||||
# end arc support block ##########################
|
|
||||||
|
|
||||||
if len(p) == 4:
|
if len(p) == 4:
|
||||||
return ([p[0], (p[0] + p[1])/2, (p[0] + 2*p[1] + p[2])/4,
|
return ([p[0], (p[0] + p[1])/2, (p[0] + 2*p[1] + p[2])/4,
|
||||||
|
@ -199,13 +192,9 @@ def bezier_bounding_box(bez):
|
||||||
(xmin, xmax, ymin, ymax).
|
(xmin, xmax, ymin, ymax).
|
||||||
Warning: For the non-cubic case this is not particularly efficient."""
|
Warning: For the non-cubic case this is not particularly efficient."""
|
||||||
|
|
||||||
# begin arc support block ########################
|
# for Arc support
|
||||||
try:
|
if hasattr(bez, 'radius'):
|
||||||
bla = bez.large_arc
|
return bez.bbox()
|
||||||
return bez.bbox() # added to support Arc objects
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
# end arc support block ##########################
|
|
||||||
|
|
||||||
if len(bez) == 4:
|
if len(bez) == 4:
|
||||||
xmin, xmax = bezier_real_minmax([p.real for p in bez])
|
xmin, xmax = bezier_real_minmax([p.real for p in bez])
|
||||||
|
|
|
@ -36,10 +36,15 @@ A Big Problem:
|
||||||
# External dependencies
|
# External dependencies
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import collections
|
import collections
|
||||||
import xml.etree.ElementTree as etree
|
from defusedxml.cElementTree import parse, tostring
|
||||||
from xml.etree.ElementTree import Element, SubElement, register_namespace
|
from xml.etree.cElementTree import register_namespace
|
||||||
from xml.dom.minidom import parseString
|
if sys.version_info.major == 2:
|
||||||
|
from xml.etree.ElementTree import Element, SubElement, ElementTree
|
||||||
|
else:
|
||||||
|
from xml.etree.cElementTree import Element, SubElement, ElementTree
|
||||||
|
from defusedxml.minidom import parseString
|
||||||
import warnings
|
import warnings
|
||||||
from tempfile import gettempdir
|
from tempfile import gettempdir
|
||||||
from time import time
|
from time import time
|
||||||
|
@ -97,9 +102,6 @@ def flattened_paths(group, group_filter=lambda x: True,
|
||||||
only convert explicit path elements, pass in
|
only convert explicit path elements, pass in
|
||||||
`path_conversions=CONVERT_ONLY_PATHS`.
|
`path_conversions=CONVERT_ONLY_PATHS`.
|
||||||
"""
|
"""
|
||||||
if not isinstance(group, Element):
|
|
||||||
raise TypeError('Must provide an xml.etree.Element object. '
|
|
||||||
'Instead you provided {0}'.format(type(group)))
|
|
||||||
|
|
||||||
# Stop right away if the group_selector rejects this group
|
# Stop right away if the group_selector rejects this group
|
||||||
if not group_filter(group):
|
if not group_filter(group):
|
||||||
|
@ -244,10 +246,10 @@ class Document:
|
||||||
self.original_filepath = os.path.join(os.getcwd(), filepath)
|
self.original_filepath = os.path.join(os.getcwd(), filepath)
|
||||||
|
|
||||||
if filepath is None:
|
if filepath is None:
|
||||||
self.tree = etree.ElementTree(Element('svg'))
|
self.tree = ElementTree(Element('svg'))
|
||||||
else:
|
else:
|
||||||
# parse svg to ElementTree object
|
# parse svg to ElementTree object
|
||||||
self.tree = etree.parse(filepath)
|
self.tree = parse(filepath)
|
||||||
|
|
||||||
self.root = self.tree.getroot()
|
self.root = self.tree.getroot()
|
||||||
|
|
||||||
|
@ -416,7 +418,7 @@ class Document:
|
||||||
SVG_NAMESPACE['svg']), group_attribs)
|
SVG_NAMESPACE['svg']), group_attribs)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return etree.tostring(self.tree.getroot()).decode()
|
return tostring(self.tree.getroot()).decode()
|
||||||
|
|
||||||
def pretty(self, **kwargs):
|
def pretty(self, **kwargs):
|
||||||
return parseString(repr(self)).toprettyxml(**kwargs)
|
return parseString(repr(self)).toprettyxml(**kwargs)
|
||||||
|
|
|
@ -920,6 +920,7 @@ class QuadraticBezier(object):
|
||||||
if t0 == 1 and t1 == 0:
|
if t0 == 1 and t1 == 0:
|
||||||
if self._length_info['bpoints'] == self.bpoints():
|
if self._length_info['bpoints'] == self.bpoints():
|
||||||
return self._length_info['length']
|
return self._length_info['length']
|
||||||
|
|
||||||
a = self.start - 2*self.control + self.end
|
a = self.start - 2*self.control + self.end
|
||||||
b = 2*(self.control - self.start)
|
b = 2*(self.control - self.start)
|
||||||
a_dot_b = a.real*b.real + a.imag*b.imag
|
a_dot_b = a.real*b.real + a.imag*b.imag
|
||||||
|
@ -927,20 +928,23 @@ class QuadraticBezier(object):
|
||||||
if abs(a) < 1e-12:
|
if abs(a) < 1e-12:
|
||||||
s = abs(b)*(t1 - t0)
|
s = abs(b)*(t1 - t0)
|
||||||
else:
|
else:
|
||||||
c2 = 4 * (a.real ** 2 + a.imag ** 2)
|
with np.testing.suppress_warnings() as sup:
|
||||||
c1 = 4 * a_dot_b
|
sup.filter(RuntimeWarning)
|
||||||
c0 = b.real ** 2 + b.imag ** 2
|
c2 = 4 * (a.real ** 2 + a.imag ** 2)
|
||||||
|
c1 = 4 * a_dot_b
|
||||||
|
c0 = b.real ** 2 + b.imag ** 2
|
||||||
|
|
||||||
beta = c1 / (2 * c2)
|
beta = c1 / (2 * c2)
|
||||||
gamma = c0 / c2 - beta ** 2
|
gamma = c0 / c2 - beta ** 2
|
||||||
|
|
||||||
|
dq1_mag = sqrt(c2 * t1 ** 2 + c1 * t1 + c0)
|
||||||
|
dq0_mag = sqrt(c2 * t0 ** 2 + c1 * t0 + c0)
|
||||||
|
logarand = (sqrt(c2) * (t1 + beta) + dq1_mag) / \
|
||||||
|
(sqrt(c2) * (t0 + beta) + dq0_mag)
|
||||||
|
s = (t1 + beta) * dq1_mag - (t0 + beta) * dq0_mag + \
|
||||||
|
gamma * sqrt(c2) * log(logarand)
|
||||||
|
s /= 2
|
||||||
|
|
||||||
dq1_mag = sqrt(c2 * t1 ** 2 + c1 * t1 + c0)
|
|
||||||
dq0_mag = sqrt(c2 * t0 ** 2 + c1 * t0 + c0)
|
|
||||||
logarand = (sqrt(c2) * (t1 + beta) + dq1_mag) / \
|
|
||||||
(sqrt(c2) * (t0 + beta) + dq0_mag)
|
|
||||||
s = (t1 + beta) * dq1_mag - (t0 + beta) * dq0_mag + \
|
|
||||||
gamma * sqrt(c2) * log(logarand)
|
|
||||||
s /= 2
|
|
||||||
if isnan(s):
|
if isnan(s):
|
||||||
tstar = abs(b) / (2 * abs(a))
|
tstar = abs(b) / (2 * abs(a))
|
||||||
if t1 < tstar:
|
if t1 < tstar:
|
||||||
|
|
|
@ -8,7 +8,7 @@ from __future__ import division, absolute_import, print_function
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from os import path as os_path, makedirs
|
from os import path as os_path, makedirs
|
||||||
from tempfile import gettempdir
|
from tempfile import gettempdir
|
||||||
from xml.dom.minidom import parse as md_xml_parse
|
from defusedxml.minidom import parse as md_xml_parse
|
||||||
from svgwrite import Drawing, text as txt
|
from svgwrite import Drawing, text as txt
|
||||||
from time import time
|
from time import time
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
|
@ -10,19 +10,26 @@ from .misctools import isclose
|
||||||
|
|
||||||
|
|
||||||
def polyroots(p, realroots=False, condition=lambda r: True):
|
def polyroots(p, realroots=False, condition=lambda r: True):
|
||||||
|
"""Returns the roots of a polynomial with coefficients given in p.
|
||||||
|
|
||||||
|
p[0] * x**n + p[1] * x**(n-1) + ... + p[n-1]*x + p[n]
|
||||||
|
|
||||||
|
Args:
|
||||||
|
p: 1D array-like object of polynomial coefficients.
|
||||||
|
realroots: a boolean. If true, only real roots will be returned
|
||||||
|
and the condition function can be written assuming all roots
|
||||||
|
are real.
|
||||||
|
condition: a boolean-valued function. Only roots satisfying
|
||||||
|
this will be returned. If realroots==True, these conditions
|
||||||
|
should assume the roots are real.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(list) A list containing the roots of the polynomial.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
* This uses np.isclose and np.roots
|
||||||
"""
|
"""
|
||||||
Returns the roots of a polynomial with coefficients given in p.
|
|
||||||
p[0] * x**n + p[1] * x**(n-1) + ... + p[n-1]*x + p[n]
|
|
||||||
INPUT:
|
|
||||||
p - Rank-1 array-like object of polynomial coefficients.
|
|
||||||
realroots - a boolean. If true, only real roots will be returned and the
|
|
||||||
condition function can be written assuming all roots are real.
|
|
||||||
condition - a boolean-valued function. Only roots satisfying this will be
|
|
||||||
returned. If realroots==True, these conditions should assume the roots
|
|
||||||
are real.
|
|
||||||
OUTPUT:
|
|
||||||
A list containing the roots of the polynomial.
|
|
||||||
NOTE: This uses np.isclose and np.roots"""
|
|
||||||
roots = np.roots(p)
|
roots = np.roots(p)
|
||||||
if realroots:
|
if realroots:
|
||||||
roots = [r.real for r in roots if isclose(r.imag, 0)]
|
roots = [r.real for r in roots if isclose(r.imag, 0)]
|
||||||
|
@ -36,16 +43,18 @@ def polyroots(p, realroots=False, condition=lambda r: True):
|
||||||
|
|
||||||
|
|
||||||
def polyroots01(p):
|
def polyroots01(p):
|
||||||
"""Returns the real roots between 0 and 1 of the polynomial with
|
"""Returns the real roots 0 < x < 1 of the polynomial given by `p`.
|
||||||
coefficients given in p,
|
|
||||||
p[0] * x**n + p[1] * x**(n-1) + ... + p[n-1]*x + p[n]
|
p[0] * x**n + p[1] * x**(n-1) + ... + p[n-1]*x + p[n]
|
||||||
p can also be a np.poly1d object. See polyroots for more information."""
|
|
||||||
|
Notes:
|
||||||
|
p can also be a np.poly1d object. See polyroots for more information.
|
||||||
|
"""
|
||||||
return polyroots(p, realroots=True, condition=lambda tval: 0 <= tval <= 1)
|
return polyroots(p, realroots=True, condition=lambda tval: 0 <= tval <= 1)
|
||||||
|
|
||||||
|
|
||||||
def rational_limit(f, g, t0):
|
def rational_limit(f, g, t0):
|
||||||
"""Computes the limit of the rational function (f/g)(t)
|
"""Computes the limit of the rational function (f/g)(t) as t approaches t0."""
|
||||||
as t approaches t0."""
|
|
||||||
assert isinstance(f, np.poly1d) and isinstance(g, np.poly1d)
|
assert isinstance(f, np.poly1d) and isinstance(g, np.poly1d)
|
||||||
assert g != np.poly1d([0])
|
assert g != np.poly1d([0])
|
||||||
if g(t0) != 0:
|
if g(t0) != 0:
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
# External dependencies
|
# External dependencies
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
import os
|
import os
|
||||||
from xml.etree.ElementTree import iterparse, Element, ElementTree, SubElement
|
from xml.etree.cElementTree import Element, ElementTree, SubElement
|
||||||
|
from defusedxml.cElementTree import iterparse
|
||||||
|
|
||||||
# Internal dependencies
|
# Internal dependencies
|
||||||
from .parser import parse_path
|
from .parser import parse_path
|
||||||
|
|
|
@ -3,7 +3,7 @@ The main tool being the svg2paths() function."""
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
from xml.dom.minidom import parse
|
from defusedxml.minidom import parse
|
||||||
from os import path as os_path, getcwd
|
from os import path as os_path, getcwd
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
@ -17,9 +17,11 @@ COORD_PAIR_TMPLT = re.compile(
|
||||||
r'([\+-]?\d*[\.\d]\d*[eE][\+-]?\d+|[\+-]?\d*[\.\d]\d*)'
|
r'([\+-]?\d*[\.\d]\d*[eE][\+-]?\d+|[\+-]?\d*[\.\d]\d*)'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def path2pathd(path):
|
def path2pathd(path):
|
||||||
return path.get('d', '')
|
return path.get('d', '')
|
||||||
|
|
||||||
|
|
||||||
def ellipse2pathd(ellipse):
|
def ellipse2pathd(ellipse):
|
||||||
"""converts the parameters from an ellipse or a circle to a string for a
|
"""converts the parameters from an ellipse or a circle to a string for a
|
||||||
Path object d-attribute"""
|
Path object d-attribute"""
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import unittest
|
import unittest
|
||||||
from svgpathtools.bezier import *
|
from svgpathtools.bezier import bezier_point, bezier2polynomial, polynomial2bezier
|
||||||
from svgpathtools.path import bpoints2bezier
|
from svgpathtools.path import bpoints2bezier
|
||||||
|
|
||||||
|
|
||||||
class HigherOrderBezier:
|
class HigherOrderBezier:
|
||||||
|
"""To help test Bezier curves of arbitrary degree"""
|
||||||
def __init__(self, bpoints):
|
def __init__(self, bpoints):
|
||||||
self.bpts = bpoints
|
self.bpts = bpoints
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# Note: This file was taken mostly as is from the svg.path module (v 2.0)
|
"""credit: This was modified from a file in the svg.path module (v 2.0)"""
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
import unittest
|
import unittest
|
||||||
from svgpathtools import *
|
from svgpathtools import parse_path
|
||||||
|
|
||||||
|
|
||||||
class TestGeneration(unittest.TestCase):
|
class TestGeneration(unittest.TestCase):
|
||||||
|
|
|
@ -5,7 +5,7 @@ $ python -m unittest test.test_groups.TestGroups.test_group_flatten
|
||||||
"""
|
"""
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
import unittest
|
import unittest
|
||||||
from svgpathtools import *
|
from svgpathtools import Document, SVG_NAMESPACE, parse_path
|
||||||
from os.path import join, dirname
|
from os.path import join, dirname
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Note: This file was taken mostly as is from the svg.path module (v 2.0)
|
# Note: This file was taken mostly as is from the svg.path module (v 2.0)
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
import unittest
|
import unittest
|
||||||
from svgpathtools import *
|
from svgpathtools import parse_path, Path, Line, QuadraticBezier, CubicBezier, Arc
|
||||||
import svgpathtools
|
import svgpathtools
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
# External dependencies
|
# External dependencies
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
import os
|
from unittest import TestCase
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
from math import sqrt, pi
|
from math import sqrt, pi
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -10,8 +8,11 @@ import random
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
# Internal dependencies
|
# Internal dependencies
|
||||||
from svgpathtools import *
|
from svgpathtools import (
|
||||||
from svgpathtools.path import _NotImplemented4ArcException, bezier_radialrange
|
Line, QuadraticBezier, CubicBezier, Arc, Path, parse_path,
|
||||||
|
is_bezier_segment, is_bezier_path, poly2bez, bpoints2bezier,
|
||||||
|
closest_point_in_path, farthest_point_in_path, path_encloses_pt)
|
||||||
|
from svgpathtools.path import bezier_radialrange
|
||||||
|
|
||||||
# An important note for those doing any debugging:
|
# An important note for those doing any debugging:
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
|
@ -66,7 +67,25 @@ def assert_intersections(test_case, a_seg, b_seg, intersections, count, msg=None
|
||||||
test_case.assertAlmostEqual(a_seg.point(i[0]), b_seg.point(i[1]), msg=msg, delta=tol)
|
test_case.assertAlmostEqual(a_seg.point(i[0]), b_seg.point(i[1]), msg=msg, delta=tol)
|
||||||
|
|
||||||
|
|
||||||
class LineTest(unittest.TestCase):
|
class AssertWarns(warnings.catch_warnings):
|
||||||
|
"""A python 2 compatible version of assertWarns."""
|
||||||
|
def __init__(self, test_case, warning):
|
||||||
|
self.test_case = test_case
|
||||||
|
self.warning_type = warning
|
||||||
|
self.log = None
|
||||||
|
super(AssertWarns, self).__init__(record=True, module=None)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.log = super(AssertWarns, self).__enter__()
|
||||||
|
return self.log
|
||||||
|
|
||||||
|
def __exit__(self, *exc_info):
|
||||||
|
super(AssertWarns, self).__exit__(*exc_info)
|
||||||
|
self.test_case.assertEqual(type(self.log[0]), self.warning_type)
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyTypeChecker
|
||||||
|
class LineTest(TestCase):
|
||||||
|
|
||||||
def test_lines(self):
|
def test_lines(self):
|
||||||
# These points are calculated, and not just regression tests.
|
# These points are calculated, and not just regression tests.
|
||||||
|
@ -160,9 +179,9 @@ class LineTest(unittest.TestCase):
|
||||||
self.assertIsNone(l.point_to_t(-0.001-0j))
|
self.assertIsNone(l.point_to_t(-0.001-0j))
|
||||||
|
|
||||||
random.seed()
|
random.seed()
|
||||||
for line_index in range(100):
|
for _ in range(100):
|
||||||
l = random_line()
|
l = random_line()
|
||||||
for t_index in range(100):
|
for __ in range(100):
|
||||||
orig_t = random.random()
|
orig_t = random.random()
|
||||||
p = l.point(orig_t)
|
p = l.point(orig_t)
|
||||||
computed_t = l.point_to_t(p)
|
computed_t = l.point_to_t(p)
|
||||||
|
@ -183,7 +202,8 @@ class LineTest(unittest.TestCase):
|
||||||
self.assertAlmostEqual(max_ta, max_tb, delta=TOL)
|
self.assertAlmostEqual(max_ta, max_tb, delta=TOL)
|
||||||
|
|
||||||
|
|
||||||
class CubicBezierTest(unittest.TestCase):
|
# noinspection PyTypeChecker
|
||||||
|
class CubicBezierTest(TestCase):
|
||||||
def test_approx_circle(self):
|
def test_approx_circle(self):
|
||||||
"""This is a approximate circle drawn in Inkscape"""
|
"""This is a approximate circle drawn in Inkscape"""
|
||||||
|
|
||||||
|
@ -419,14 +439,13 @@ class CubicBezierTest(unittest.TestCase):
|
||||||
segment = CubicBezier(complex(600, 500), complex(600, 350),
|
segment = CubicBezier(complex(600, 500), complex(600, 350),
|
||||||
complex(900, 650), complex(900, 500))
|
complex(900, 650), complex(900, 500))
|
||||||
|
|
||||||
self.assertTrue(segment ==
|
self.assertTrue(segment == CubicBezier(600 + 500j, 600 + 350j, 900 + 650j, 900 + 500j))
|
||||||
CubicBezier(600 + 500j, 600 + 350j, 900 + 650j, 900 + 500j))
|
self.assertTrue(segment != CubicBezier(600 + 501j, 600 + 350j, 900 + 650j, 900 + 500j))
|
||||||
self.assertTrue(segment !=
|
|
||||||
CubicBezier(600 + 501j, 600 + 350j, 900 + 650j, 900 + 500j))
|
|
||||||
self.assertTrue(segment != Line(0, 400))
|
self.assertTrue(segment != Line(0, 400))
|
||||||
|
|
||||||
|
|
||||||
class QuadraticBezierTest(unittest.TestCase):
|
# noinspection PyTypeChecker
|
||||||
|
class QuadraticBezierTest(TestCase):
|
||||||
|
|
||||||
def test_svg_examples(self):
|
def test_svg_examples(self):
|
||||||
"""These is the path in the SVG specs"""
|
"""These is the path in the SVG specs"""
|
||||||
|
@ -495,25 +514,24 @@ class QuadraticBezierTest(unittest.TestCase):
|
||||||
# This is to test the __eq__ and __ne__ methods, so we can't use
|
# This is to test the __eq__ and __ne__ methods, so we can't use
|
||||||
# assertEqual and assertNotEqual
|
# assertEqual and assertNotEqual
|
||||||
segment = QuadraticBezier(200 + 300j, 400 + 50j, 600 + 300j)
|
segment = QuadraticBezier(200 + 300j, 400 + 50j, 600 + 300j)
|
||||||
self.assertTrue(segment ==
|
self.assertTrue(segment == QuadraticBezier(200 + 300j, 400 + 50j, 600 + 300j))
|
||||||
QuadraticBezier(200 + 300j, 400 + 50j, 600 + 300j))
|
self.assertTrue(segment != QuadraticBezier(200 + 301j, 400 + 50j, 600 + 300j))
|
||||||
self.assertTrue(segment !=
|
|
||||||
QuadraticBezier(200 + 301j, 400 + 50j, 600 + 300j))
|
|
||||||
self.assertFalse(segment == Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j))
|
self.assertFalse(segment == Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j))
|
||||||
self.assertTrue(Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j) != segment)
|
self.assertTrue(Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j) != segment)
|
||||||
|
|
||||||
|
|
||||||
class ArcTest(unittest.TestCase):
|
# noinspection PyTypeChecker
|
||||||
|
class ArcTest(TestCase):
|
||||||
|
|
||||||
def test_trusting_acos(self):
|
def test_trusting_acos(self):
|
||||||
"""`u1.real` is > 1 in this arc due to numerical error."""
|
"""`u1.real` is > 1 in this arc due to numerical error."""
|
||||||
try:
|
try:
|
||||||
a1 = Arc(start=(160.197+102.925j),
|
_ = Arc(start=(160.197+102.925j),
|
||||||
radius=(0.025+0.025j),
|
radius=(0.025+0.025j),
|
||||||
rotation=0.0,
|
rotation=0.0,
|
||||||
large_arc=False,
|
large_arc=False,
|
||||||
sweep=True,
|
sweep=True,
|
||||||
end=(160.172+102.95j))
|
end=(160.172+102.95j))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Arc() raised ValueError unexpectedly!")
|
self.fail("Arc() raised ValueError unexpectedly!")
|
||||||
|
|
||||||
|
@ -680,9 +698,9 @@ class ArcTest(unittest.TestCase):
|
||||||
self.assertIsNone(a.point_to_t(730.5212132777968+171j))
|
self.assertIsNone(a.point_to_t(730.5212132777968+171j))
|
||||||
|
|
||||||
random.seed()
|
random.seed()
|
||||||
for arc_index in range(100):
|
for _ in range(100):
|
||||||
a = random_arc()
|
a = random_arc()
|
||||||
for t_index in np.linspace(0, 1, 100):
|
for __ in np.linspace(0, 1, 100):
|
||||||
orig_t = random.random()
|
orig_t = random.random()
|
||||||
p = a.point(orig_t)
|
p = a.point(orig_t)
|
||||||
computed_t = a.point_to_t(p)
|
computed_t = a.point_to_t(p)
|
||||||
|
@ -692,7 +710,7 @@ class ArcTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_approx_quad(self):
|
def test_approx_quad(self):
|
||||||
n = 100
|
n = 100
|
||||||
for i in range(n):
|
for _ in range(n):
|
||||||
arc = random_arc()
|
arc = random_arc()
|
||||||
if arc.radius.real > 2000 or arc.radius.imag > 2000:
|
if arc.radius.real > 2000 or arc.radius.imag > 2000:
|
||||||
continue # Random Arc too large, by autoscale.
|
continue # Random Arc too large, by autoscale.
|
||||||
|
@ -705,7 +723,7 @@ class ArcTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_approx_cubic(self):
|
def test_approx_cubic(self):
|
||||||
n = 100
|
n = 100
|
||||||
for i in range(n):
|
for _ in range(n):
|
||||||
arc = random_arc()
|
arc = random_arc()
|
||||||
if arc.radius.real > 2000 or arc.radius.imag > 2000:
|
if arc.radius.real > 2000 or arc.radius.imag > 2000:
|
||||||
continue # Random Arc too large, by autoscale.
|
continue # Random Arc too large, by autoscale.
|
||||||
|
@ -717,7 +735,8 @@ class ArcTest(unittest.TestCase):
|
||||||
self.assertAlmostEqual(d, 0.0, delta=2)
|
self.assertAlmostEqual(d, 0.0, delta=2)
|
||||||
|
|
||||||
|
|
||||||
class TestPath(unittest.TestCase):
|
# noinspection PyTypeChecker
|
||||||
|
class TestPath(TestCase):
|
||||||
|
|
||||||
# def test_hash(self):
|
# def test_hash(self):
|
||||||
# line1 = Line(600.5 + 350.5j, 650.5 + 325.5j)
|
# line1 = Line(600.5 + 350.5j, 650.5 + 325.5j)
|
||||||
|
@ -810,8 +829,7 @@ class TestPath(unittest.TestCase):
|
||||||
# regression tests.
|
# regression tests.
|
||||||
self.assertAlmostEqual(path.point(0.0), (275 + 175j), delta=TOL)
|
self.assertAlmostEqual(path.point(0.0), (275 + 175j), delta=TOL)
|
||||||
self.assertAlmostEqual(path.point(0.2800495767557787), (275 + 25j), delta=TOL)
|
self.assertAlmostEqual(path.point(0.2800495767557787), (275 + 25j), delta=TOL)
|
||||||
self.assertAlmostEqual(path.point(0.5),
|
self.assertAlmostEqual(path.point(0.5), (168.93398282201787 + 68.93398282201787j))
|
||||||
(168.93398282201787 + 68.93398282201787j))
|
|
||||||
self.assertAlmostEqual(path.point(1 - 0.2800495767557787), (125 + 175j), delta=TOL)
|
self.assertAlmostEqual(path.point(1 - 0.2800495767557787), (125 + 175j), delta=TOL)
|
||||||
self.assertAlmostEqual(path.point(1.0), (275 + 175j), delta=TOL)
|
self.assertAlmostEqual(path.point(1.0), (275 + 175j), delta=TOL)
|
||||||
# The errors seem to accumulate. Still 6 decimal places is more
|
# The errors seem to accumulate. Still 6 decimal places is more
|
||||||
|
@ -852,7 +870,7 @@ class TestPath(unittest.TestCase):
|
||||||
Arc(start=650 + 325j, radius=25 + 25j, rotation=-30,
|
Arc(start=650 + 325j, radius=25 + 25j, rotation=-30,
|
||||||
large_arc=0, sweep=1, end=700 + 300j),
|
large_arc=0, sweep=1, end=700 + 300j),
|
||||||
CubicBezier(start=700 + 300j, control1=800 + 400j,
|
CubicBezier(start=700 + 300j, control1=800 + 400j,
|
||||||
control2=750 + 200j, end=600 + 100j),
|
control2=750 + 200j, end=600 + 100j),
|
||||||
QuadraticBezier(start=600 + 100j, control=600, end=600 + 300j))
|
QuadraticBezier(start=600 + 100j, control=600, end=600 + 300j))
|
||||||
self.assertEqual(eval(repr(path)), path)
|
self.assertEqual(eval(repr(path)), path)
|
||||||
|
|
||||||
|
@ -864,14 +882,14 @@ class TestPath(unittest.TestCase):
|
||||||
Arc(start=650 + 325j, radius=25 + 25j, rotation=-30,
|
Arc(start=650 + 325j, radius=25 + 25j, rotation=-30,
|
||||||
large_arc=0, sweep=1, end=700 + 300j),
|
large_arc=0, sweep=1, end=700 + 300j),
|
||||||
CubicBezier(start=700 + 300j, control1=800 + 400j,
|
CubicBezier(start=700 + 300j, control1=800 + 400j,
|
||||||
control2=750 + 200j, end=600 + 100j),
|
control2=750 + 200j, end=600 + 100j),
|
||||||
QuadraticBezier(start=600 + 100j, control=600, end=600 + 300j))
|
QuadraticBezier(start=600 + 100j, control=600, end=600 + 300j))
|
||||||
path2 = Path(
|
path2 = Path(
|
||||||
Line(start=600 + 350j, end=650 + 325j),
|
Line(start=600 + 350j, end=650 + 325j),
|
||||||
Arc(start=650 + 325j, radius=25 + 25j, rotation=-30,
|
Arc(start=650 + 325j, radius=25 + 25j, rotation=-30,
|
||||||
large_arc=0, sweep=1, end=700 + 300j),
|
large_arc=0, sweep=1, end=700 + 300j),
|
||||||
CubicBezier(start=700 + 300j, control1=800 + 400j,
|
CubicBezier(start=700 + 300j, control1=800 + 400j,
|
||||||
control2=750 + 200j, end=600 + 100j),
|
control2=750 + 200j, end=600 + 100j),
|
||||||
QuadraticBezier(start=600 + 100j, control=600, end=600 + 300j))
|
QuadraticBezier(start=600 + 100j, control=600, end=600 + 300j))
|
||||||
|
|
||||||
self.assertTrue(path1 == path2)
|
self.assertTrue(path1 == path2)
|
||||||
|
@ -1041,17 +1059,17 @@ class TestPath(unittest.TestCase):
|
||||||
test_curves = [bezpath, bezpathz, path, pathz, lpath, qpath, cpath,
|
test_curves = [bezpath, bezpathz, path, pathz, lpath, qpath, cpath,
|
||||||
apath, line1, arc1, arc2, cub1, cub2, quad3, linez]
|
apath, line1, arc1, arc2, cub1, cub2, quad3, linez]
|
||||||
|
|
||||||
def scale_a_point(pt, sx, sy=None, origin=0j):
|
def scale_a_point(pt_, sx_, sy_=None, origin_=0j):
|
||||||
|
|
||||||
if sy is None:
|
if sy_ is None:
|
||||||
sy = sx
|
sy_ = sx_
|
||||||
|
|
||||||
zeta = pt - origin
|
zeta = pt_ - origin_
|
||||||
pt_vec = [[zeta.real],
|
pt_vec = [[zeta.real],
|
||||||
[zeta.imag],
|
[zeta.imag],
|
||||||
[1]]
|
[1]]
|
||||||
transform = [[sx, 0, origin.real],
|
transform = [[sx_, 0, origin_.real],
|
||||||
[0, sy, origin.imag]]
|
[0, sy_, origin_.imag]]
|
||||||
|
|
||||||
return complex(*np.dot(transform, pt_vec).ravel())
|
return complex(*np.dot(transform, pt_vec).ravel())
|
||||||
|
|
||||||
|
@ -1075,6 +1093,8 @@ class TestPath(unittest.TestCase):
|
||||||
|
|
||||||
# find seg which t lands on for failure reporting
|
# find seg which t lands on for failure reporting
|
||||||
seg = curve
|
seg = curve
|
||||||
|
seg_idx = None
|
||||||
|
seg_t = None
|
||||||
if isinstance(curve, Path):
|
if isinstance(curve, Path):
|
||||||
seg_idx, seg_t = curve.T2t(t)
|
seg_idx, seg_t = curve.T2t(t)
|
||||||
seg = curve[seg_idx]
|
seg = curve[seg_idx]
|
||||||
|
@ -1113,7 +1133,7 @@ class TestPath(unittest.TestCase):
|
||||||
curve.scaled(sx, sy).point(t)
|
curve.scaled(sx, sy).point(t)
|
||||||
else:
|
else:
|
||||||
curve_scaled = curve.scaled(sx, sy)
|
curve_scaled = curve.scaled(sx, sy)
|
||||||
seg_scaled = seg.scaled(sx, sy)
|
_ = seg.scaled(sx, sy)
|
||||||
if isinstance(curve, Path):
|
if isinstance(curve, Path):
|
||||||
res = curve_scaled[seg_idx].point(seg_t)
|
res = curve_scaled[seg_idx].point(seg_t)
|
||||||
else:
|
else:
|
||||||
|
@ -1187,20 +1207,21 @@ class TestPath(unittest.TestCase):
|
||||||
self.assertEqual(path2.d(use_closed_attrib=True, rel=True), rel_s)
|
self.assertEqual(path2.d(use_closed_attrib=True, rel=True), rel_s)
|
||||||
|
|
||||||
|
|
||||||
class Test_ilength(unittest.TestCase):
|
# noinspection PyTypeChecker
|
||||||
|
class Test_ilength(TestCase):
|
||||||
# See svgpathtools.notes.inv_arclength.py for information on how these
|
# See svgpathtools.notes.inv_arclength.py for information on how these
|
||||||
# test values were generated (using the .length() method).
|
# test values were generated (using the .length() method).
|
||||||
##############################################################
|
##############################################################
|
||||||
|
|
||||||
def test_ilength_lines(self):
|
def test_ilength_lines(self):
|
||||||
l = Line(1, 3-1j)
|
l = Line(1, 3-1j)
|
||||||
nodall = Line(1+1j, 1+1j)
|
# nodall = Line(1+1j, 1+1j)
|
||||||
|
|
||||||
tests = [(l, 0.01, 0.022360679774997897),
|
tests = [(l, 0.01, 0.022360679774997897),
|
||||||
(l, 0.1, 0.223606797749979),
|
(l, 0.1, 0.223606797749979),
|
||||||
(l, 0.5, 1.118033988749895),
|
(l, 0.5, 1.118033988749895),
|
||||||
(l, 0.9, 2.012461179749811),
|
(l, 0.9, 2.012461179749811),
|
||||||
(l, 0.99, 2.213707297724792)]
|
(l, 0.99, 2.213707297724792)]
|
||||||
|
|
||||||
for (l, t, s) in tests:
|
for (l, t, s) in tests:
|
||||||
self.assertAlmostEqual(l.ilength(s), t, delta=TOL)
|
self.assertAlmostEqual(l.ilength(s), t, delta=TOL)
|
||||||
|
@ -1210,37 +1231,31 @@ class Test_ilength(unittest.TestCase):
|
||||||
q2 = QuadraticBezier(200 + 300j, 400 + 50j, 500 + 200j)
|
q2 = QuadraticBezier(200 + 300j, 400 + 50j, 500 + 200j)
|
||||||
closedq = QuadraticBezier(6 + 2j, 5 - 1j, 6 + 2j)
|
closedq = QuadraticBezier(6 + 2j, 5 - 1j, 6 + 2j)
|
||||||
linq = QuadraticBezier(1+3j, 2+5j, -9 - 17j)
|
linq = QuadraticBezier(1+3j, 2+5j, -9 - 17j)
|
||||||
nodalq = QuadraticBezier(1, 1, 1)
|
# nodalq = QuadraticBezier(1, 1, 1)
|
||||||
|
|
||||||
tests = [(q1, 0.01, 6.364183310105577),
|
tests = [(q1, 0.01, 6.364183310105577),
|
||||||
(q1, 0.1, 60.23857499635088),
|
(q1, 0.1, 60.23857499635088),
|
||||||
(q1, 0.5, 243.8855469477619),
|
(q1, 0.5, 243.8855469477619),
|
||||||
(q1, 0.9, 427.53251889917294),
|
(q1, 0.9, 427.53251889917294),
|
||||||
(q1, 0.99, 481.40691058541813),
|
(q1, 0.99, 481.40691058541813),
|
||||||
(q2, 0.01, 6.365673533661836),
|
(q2, 0.01, 6.365673533661836),
|
||||||
(q2, 0.1, 60.31675895732397),
|
(q2, 0.1, 60.31675895732397),
|
||||||
(q2, 0.5, 233.24592830045907),
|
(q2, 0.5, 233.24592830045907),
|
||||||
(q2, 0.9, 346.42891253298706),
|
(q2, 0.9, 346.42891253298706),
|
||||||
(q2, 0.99, 376.32659156736844),
|
(q2, 0.99, 376.32659156736844),
|
||||||
(closedq, 0.01, 0.06261309767133393),
|
(closedq, 0.01, 0.06261309767133393),
|
||||||
(closedq, 0.1, 0.5692099788303084),
|
(closedq, 0.1, 0.5692099788303084),
|
||||||
(closedq, 0.5, 1.5811388300841898),
|
(closedq, 0.5, 1.5811388300841898),
|
||||||
(closedq, 0.9, 2.5930676813380713),
|
(closedq, 0.9, 2.5930676813380713),
|
||||||
(closedq, 0.99, 3.0996645624970456),
|
(closedq, 0.99, 3.0996645624970456),
|
||||||
(linq, 0.01, 0.04203807797699605),
|
(linq, 0.01, 0.04203807797699605),
|
||||||
(linq, 0.1, 0.19379255804998186),
|
(linq, 0.1, 0.19379255804998186),
|
||||||
(linq, 0.5, 4.844813951249544),
|
(linq, 0.5, 4.844813951249544),
|
||||||
(linq, 0.9, 18.0823363780483),
|
(linq, 0.9, 18.0823363780483),
|
||||||
(linq, 0.99, 22.24410609777091)]
|
(linq, 0.99, 22.24410609777091)]
|
||||||
|
|
||||||
for q, t, s in tests:
|
for q, t, s in tests:
|
||||||
try:
|
self.assertAlmostEqual(q.ilength(s), t, delta=TOL)
|
||||||
self.assertAlmostEqual(q.ilength(s), t, delta=TOL)
|
|
||||||
except:
|
|
||||||
print(q)
|
|
||||||
print(s)
|
|
||||||
print(t)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def test_ilength_cubics(self):
|
def test_ilength_cubics(self):
|
||||||
c1 = CubicBezier(200 + 300j, 400 + 50j, 600+100j, -200)
|
c1 = CubicBezier(200 + 300j, 400 + 50j, 600+100j, -200)
|
||||||
|
@ -1415,7 +1430,7 @@ class Test_ilength(unittest.TestCase):
|
||||||
for (c, t, s) in tests:
|
for (c, t, s) in tests:
|
||||||
try:
|
try:
|
||||||
self.assertAlmostEqual(c.ilength(s), t, msg=str((c, t, s)), delta=TOL)
|
self.assertAlmostEqual(c.ilength(s), t, msg=str((c, t, s)), delta=TOL)
|
||||||
except:
|
except ValueError:
|
||||||
# These test case values were generated using a system
|
# These test case values were generated using a system
|
||||||
# with scipy installed -- if scipy is not installed,
|
# with scipy installed -- if scipy is not installed,
|
||||||
# then in cases where `t == 1`, `s` may be slightly
|
# then in cases where `t == 1`, `s` may be slightly
|
||||||
|
@ -1438,7 +1453,8 @@ class Test_ilength(unittest.TestCase):
|
||||||
lin.ilength(1)
|
lin.ilength(1)
|
||||||
|
|
||||||
|
|
||||||
class Test_intersect(unittest.TestCase):
|
# noinspection PyTypeChecker
|
||||||
|
class Test_intersect(TestCase):
|
||||||
def test_intersect(self):
|
def test_intersect(self):
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
|
@ -1579,9 +1595,9 @@ class Test_intersect(unittest.TestCase):
|
||||||
assert_intersections(self, a, l, intersections, 0)
|
assert_intersections(self, a, l, intersections, 0)
|
||||||
|
|
||||||
random.seed()
|
random.seed()
|
||||||
for arc_index in range(50):
|
for _ in range(50):
|
||||||
a = random_arc()
|
a = random_arc()
|
||||||
for line_index in range(100):
|
for __ in range(100):
|
||||||
l = random_line()
|
l = random_line()
|
||||||
intersections = a.intersect(l)
|
intersections = a.intersect(l)
|
||||||
msg = 'Generated: arc = {}, line = {}'.format(a, l)
|
msg = 'Generated: arc = {}, line = {}'.format(a, l)
|
||||||
|
@ -1738,7 +1754,8 @@ class Test_intersect(unittest.TestCase):
|
||||||
assert_intersections(self, a0, a1, intersections, 0)
|
assert_intersections(self, a0, a1, intersections, 0)
|
||||||
|
|
||||||
|
|
||||||
class TestPathTools(unittest.TestCase):
|
# noinspection PyTypeChecker
|
||||||
|
class TestPathTools(TestCase):
|
||||||
# moved from test_pathtools.py
|
# moved from test_pathtools.py
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -1973,7 +1990,7 @@ class TestPathTools(unittest.TestCase):
|
||||||
|
|
||||||
def test_path_area(self):
|
def test_path_area(self):
|
||||||
if not RUN_SLOW_TESTS:
|
if not RUN_SLOW_TESTS:
|
||||||
warnings.warn("Skipping `test_path_area` as RUN_SLOW_TESTS is false.")
|
# warnings.warn("Skipping `test_path_area` as RUN_SLOW_TESTS is false.")
|
||||||
return
|
return
|
||||||
cw_square = Path()
|
cw_square = Path()
|
||||||
cw_square.append(Line((0+0j), (0+100j)))
|
cw_square.append(Line((0+0j), (0+100j)))
|
||||||
|
@ -2029,7 +2046,8 @@ class TestPathTools(unittest.TestCase):
|
||||||
self.assertTrue(enclosing_shape.is_contained_by(larger_shape))
|
self.assertTrue(enclosing_shape.is_contained_by(larger_shape))
|
||||||
|
|
||||||
|
|
||||||
class TestPathBugs(unittest.TestCase):
|
# noinspection PyTypeChecker
|
||||||
|
class TestPathBugs(TestCase):
|
||||||
|
|
||||||
def test_issue_113(self):
|
def test_issue_113(self):
|
||||||
"""
|
"""
|
||||||
|
@ -2053,9 +2071,21 @@ class TestPathBugs(unittest.TestCase):
|
||||||
self.assertAlmostEqual(p.length(), 236.70287281737836, delta=TOL)
|
self.assertAlmostEqual(p.length(), 236.70287281737836, delta=TOL)
|
||||||
|
|
||||||
def test_issue_71(self):
|
def test_issue_71(self):
|
||||||
p = Path("M327 468z")
|
"""Test that degenerate (point-like) paths behave properly."""
|
||||||
m = p.closed
|
# degenerate (point-like) closed path
|
||||||
q = p.d() # Failing to Crash is good.
|
d_string = "M327 468z"
|
||||||
|
path = Path(d_string)
|
||||||
|
|
||||||
|
warning_type = warnings.WarningMessage
|
||||||
|
with AssertWarns(self, warning_type):
|
||||||
|
self.assertTrue(path.closed)
|
||||||
|
|
||||||
|
# test the Path.d() method reproduces an empty d-string
|
||||||
|
# note that ideally this would reproduce the original, but
|
||||||
|
# as a Path is a sequence of Bezier segments and arcs, and this
|
||||||
|
# d-string contains no Bezier segments or arcs, this output seems
|
||||||
|
# like an acceptable compromise
|
||||||
|
self.assertEqual(path.d(), '')
|
||||||
|
|
||||||
def test_issue_95(self):
|
def test_issue_95(self):
|
||||||
"""
|
"""
|
||||||
|
@ -2074,4 +2104,5 @@ class TestPathBugs(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
from unittest import main
|
||||||
|
main()
|
||||||
|
|
|
@ -4,7 +4,7 @@ import unittest
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
# Internal dependencies
|
# Internal dependencies
|
||||||
from svgpathtools import *
|
from svgpathtools import rational_limit
|
||||||
|
|
||||||
|
|
||||||
class Test_polytools(unittest.TestCase):
|
class Test_polytools(unittest.TestCase):
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
import unittest
|
import unittest
|
||||||
from svgpathtools import *
|
from svgpathtools import SaxDocument
|
||||||
from os.path import join, dirname
|
from os.path import join, dirname
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
import unittest
|
import unittest
|
||||||
from svgpathtools import *
|
from svgpathtools import svg2paths, Path, Line, Arc
|
||||||
from os.path import join, dirname
|
from os.path import join, dirname
|
||||||
|
|
||||||
|
|
||||||
class TestSVG2Paths(unittest.TestCase):
|
class TestSVG2Paths(unittest.TestCase):
|
||||||
def test_svg2paths_polygons(self):
|
def test_svg2paths_polygons(self):
|
||||||
|
|
||||||
|
@ -15,8 +16,8 @@ class TestSVG2Paths(unittest.TestCase):
|
||||||
Line(105.5+50j, 55.5+0j)
|
Line(105.5+50j, 55.5+0j)
|
||||||
)
|
)
|
||||||
self.assertTrue(path.isclosed())
|
self.assertTrue(path.isclosed())
|
||||||
self.assertTrue(len(path)==3)
|
self.assertEqual(len(path), 3)
|
||||||
self.assertTrue(path==path_correct)
|
self.assertEqual(path, path_correct)
|
||||||
|
|
||||||
# triangular quadrilateral (with a redundant 4th "closure" point)
|
# triangular quadrilateral (with a redundant 4th "closure" point)
|
||||||
path = paths[1]
|
path = paths[1]
|
||||||
|
@ -26,8 +27,8 @@ class TestSVG2Paths(unittest.TestCase):
|
||||||
Line(0+0j, 0+0j) # result of redundant point
|
Line(0+0j, 0+0j) # result of redundant point
|
||||||
)
|
)
|
||||||
self.assertTrue(path.isclosed())
|
self.assertTrue(path.isclosed())
|
||||||
self.assertTrue(len(path)==4)
|
self.assertEqual(len(path), 4)
|
||||||
self.assertTrue(path==path_correct)
|
self.assertEqual(path, path_correct)
|
||||||
|
|
||||||
def test_svg2paths_ellipses(self):
|
def test_svg2paths_ellipses(self):
|
||||||
|
|
||||||
|
@ -37,8 +38,8 @@ class TestSVG2Paths(unittest.TestCase):
|
||||||
path_ellipse = paths[0]
|
path_ellipse = paths[0]
|
||||||
path_ellipse_correct = Path(Arc(50+100j, 50+50j, 0.0, True, False, 150+100j),
|
path_ellipse_correct = Path(Arc(50+100j, 50+50j, 0.0, True, False, 150+100j),
|
||||||
Arc(150+100j, 50+50j, 0.0, True, False, 50+100j))
|
Arc(150+100j, 50+50j, 0.0, True, False, 50+100j))
|
||||||
self.assertTrue(len(path_ellipse)==2)
|
self.assertEqual(len(path_ellipse), 2)
|
||||||
self.assertTrue(path_ellipse==path_ellipse_correct)
|
self.assertEqual(path_ellipse, path_ellipse_correct)
|
||||||
self.assertTrue(path_ellipse.isclosed())
|
self.assertTrue(path_ellipse.isclosed())
|
||||||
|
|
||||||
# circle tests
|
# circle tests
|
||||||
|
@ -46,7 +47,7 @@ class TestSVG2Paths(unittest.TestCase):
|
||||||
|
|
||||||
path_circle = paths[0]
|
path_circle = paths[0]
|
||||||
path_circle_correct = Path(Arc(50+100j, 50+50j, 0.0, True, False, 150+100j),
|
path_circle_correct = Path(Arc(50+100j, 50+50j, 0.0, True, False, 150+100j),
|
||||||
Arc(150+100j, 50+50j, 0.0, True, False, 50+100j))
|
Arc(150+100j, 50+50j, 0.0, True, False, 50+100j))
|
||||||
self.assertTrue(len(path_circle)==2)
|
self.assertEqual(len(path_circle), 2)
|
||||||
self.assertTrue(path_circle==path_circle_correct)
|
self.assertEqual(path_circle, path_circle_correct)
|
||||||
self.assertTrue(path_circle.isclosed())
|
self.assertTrue(path_circle.isclosed())
|
||||||
|
|
Loading…
Reference in New Issue