Compare commits
No commits in common. "master" and "issue-171" have entirely different histories.
|
@ -1,34 +0,0 @@
|
||||||
name: Github CI Unit Testing for Legacy Environments
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
pull_request:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
continue-on-error: true
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-18.04, macos-10.15, windows-2019]
|
|
||||||
python-version: [2.7, 3.5, 3.6]
|
|
||||||
steps:
|
|
||||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
# configure python
|
|
||||||
- uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
|
|
||||||
# install deps
|
|
||||||
- name: Install dependencies for ${{ matrix.os }} Python ${{ matrix.python-version }}
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install -r requirements.txt
|
|
||||||
pip install scipy
|
|
||||||
|
|
||||||
# find and run all unit tests
|
|
||||||
- name: Run unit tests
|
|
||||||
run: python -m unittest discover test
|
|
|
@ -12,7 +12,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]
|
python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, "3.10"]
|
||||||
steps:
|
steps:
|
||||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
|
@ -8,7 +8,7 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
build-n-publish:
|
build-n-publish:
|
||||||
name: Build and publish to TestPyPI
|
name: Build and publish to TestPyPI
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
- name: Set up Python 3
|
- name: Set up Python 3
|
||||||
|
@ -30,7 +30,7 @@ jobs:
|
||||||
--outdir dist/
|
--outdir dist/
|
||||||
.
|
.
|
||||||
- name: Publish to Test PyPI
|
- name: Publish to Test PyPI
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
uses: pypa/gh-action-pypi-publish@master
|
||||||
with:
|
with:
|
||||||
skip_existing: true
|
skip_existing: true
|
||||||
password: ${{ secrets.TESTPYPI_API_TOKEN }}
|
password: ${{ secrets.TESTPYPI_API_TOKEN }}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
name: Publish to PyPI if new version
|
name: Publish to TestPyPI and if new version PyPI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -8,7 +8,7 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
build-n-publish:
|
build-n-publish:
|
||||||
name: Build and publish to TestPyPI and PyPI
|
name: Build and publish to TestPyPI and PyPI
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
- name: Set up Python 3
|
- name: Set up Python 3
|
||||||
|
@ -30,13 +30,13 @@ jobs:
|
||||||
--outdir dist/
|
--outdir dist/
|
||||||
.
|
.
|
||||||
- name: Publish to Test PyPI
|
- name: Publish to Test PyPI
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
uses: pypa/gh-action-pypi-publish@master
|
||||||
with:
|
with:
|
||||||
skip_existing: true
|
skip_existing: true
|
||||||
password: ${{ secrets.TESTPYPI_API_TOKEN }}
|
password: ${{ secrets.TESTPYPI_API_TOKEN }}
|
||||||
repository_url: https://test.pypi.org/legacy/
|
repository_url: https://test.pypi.org/legacy/
|
||||||
- name: Publish to PyPI
|
- name: Publish to PyPI
|
||||||
if: startsWith(github.ref, 'refs/tags')
|
if: startsWith(github.ref, 'refs/tags')
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
uses: pypa/gh-action-pypi-publish@master
|
||||||
with:
|
with:
|
||||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
"[![Donate](https://img.shields.io/badge/donate-paypal-brightgreen)](https://www.paypal.com/donate?business=4SKJ27AM4EYYA&no_recurring=0&item_name=Support+the+creator+of+svgpathtools?++He%27s+a+student+and+would+appreciate+it.&currency_code=USD)\n",
|
"[![Donate](https://img.shields.io/badge/donate-paypal-brightgreen)](https://www.paypal.com/donate?business=4SKJ27AM4EYYA&no_recurring=0&item_name=Support+the+creator+of+svgpathtools?++He%27s+a+student+and+would+appreciate+it.&currency_code=USD)\n",
|
||||||
"![Python](https://img.shields.io/pypi/pyversions/svgpathtools.svg)\n",
|
"![Python](https://img.shields.io/pypi/pyversions/svgpathtools.svg)\n",
|
||||||
"[![PyPI](https://img.shields.io/pypi/v/svgpathtools)](https://pypi.org/project/svgpathtools/)\n",
|
"[![PyPI](https://img.shields.io/pypi/v/svgpathtools)](https://pypi.org/project/svgpathtools/)\n",
|
||||||
|
"![Build](https://img.shields.io/github/workflow/status/mathandy/svgpathtools/Github%20CI%20Unit%20Testing)\n",
|
||||||
"[![PyPI - Downloads](https://img.shields.io/pypi/dm/svgpathtools?color=yellow)](https://pypistats.org/packages/svgpathtools)\n",
|
"[![PyPI - Downloads](https://img.shields.io/pypi/dm/svgpathtools?color=yellow)](https://pypistats.org/packages/svgpathtools)\n",
|
||||||
"# svgpathtools\n",
|
"# svgpathtools\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
@ -720,4 +721,4 @@
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
"nbformat_minor": 1
|
"nbformat_minor": 1
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
[![Donate](https://img.shields.io/badge/donate-paypal-brightgreen)](https://www.paypal.com/donate?business=4SKJ27AM4EYYA&no_recurring=0&item_name=Support+the+creator+of+svgpathtools?++He%27s+a+student+and+would+appreciate+it.&currency_code=USD)
|
[![Donate](https://img.shields.io/badge/donate-paypal-brightgreen)](https://www.paypal.com/donate?business=4SKJ27AM4EYYA&no_recurring=0&item_name=Support+the+creator+of+svgpathtools?++He%27s+a+student+and+would+appreciate+it.&currency_code=USD)
|
||||||
![Python](https://img.shields.io/pypi/pyversions/svgpathtools.svg)
|
![Python](https://img.shields.io/pypi/pyversions/svgpathtools.svg)
|
||||||
[![PyPI](https://img.shields.io/pypi/v/svgpathtools)](https://pypi.org/project/svgpathtools/)
|
[![PyPI](https://img.shields.io/pypi/v/svgpathtools)](https://pypi.org/project/svgpathtools/)
|
||||||
|
![Build](https://img.shields.io/github/workflow/status/mathandy/svgpathtools/Github%20CI%20Unit%20Testing)
|
||||||
[![PyPI - Downloads](https://img.shields.io/pypi/dm/svgpathtools?color=yellow)](https://pypistats.org/packages/svgpathtools)
|
[![PyPI - Downloads](https://img.shields.io/pypi/dm/svgpathtools?color=yellow)](https://pypistats.org/packages/svgpathtools)
|
||||||
# svgpathtools
|
# svgpathtools
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ 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 bezier_point, Path, bpoints2bezier, polynomial2bezier
|
from svgpathtools import *
|
||||||
|
|
||||||
|
|
||||||
class HigherOrderBezier:
|
class HigherOrderBezier:
|
||||||
|
|
|
@ -7,8 +7,7 @@ Path.continuous_subpaths() method to split a paths into a list of its
|
||||||
continuous subpaths.
|
continuous subpaths.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from svgpathtools import Path, Line
|
from svgpathtools import *
|
||||||
|
|
||||||
|
|
||||||
def path1_is_contained_in_path2(path1, path2):
|
def path1_is_contained_in_path2(path1, path2):
|
||||||
assert path2.isclosed() # This question isn't well-defined otherwise
|
assert path2.isclosed() # This question isn't well-defined otherwise
|
||||||
|
@ -17,11 +16,11 @@ def path1_is_contained_in_path2(path1, path2):
|
||||||
|
|
||||||
# find a point that's definitely outside path2
|
# find a point that's definitely outside path2
|
||||||
xmin, xmax, ymin, ymax = path2.bbox()
|
xmin, xmax, ymin, ymax = path2.bbox()
|
||||||
b = (xmin + 1) + 1j*(ymax + 1)
|
B = (xmin + 1) + 1j*(ymax + 1)
|
||||||
|
|
||||||
a = path1.start # pick an arbitrary point in path1
|
A = path1.start # pick an arbitrary point in path1
|
||||||
ab_line = Path(Line(a, b))
|
AB_line = Path(Line(A, B))
|
||||||
number_of_intersections = len(ab_line.intersect(path2))
|
number_of_intersections = len(AB_line.intersect(path2))
|
||||||
if number_of_intersections % 2: # if number of intersections is odd
|
if number_of_intersections % 2: # if number of intersections is odd
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
from svgpathtools import disvg, Line, CubicBezier
|
from svgpathtools import *
|
||||||
from scipy.optimize import fminbound
|
|
||||||
|
|
||||||
# create some example paths
|
# create some example paths
|
||||||
path1 = CubicBezier(1,2+3j,3-5j,4+1j)
|
path1 = CubicBezier(1,2+3j,3-5j,4+1j)
|
||||||
path2 = path1.rotated(60).translated(3)
|
path2 = path1.rotated(60).translated(3)
|
||||||
|
|
||||||
|
# find minimizer
|
||||||
|
from scipy.optimize import fminbound
|
||||||
def dist(t):
|
def dist(t):
|
||||||
return path1.radialrange(path2.point(t))[0][0]
|
return path1.radialrange(path2.point(t))[0][0]
|
||||||
|
|
||||||
|
|
||||||
# find minimizer
|
|
||||||
T2 = fminbound(dist, 0, 1)
|
T2 = fminbound(dist, 0, 1)
|
||||||
|
|
||||||
# Let's do a visual check
|
# Let's do a visual check
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -3,7 +3,7 @@ import codecs
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
VERSION = '1.6.1'
|
VERSION = '1.5.0'
|
||||||
AUTHOR_NAME = 'Andy Port'
|
AUTHOR_NAME = 'Andy Port'
|
||||||
AUTHOR_EMAIL = 'AndyAPort@gmail.com'
|
AUTHOR_EMAIL = 'AndyAPort@gmail.com'
|
||||||
GITHUB = 'https://github.com/mathandy/svgpathtools'
|
GITHUB = 'https://github.com/mathandy/svgpathtools'
|
||||||
|
@ -47,7 +47,6 @@ setup(name='svgpathtools',
|
||||||
"Programming Language :: Python :: 3.8",
|
"Programming Language :: Python :: 3.8",
|
||||||
"Programming Language :: Python :: 3.9",
|
"Programming Language :: Python :: 3.9",
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
"Programming Language :: Python :: 3.11",
|
|
||||||
"Topic :: Multimedia :: Graphics :: Editors :: Vector-Based",
|
"Topic :: Multimedia :: Graphics :: Editors :: Vector-Based",
|
||||||
"Topic :: Scientific/Engineering",
|
"Topic :: Scientific/Engineering",
|
||||||
"Topic :: Scientific/Engineering :: Image Recognition",
|
"Topic :: Scientific/Engineering :: Image Recognition",
|
||||||
|
|
|
@ -13,7 +13,7 @@ An Historic Note:
|
||||||
Example:
|
Example:
|
||||||
Typical usage looks something like the following.
|
Typical usage looks something like the following.
|
||||||
|
|
||||||
>> from svgpathtools import Document
|
>> from svgpathtools import *
|
||||||
>> doc = Document('my_file.html')
|
>> doc = Document('my_file.html')
|
||||||
>> for path in doc.paths():
|
>> for path in doc.paths():
|
||||||
>> # Do something with the transformed Path object.
|
>> # Do something with the transformed Path object.
|
||||||
|
@ -44,7 +44,6 @@ import warnings
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from tempfile import gettempdir
|
from tempfile import gettempdir
|
||||||
from time import time
|
from time import time
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
# Internal dependencies
|
# Internal dependencies
|
||||||
from .parser import parse_path
|
from .parser import parse_path
|
||||||
|
@ -52,7 +51,7 @@ from .parser import parse_transform
|
||||||
from .svg_to_paths import (path2pathd, ellipse2pathd, line2pathd,
|
from .svg_to_paths import (path2pathd, ellipse2pathd, line2pathd,
|
||||||
polyline2pathd, polygon2pathd, rect2pathd)
|
polyline2pathd, polygon2pathd, rect2pathd)
|
||||||
from .misctools import open_in_browser
|
from .misctools import open_in_browser
|
||||||
from .path import transform, Path, is_path_segment
|
from .path import *
|
||||||
|
|
||||||
# To maintain forward/backward compatibility
|
# To maintain forward/backward compatibility
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -43,8 +43,8 @@ except NameError:
|
||||||
COMMANDS = set('MmZzLlHhVvCcSsQqTtAa')
|
COMMANDS = set('MmZzLlHhVvCcSsQqTtAa')
|
||||||
UPPERCASE = set('MZLHVCSQTA')
|
UPPERCASE = set('MZLHVCSQTA')
|
||||||
|
|
||||||
COMMAND_RE = re.compile(r"([MmZzLlHhVvCcSsQqTtAa])")
|
COMMAND_RE = re.compile("([MmZzLlHhVvCcSsQqTtAa])")
|
||||||
FLOAT_RE = re.compile(r"[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?")
|
FLOAT_RE = re.compile("[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?")
|
||||||
|
|
||||||
# Default Parameters ##########################################################
|
# Default Parameters ##########################################################
|
||||||
|
|
||||||
|
@ -338,13 +338,9 @@ def transform(curve, tf):
|
||||||
|
|
||||||
if new_radius.real == 0 or new_radius.imag == 0 :
|
if new_radius.real == 0 or new_radius.imag == 0 :
|
||||||
return Line(new_start, new_end)
|
return Line(new_start, new_end)
|
||||||
else:
|
else :
|
||||||
if tf[0][0] * tf[1][1] >= 0.0:
|
|
||||||
new_sweep = curve.sweep
|
|
||||||
else:
|
|
||||||
new_sweep = not curve.sweep
|
|
||||||
return Arc(new_start, radius=new_radius, rotation=curve.rotation + rot,
|
return Arc(new_start, radius=new_radius, rotation=curve.rotation + rot,
|
||||||
large_arc=curve.large_arc, sweep=new_sweep, end=new_end,
|
large_arc=curve.large_arc, sweep=curve.sweep, end=new_end,
|
||||||
autoscale_radius=True)
|
autoscale_radius=True)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -715,19 +711,6 @@ class Line(object):
|
||||||
Note: This will fail if the two segments coincide for more than a
|
Note: This will fail if the two segments coincide for more than a
|
||||||
finite collection of points.
|
finite collection of points.
|
||||||
tol is not used."""
|
tol is not used."""
|
||||||
if isinstance(other_seg, (Line, QuadraticBezier, CubicBezier)):
|
|
||||||
ob = [e.real for e in other_seg.bpoints()]
|
|
||||||
sb = [e.real for e in self.bpoints()]
|
|
||||||
if min(ob) > max(sb):
|
|
||||||
return []
|
|
||||||
if max(ob) < min(sb):
|
|
||||||
return []
|
|
||||||
ob = [e.imag for e in other_seg.bpoints()]
|
|
||||||
sb = [e.imag for e in self.bpoints()]
|
|
||||||
if min(ob) > max(sb):
|
|
||||||
return []
|
|
||||||
if max(ob) < min(sb):
|
|
||||||
return []
|
|
||||||
if isinstance(other_seg, Line):
|
if isinstance(other_seg, Line):
|
||||||
assert other_seg.end != other_seg.start and self.end != self.start
|
assert other_seg.end != other_seg.start and self.end != self.start
|
||||||
assert self != other_seg
|
assert self != other_seg
|
||||||
|
@ -1055,19 +1038,6 @@ class QuadraticBezier(object):
|
||||||
self.point(t1) == other_seg.point(t2).
|
self.point(t1) == other_seg.point(t2).
|
||||||
Note: This will fail if the two segments coincide for more than a
|
Note: This will fail if the two segments coincide for more than a
|
||||||
finite collection of points."""
|
finite collection of points."""
|
||||||
if isinstance(other_seg, (Line, QuadraticBezier, CubicBezier)):
|
|
||||||
ob = [e.real for e in other_seg.bpoints()]
|
|
||||||
sb = [e.real for e in self.bpoints()]
|
|
||||||
if min(ob) > max(sb):
|
|
||||||
return []
|
|
||||||
if max(ob) < min(sb):
|
|
||||||
return []
|
|
||||||
ob = [e.imag for e in other_seg.bpoints()]
|
|
||||||
sb = [e.imag for e in self.bpoints()]
|
|
||||||
if min(ob) > max(sb):
|
|
||||||
return []
|
|
||||||
if max(ob) < min(sb):
|
|
||||||
return []
|
|
||||||
if isinstance(other_seg, Line):
|
if isinstance(other_seg, Line):
|
||||||
return bezier_by_line_intersections(self, other_seg)
|
return bezier_by_line_intersections(self, other_seg)
|
||||||
elif isinstance(other_seg, QuadraticBezier):
|
elif isinstance(other_seg, QuadraticBezier):
|
||||||
|
@ -1328,19 +1298,6 @@ class CubicBezier(object):
|
||||||
This will fail if the two segments coincide for more than a
|
This will fail if the two segments coincide for more than a
|
||||||
finite collection of points.
|
finite collection of points.
|
||||||
"""
|
"""
|
||||||
if isinstance(other_seg, (Line, QuadraticBezier, CubicBezier)):
|
|
||||||
ob = [e.real for e in other_seg.bpoints()]
|
|
||||||
sb = [e.real for e in self.bpoints()]
|
|
||||||
if min(ob) > max(sb):
|
|
||||||
return []
|
|
||||||
if max(ob) < min(sb):
|
|
||||||
return []
|
|
||||||
ob = [e.imag for e in other_seg.bpoints()]
|
|
||||||
sb = [e.imag for e in self.bpoints()]
|
|
||||||
if min(ob) > max(sb):
|
|
||||||
return []
|
|
||||||
if max(ob) < min(sb):
|
|
||||||
return []
|
|
||||||
if isinstance(other_seg, Line):
|
if isinstance(other_seg, Line):
|
||||||
return bezier_by_line_intersections(self, other_seg)
|
return bezier_by_line_intersections(self, other_seg)
|
||||||
elif (isinstance(other_seg, QuadraticBezier) or
|
elif (isinstance(other_seg, QuadraticBezier) or
|
||||||
|
@ -1398,7 +1355,7 @@ class CubicBezier(object):
|
||||||
class Arc(object):
|
class Arc(object):
|
||||||
def __init__(self, start, radius, rotation, large_arc, sweep, end,
|
def __init__(self, start, radius, rotation, large_arc, sweep, end,
|
||||||
autoscale_radius=True):
|
autoscale_radius=True):
|
||||||
r"""
|
"""
|
||||||
This should be thought of as a part of an ellipse connecting two
|
This should be thought of as a part of an ellipse connecting two
|
||||||
points on that ellipse, start and end.
|
points on that ellipse, start and end.
|
||||||
Parameters
|
Parameters
|
||||||
|
@ -2572,7 +2529,7 @@ class Path(MutableSequence):
|
||||||
|
|
||||||
# Shortcuts
|
# Shortcuts
|
||||||
if len(self._segments) == 0:
|
if len(self._segments) == 0:
|
||||||
raise ValueError("This path contains no segments!")
|
return None
|
||||||
if pos == 0.0:
|
if pos == 0.0:
|
||||||
return self._segments[0].point(pos)
|
return self._segments[0].point(pos)
|
||||||
if pos == 1.0:
|
if pos == 1.0:
|
||||||
|
@ -2589,7 +2546,6 @@ class Path(MutableSequence):
|
||||||
segment_end - segment_start)
|
segment_end - segment_start)
|
||||||
return segment.point(segment_pos)
|
return segment.point(segment_pos)
|
||||||
segment_start = segment_end
|
segment_start = segment_end
|
||||||
raise RuntimeError("Something has gone wrong. Could not compute Path.point({}) for path {}".format(pos, self))
|
|
||||||
|
|
||||||
def length(self, T0=0, T1=1, error=LENGTH_ERROR, min_depth=LENGTH_MIN_DEPTH):
|
def length(self, T0=0, T1=1, error=LENGTH_ERROR, min_depth=LENGTH_MIN_DEPTH):
|
||||||
self._calc_lengths(error=error, min_depth=min_depth)
|
self._calc_lengths(error=error, min_depth=min_depth)
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
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.ElementTree import iterparse, Element, ElementTree, SubElement
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
# Internal dependencies
|
# Internal dependencies
|
||||||
from .parser import parse_path
|
from .parser import parse_path
|
||||||
|
@ -14,13 +13,13 @@ from .parser import parse_transform
|
||||||
from .svg_to_paths import (path2pathd, ellipse2pathd, line2pathd,
|
from .svg_to_paths import (path2pathd, ellipse2pathd, line2pathd,
|
||||||
polyline2pathd, polygon2pathd, rect2pathd)
|
polyline2pathd, polygon2pathd, rect2pathd)
|
||||||
from .misctools import open_in_browser
|
from .misctools import open_in_browser
|
||||||
from .path import transform
|
from .path import *
|
||||||
|
|
||||||
# To maintain forward/backward compatibility
|
# To maintain forward/backward compatibility
|
||||||
try:
|
try:
|
||||||
string = basestring
|
str = basestring
|
||||||
except NameError:
|
except NameError:
|
||||||
string = str
|
pass
|
||||||
|
|
||||||
NAME_SVG = "svg"
|
NAME_SVG = "svg"
|
||||||
ATTR_VERSION = "version"
|
ATTR_VERSION = "version"
|
||||||
|
@ -165,17 +164,17 @@ class SaxDocument:
|
||||||
if matrix is not None and not np.all(np.equal(matrix, identity)):
|
if matrix is not None and not np.all(np.equal(matrix, identity)):
|
||||||
matrix_string = "matrix("
|
matrix_string = "matrix("
|
||||||
matrix_string += " "
|
matrix_string += " "
|
||||||
matrix_string += string(matrix[0][0])
|
matrix_string += str(matrix[0][0])
|
||||||
matrix_string += " "
|
matrix_string += " "
|
||||||
matrix_string += string(matrix[1][0])
|
matrix_string += str(matrix[1][0])
|
||||||
matrix_string += " "
|
matrix_string += " "
|
||||||
matrix_string += string(matrix[0][1])
|
matrix_string += str(matrix[0][1])
|
||||||
matrix_string += " "
|
matrix_string += " "
|
||||||
matrix_string += string(matrix[1][1])
|
matrix_string += str(matrix[1][1])
|
||||||
matrix_string += " "
|
matrix_string += " "
|
||||||
matrix_string += string(matrix[0][2])
|
matrix_string += str(matrix[0][2])
|
||||||
matrix_string += " "
|
matrix_string += " "
|
||||||
matrix_string += string(matrix[1][2])
|
matrix_string += str(matrix[1][2])
|
||||||
matrix_string += ")"
|
matrix_string += ")"
|
||||||
path.set(ATTR_TRANSFORM, matrix_string)
|
path.set(ATTR_TRANSFORM, matrix_string)
|
||||||
if ATTR_DATA in values:
|
if ATTR_DATA in values:
|
||||||
|
|
|
@ -51,7 +51,7 @@ def ellipse2pathd(ellipse):
|
||||||
d += 'a' + str(rx) + ',' + str(ry) + ' 0 1,0 ' + str(2 * rx) + ',0'
|
d += 'a' + str(rx) + ',' + str(ry) + ' 0 1,0 ' + str(2 * rx) + ',0'
|
||||||
d += 'a' + str(rx) + ',' + str(ry) + ' 0 1,0 ' + str(-2 * rx) + ',0'
|
d += 'a' + str(rx) + ',' + str(ry) + ' 0 1,0 ' + str(-2 * rx) + ',0'
|
||||||
|
|
||||||
return d + 'z'
|
return d
|
||||||
|
|
||||||
|
|
||||||
def polyline2pathd(polyline, is_polygon=False):
|
def polyline2pathd(polyline, is_polygon=False):
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100mm" height="100mm" viewBox="-100 -200 500 500" xmlns="http://www.w3.org/2000/svg" version="1.1">
|
|
||||||
<g id="Sketch" transform="scale(1,-1)">
|
|
||||||
<path id="slot" d="
|
|
||||||
M 0 10
|
|
||||||
L 0 80
|
|
||||||
A 30 30 0 1 0 0 140
|
|
||||||
A 10 10 0 0 1 0 100
|
|
||||||
L 100 100
|
|
||||||
A 10 10 0 1 1 100 140
|
|
||||||
A 30 30 0 0 0 100 80
|
|
||||||
L 100 10
|
|
||||||
A 10 10 0 0 0 90 0
|
|
||||||
L 10 0
|
|
||||||
A 10 10 0 0 0 0 10
|
|
||||||
" stroke="#ff0000" stroke-width="0.35 px"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 665 B |
|
@ -1,7 +1,7 @@
|
||||||
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 bezier_point, bezier2polynomial, polynomial2bezier
|
from svgpathtools.bezier import *
|
||||||
from svgpathtools.path import bpoints2bezier
|
from svgpathtools.path import bpoints2bezier
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 Document
|
from svgpathtools import *
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from io import open # overrides build-in open for compatibility with python2
|
from io import open # overrides build-in open for compatibility with python2
|
||||||
from os.path import join, dirname
|
from os.path import join, dirname
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
import unittest
|
import unittest
|
||||||
from svgpathtools import parse_path
|
from svgpathtools import *
|
||||||
|
|
||||||
|
|
||||||
class TestGeneration(unittest.TestCase):
|
class TestGeneration(unittest.TestCase):
|
||||||
|
|
|
@ -5,15 +5,11 @@ $ 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 Document, SVG_NAMESPACE, parse_path, Line, Arc
|
from svgpathtools import *
|
||||||
from os.path import join, dirname
|
from os.path import join, dirname
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
# When an assert fails, show the full error message, don't truncate it.
|
|
||||||
unittest.util._MAX_LENGTH = 999999999
|
|
||||||
|
|
||||||
|
|
||||||
def get_desired_path(name, paths):
|
def get_desired_path(name, paths):
|
||||||
return next(p for p in paths
|
return next(p for p in paths
|
||||||
if p.element.get('{some://testuri}name') == name)
|
if p.element.get('{some://testuri}name') == name)
|
||||||
|
@ -46,22 +42,6 @@ class TestGroups(unittest.TestCase):
|
||||||
self.check_values(tf.dot(v_s), actual.start)
|
self.check_values(tf.dot(v_s), actual.start)
|
||||||
self.check_values(tf.dot(v_e), actual.end)
|
self.check_values(tf.dot(v_e), actual.end)
|
||||||
|
|
||||||
def test_group_transform(self):
|
|
||||||
# The input svg has a group transform of "scale(1,-1)", which
|
|
||||||
# can mess with Arc sweeps.
|
|
||||||
doc = Document(join(dirname(__file__), 'negative-scale.svg'))
|
|
||||||
path = doc.paths()[0]
|
|
||||||
self.assertEqual(path[0], Line(start=-10j, end=-80j))
|
|
||||||
self.assertEqual(path[1], Arc(start=-80j, radius=(30+30j), rotation=0.0, large_arc=True, sweep=True, end=-140j))
|
|
||||||
self.assertEqual(path[2], Arc(start=-140j, radius=(20+20j), rotation=0.0, large_arc=False, sweep=False, end=-100j))
|
|
||||||
self.assertEqual(path[3], Line(start=-100j, end=(100-100j)))
|
|
||||||
self.assertEqual(path[4], Arc(start=(100-100j), radius=(20+20j), rotation=0.0, large_arc=True, sweep=False, end=(100-140j)))
|
|
||||||
self.assertEqual(path[5], Arc(start=(100-140j), radius=(30+30j), rotation=0.0, large_arc=False, sweep=True, end=(100-80j)))
|
|
||||||
self.assertEqual(path[6], Line(start=(100-80j), end=(100-10j)))
|
|
||||||
self.assertEqual(path[7], Arc(start=(100-10j), radius=(10+10j), rotation=0.0, large_arc=False, sweep=True, end=(90+0j)))
|
|
||||||
self.assertEqual(path[8], Line(start=(90+0j), end=(10+0j)))
|
|
||||||
self.assertEqual(path[9], Arc(start=(10+0j), radius=(10+10j), rotation=0.0, large_arc=False, sweep=True, end=-10j))
|
|
||||||
|
|
||||||
def test_group_flatten(self):
|
def test_group_flatten(self):
|
||||||
# Test the Document.paths() function against the
|
# Test the Document.paths() function against the
|
||||||
# groups.svg test file.
|
# groups.svg test file.
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
# 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 Path, Line, QuadraticBezier, CubicBezier, Arc, parse_path
|
from svgpathtools import *
|
||||||
import svgpathtools
|
import svgpathtools
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# 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 time
|
|
||||||
from sys import version_info
|
from sys import version_info
|
||||||
import unittest
|
import unittest
|
||||||
from math import sqrt, pi
|
from math import sqrt, pi
|
||||||
|
@ -11,12 +10,8 @@ import random
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
# Internal dependencies
|
# Internal dependencies
|
||||||
from svgpathtools import (
|
from svgpathtools import *
|
||||||
Line, QuadraticBezier, CubicBezier, Arc, Path, poly2bez, path_encloses_pt,
|
from svgpathtools.path import _NotImplemented4ArcException, bezier_radialrange
|
||||||
bpoints2bezier, closest_point_in_path, farthest_point_in_path,
|
|
||||||
is_bezier_segment, is_bezier_path, parse_path
|
|
||||||
)
|
|
||||||
from svgpathtools.path import bezier_radialrange
|
|
||||||
|
|
||||||
# An important note for those doing any debugging:
|
# An important note for those doing any debugging:
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
|
@ -1496,50 +1491,6 @@ class Test_intersect(unittest.TestCase):
|
||||||
self.assertTrue(len(yix) == 1)
|
self.assertTrue(len(yix) == 1)
|
||||||
###################################################################
|
###################################################################
|
||||||
|
|
||||||
def test_random_intersections(self):
|
|
||||||
from random import Random
|
|
||||||
r = Random()
|
|
||||||
distance = 100
|
|
||||||
distribution = 10000
|
|
||||||
count = 500
|
|
||||||
|
|
||||||
def random_complex(offset_x=0.0, offset_y=0.0):
|
|
||||||
return complex(r.random() * distance + offset_x, r.random() * distance + offset_y)
|
|
||||||
|
|
||||||
def random_line():
|
|
||||||
offset_x = r.random() * distribution
|
|
||||||
offset_y = r.random() * distribution
|
|
||||||
return Line(random_complex(offset_x, offset_y), random_complex(offset_x, offset_y))
|
|
||||||
|
|
||||||
def random_quad():
|
|
||||||
offset_x = r.random() * distribution
|
|
||||||
offset_y = r.random() * distribution
|
|
||||||
return QuadraticBezier(random_complex(offset_x, offset_y), random_complex(offset_x, offset_y), random_complex(offset_x, offset_y))
|
|
||||||
|
|
||||||
def random_cubic():
|
|
||||||
offset_x = r.random() * distribution
|
|
||||||
offset_y = r.random() * distribution
|
|
||||||
return CubicBezier(random_complex(offset_x, offset_y), random_complex(offset_x, offset_y), random_complex(offset_x, offset_y), random_complex(offset_x, offset_y))
|
|
||||||
|
|
||||||
def random_path():
|
|
||||||
path = Path()
|
|
||||||
for i in range(count):
|
|
||||||
type_segment = random.randint(0, 3)
|
|
||||||
if type_segment == 0:
|
|
||||||
path.append(random_line())
|
|
||||||
if type_segment == 1:
|
|
||||||
path.append(random_quad())
|
|
||||||
if type_segment == 2:
|
|
||||||
path.append(random_cubic())
|
|
||||||
return path
|
|
||||||
|
|
||||||
path1 = random_path()
|
|
||||||
path2 = random_path()
|
|
||||||
t = time.time()
|
|
||||||
intersections = path1.intersect(path2)
|
|
||||||
print("\nFound {} intersections in {} seconds.\n"
|
|
||||||
"".format(len(intersections), time.time() - t))
|
|
||||||
|
|
||||||
def test_line_line_0(self):
|
def test_line_line_0(self):
|
||||||
l0 = Line(start=(25.389999999999997+99.989999999999995j),
|
l0 = Line(start=(25.389999999999997+99.989999999999995j),
|
||||||
end=(25.389999999999997+90.484999999999999j))
|
end=(25.389999999999997+90.484999999999999j))
|
||||||
|
|
|
@ -4,7 +4,7 @@ import unittest
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
# Internal dependencies
|
# Internal dependencies
|
||||||
from svgpathtools import rational_limit
|
from svgpathtools import *
|
||||||
|
|
||||||
|
|
||||||
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 SaxDocument
|
from svgpathtools import *
|
||||||
from os.path import join, dirname
|
from os.path import join, dirname
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
import unittest
|
import unittest
|
||||||
from svgpathtools import Path, Line, Arc, svg2paths, svgstr2paths
|
from svgpathtools import *
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from io import open # overrides build-in open for compatibility with python2
|
from io import open # overrides build-in open for compatibility with python2
|
||||||
import os
|
|
||||||
from os.path import join, dirname
|
from os.path import join, dirname
|
||||||
from sys import version_info
|
from sys import version_info
|
||||||
import tempfile
|
|
||||||
import shutil
|
|
||||||
|
|
||||||
from svgpathtools.svg_to_paths import rect2pathd
|
from svgpathtools.svg_to_paths import rect2pathd
|
||||||
|
|
||||||
|
@ -60,26 +57,6 @@ class TestSVG2Paths(unittest.TestCase):
|
||||||
self.assertTrue(path_circle==path_circle_correct)
|
self.assertTrue(path_circle==path_circle_correct)
|
||||||
self.assertTrue(path_circle.isclosed())
|
self.assertTrue(path_circle.isclosed())
|
||||||
|
|
||||||
# test for issue #198 (circles not being closed)
|
|
||||||
svg = u"""<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" width="40mm" height="40mm"
|
|
||||||
viewBox="0 0 40 40" version="1.1">
|
|
||||||
|
|
||||||
<g id="layer">
|
|
||||||
<circle id="c1" cx="20.000" cy="20.000" r="11.000" />
|
|
||||||
<circle id="c2" cx="20.000" cy="20.000" r="5.15" />
|
|
||||||
</g>
|
|
||||||
</svg>"""
|
|
||||||
tmpdir = tempfile.mkdtemp()
|
|
||||||
svgfile = os.path.join(tmpdir, 'test.svg')
|
|
||||||
with open(svgfile, 'w') as f:
|
|
||||||
f.write(svg)
|
|
||||||
paths, _ = svg2paths(svgfile)
|
|
||||||
self.assertEqual(len(paths), 2)
|
|
||||||
self.assertTrue(paths[0].isclosed())
|
|
||||||
self.assertTrue(paths[1].isclosed())
|
|
||||||
shutil.rmtree(tmpdir)
|
|
||||||
|
|
||||||
def test_rect2pathd(self):
|
def test_rect2pathd(self):
|
||||||
non_rounded = {"x":"10", "y":"10", "width":"100","height":"100"}
|
non_rounded = {"x":"10", "y":"10", "width":"100","height":"100"}
|
||||||
self.assertEqual(rect2pathd(non_rounded), 'M10.0 10.0 L 110.0 10.0 L 110.0 110.0 L 10.0 110.0 z')
|
self.assertEqual(rect2pathd(non_rounded), 'M10.0 10.0 L 110.0 10.0 L 110.0 110.0 L 10.0 110.0 z')
|
||||||
|
@ -130,7 +107,3 @@ class TestSVG2Paths(unittest.TestCase):
|
||||||
paths, _ = svgstr2paths(file_content)
|
paths, _ = svgstr2paths(file_content)
|
||||||
|
|
||||||
self.assertEqual(len(paths), 2)
|
self.assertEqual(len(paths), 2)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
||||||
|
|
Loading…
Reference in New Issue