SVG rectangles with rounded corners.
This commit is contained in:
99
svgparse.py
99
svgparse.py
@@ -7,7 +7,7 @@
|
|||||||
# #
|
# #
|
||||||
# SVG Features supported: #
|
# SVG Features supported: #
|
||||||
# * Groups #
|
# * Groups #
|
||||||
# * Rectangles #
|
# * Rectangles (w/ rounded corners) #
|
||||||
# * Circles #
|
# * Circles #
|
||||||
# * Ellipses #
|
# * Ellipses #
|
||||||
# * Polygons #
|
# * Polygons #
|
||||||
@@ -32,6 +32,14 @@ log = logging.getLogger('base2')
|
|||||||
|
|
||||||
|
|
||||||
def svgparselength(lengthstr):
|
def svgparselength(lengthstr):
|
||||||
|
"""
|
||||||
|
Parse an SVG length string into a float and a units
|
||||||
|
string, if any.
|
||||||
|
|
||||||
|
:param lengthstr: SVG length string.
|
||||||
|
:return: Number and units pair.
|
||||||
|
:rtype: tuple(float, str|None)
|
||||||
|
"""
|
||||||
|
|
||||||
integer_re_str = r'[+-]?[0-9]+'
|
integer_re_str = r'[+-]?[0-9]+'
|
||||||
number_re_str = r'(?:[+-]?[0-9]*\.[0-9]+(?:[Ee]' + integer_re_str + ')?' + r')|' + \
|
number_re_str = r'(?:[+-]?[0-9]*\.[0-9]+(?:[Ee]' + integer_re_str + ')?' + r')|' + \
|
||||||
@@ -99,24 +107,63 @@ def path2shapely(path, res=1.0):
|
|||||||
return LineString(points)
|
return LineString(points)
|
||||||
|
|
||||||
|
|
||||||
def svgrect2shapely(rect):
|
def svgrect2shapely(rect, n_points=32):
|
||||||
"""
|
"""
|
||||||
Converts an SVG rect into Shapely geometry.
|
Converts an SVG rect into Shapely geometry.
|
||||||
|
|
||||||
:param rect: Rect Element
|
:param rect: Rect Element
|
||||||
:type rect: xml.etree.ElementTree.Element
|
:type rect: xml.etree.ElementTree.Element
|
||||||
:return: shapely.geometry.polygon.LinearRing
|
:return: shapely.geometry.polygon.LinearRing
|
||||||
|
|
||||||
:param rect:
|
|
||||||
:return:
|
|
||||||
"""
|
"""
|
||||||
w = float(rect.get('width'))
|
w = svgparselength(rect.get('width'))[0]
|
||||||
h = float(rect.get('height'))
|
h = svgparselength(rect.get('height'))[0]
|
||||||
x = float(rect.get('x'))
|
x = svgparselength(rect.get('x'))[0]
|
||||||
y = float(rect.get('y'))
|
y = svgparselength(rect.get('y'))[0]
|
||||||
pts = [
|
|
||||||
(x, y), (x + w, y), (x + w, y + h), (x, y + h), (x, y)
|
rxstr = rect.get('rx')
|
||||||
]
|
rystr = rect.get('ry')
|
||||||
|
|
||||||
|
if rxstr is None and rystr is None: # Sharp corners
|
||||||
|
pts = [
|
||||||
|
(x, y), (x + w, y), (x + w, y + h), (x, y + h), (x, y)
|
||||||
|
]
|
||||||
|
|
||||||
|
else: # Rounded corners
|
||||||
|
rx = 0.0 if rxstr is None else svgparselength(rxstr)[0]
|
||||||
|
ry = 0.0 if rystr is None else svgparselength(rystr)[0]
|
||||||
|
|
||||||
|
n_points = int(n_points / 4 + 0.5)
|
||||||
|
t = np.arange(n_points, dtype=float) / n_points / 4
|
||||||
|
|
||||||
|
x_ = (x + w - rx) + rx * np.cos(2 * np.pi * (t + 0.75))
|
||||||
|
y_ = (y + ry) + ry * np.sin(2 * np.pi * (t + 0.75))
|
||||||
|
|
||||||
|
lower_right = [(x_[i], y_[i]) for i in range(n_points)]
|
||||||
|
|
||||||
|
x_ = (x + w - rx) + rx * np.cos(2 * np.pi * t)
|
||||||
|
y_ = (y + h - ry) + ry * np.sin(2 * np.pi * t)
|
||||||
|
|
||||||
|
upper_right = [(x_[i], y_[i]) for i in range(n_points)]
|
||||||
|
|
||||||
|
x_ = (x + rx) + rx * np.cos(2 * np.pi * (t + 0.25))
|
||||||
|
y_ = (y + h - ry) + ry * np.sin(2 * np.pi * (t + 0.25))
|
||||||
|
|
||||||
|
upper_left = [(x_[i], y_[i]) for i in range(n_points)]
|
||||||
|
|
||||||
|
x_ = (x + rx) + rx * np.cos(2 * np.pi * (t + 0.5))
|
||||||
|
y_ = (y + ry) + ry * np.sin(2 * np.pi * (t + 0.5))
|
||||||
|
|
||||||
|
lower_left = [(x_[i], y_[i]) for i in range(n_points)]
|
||||||
|
|
||||||
|
pts = [(x + rx, y), (x - rx + w, y)] + \
|
||||||
|
lower_right + \
|
||||||
|
[(x + w, y + ry), (x + w, y + h - ry)] + \
|
||||||
|
upper_right + \
|
||||||
|
[(x + w - rx, y + h), (x + rx, y + h)] + \
|
||||||
|
upper_left + \
|
||||||
|
[(x, y + h - ry), (x, y + ry)] + \
|
||||||
|
lower_left
|
||||||
|
|
||||||
return LinearRing(pts)
|
return LinearRing(pts)
|
||||||
|
|
||||||
|
|
||||||
@@ -126,7 +173,8 @@ def svgcircle2shapely(circle):
|
|||||||
|
|
||||||
:param circle: Circle Element
|
:param circle: Circle Element
|
||||||
:type circle: xml.etree.ElementTree.Element
|
:type circle: xml.etree.ElementTree.Element
|
||||||
:return: shapely.geometry.polygon.LinearRing
|
:return: Shapely representation of the circle.
|
||||||
|
:rtype: shapely.geometry.polygon.LinearRing
|
||||||
"""
|
"""
|
||||||
# cx = float(circle.get('cx'))
|
# cx = float(circle.get('cx'))
|
||||||
# cy = float(circle.get('cy'))
|
# cy = float(circle.get('cy'))
|
||||||
@@ -139,14 +187,15 @@ def svgcircle2shapely(circle):
|
|||||||
return Point(cx, cy).buffer(r)
|
return Point(cx, cy).buffer(r)
|
||||||
|
|
||||||
|
|
||||||
def svgellipse2shapely(ellipse, n_points=32):
|
def svgellipse2shapely(ellipse, n_points=64):
|
||||||
"""
|
"""
|
||||||
Converts an SVG ellipse into Shapely geometry
|
Converts an SVG ellipse into Shapely geometry
|
||||||
|
|
||||||
:param ellipse: Ellipse Element
|
:param ellipse: Ellipse Element
|
||||||
:type ellipse: xml.etree.ElementTree.Element
|
:type ellipse: xml.etree.ElementTree.Element
|
||||||
:param n_points: Number of discrete points in output.
|
:param n_points: Number of discrete points in output.
|
||||||
:return: shapely.geometry.polygon.LinearRing
|
:return: Shapely representation of the ellipse.
|
||||||
|
:rtype: shapely.geometry.polygon.LinearRing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cx = svgparselength(ellipse.get('cx'))[0] # TODO: No units support yet
|
cx = svgparselength(ellipse.get('cx'))[0] # TODO: No units support yet
|
||||||
@@ -164,11 +213,18 @@ def svgellipse2shapely(ellipse, n_points=32):
|
|||||||
|
|
||||||
|
|
||||||
def svgline2shapely(line):
|
def svgline2shapely(line):
|
||||||
|
"""
|
||||||
|
|
||||||
x1 = svgparselength(line.get('x1'))
|
:param line: Line element
|
||||||
y1 = svgparselength(line.get('y1'))
|
:type line: xml.etree.ElementTree.Element
|
||||||
x2 = svgparselength(line.get('x2'))
|
:return: Shapely representation on the line.
|
||||||
y2 = svgparselength(line.get('y2'))
|
:rtype: shapely.geometry.polygon.LinearRing
|
||||||
|
"""
|
||||||
|
|
||||||
|
x1 = svgparselength(line.get('x1'))[0]
|
||||||
|
y1 = svgparselength(line.get('y1'))[0]
|
||||||
|
x2 = svgparselength(line.get('x2'))[0]
|
||||||
|
y2 = svgparselength(line.get('y2'))[0]
|
||||||
|
|
||||||
return LineString([(x1, y1), (x2, y2)])
|
return LineString([(x1, y1), (x2, y2)])
|
||||||
|
|
||||||
@@ -194,8 +250,9 @@ def getsvggeo(node):
|
|||||||
Extracts and flattens all geometry from an SVG node
|
Extracts and flattens all geometry from an SVG node
|
||||||
into a list of Shapely geometry.
|
into a list of Shapely geometry.
|
||||||
|
|
||||||
:param node:
|
:param node: xml.etree.ElementTree.Element
|
||||||
:return:
|
:return: List of Shapely geometry
|
||||||
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
kind = re.search('(?:\{.*\})?(.*)$', node.tag).group(1)
|
kind = re.search('(?:\{.*\})?(.*)$', node.tag).group(1)
|
||||||
geo = []
|
geo = []
|
||||||
|
|||||||
Reference in New Issue
Block a user