Module sverchok.utils.svg
Expand source code
# This file is part of project Sverchok. It's copyrighted by the contributors
# recorded in the version control history of the file, available from
# its original location https://github.com/nortikin/sverchok/commit/master
#
# SPDX-License-Identifier: GPL3
# License-Filename: LICENSE
from math import degrees
from numpy import float64 as np_float64, linspace as np_linspace
from sverchok.utils.curve import SvLine
class SvgGroup():
def __repr__(self):
return "<SVG Group of {} {}>".format(len(self.shapes), self.shapes[0].__repr__())
def __init__(self, shapes, name=None, location=None):
self.shapes = shapes
self.name = name
self.location=location
def draw(self, document):
scale = document.scale
svg = '<g '
if self.name:
svg += f'id="{self.name}" '
if self.location:
svg += f'transform="translate({self.location[0]*scale},{-self.location[1]*scale})"'
svg += '>\n'
for shape in self.shapes:
svg += shape.draw(document)
svg += '\n'
svg += '</g>'
return svg
def draw_path_linear_mode(verts, height, scale, start):
svg = ''
if start:
x = verts[0][0] * scale
y = height - verts[0][1] * scale
svg += f'd="M {x} {y}'
for v in verts[1:]:
x = scale * v[0]
y = height - scale * v[1]
svg += f' L {x} {y}'
return svg
def end_path(attributes, document):
svg = ''
if attributes:
svg += attributes.draw(document)
else:
svg += 'fill="none" '
svg += 'stroke-width="1px"'
svg += ' stroke="rgb(0,0,0)"'
svg += '/>'
return svg
def generic_path(self, height, scale, document, start=False):
t_min, t_max = self.curve.get_u_bounds()
ts = np_linspace(t_min, t_max, num=self.node.curve_samples, dtype=np_float64)
verts = self.curve.evaluate_array(ts).tolist()
svg = draw_path_linear_mode(verts, height, scale, start)
return svg
class SvgCurve():
def __repr__(self):
return "<SVG Curve>"
def __init__(self, curve, attributes, node):
self.curve = curve
self.attributes = attributes
self.node = node
def draw(self, document):
height = document.height
scale = document.scale
if isinstance(self.curve, SvLine):
svg = '<path\n'
u_min, u_max = self.curve.get_u_bounds()
p1 = self.curve.evaluate(u_min)
p2 = self.curve.evaluate(u_max)
x = p1[0] * scale
y = height - p1[1] * scale
svg += f'd="M {x} {y}'
x = scale * p2[0]
y = height - scale * p2[1]
svg += f' L {x} {y}" '
svg += end_path(self.attributes, document)
elif hasattr(self.curve, 'to_bezier_segments'):
svg = ''
last_point = []
for segment in self.curve.to_bezier_segments():
if segment.get_degree() not in {2, 3}: print("SVG supports only curves of 2nd and 3rd degree Linear Interpretation used")
bezier_points = segment.get_control_points()
if last_point != bezier_points[0].tolist():
if last_point:
svg += '" '
svg += end_path(self.attributes, document)
svg += '<path\n'
x = bezier_points[0][0] * scale
y = height - bezier_points[0][1] * scale
svg += f'd="M {x} {y}'
if len(bezier_points) == 3:
cx = bezier_points[1][0] * scale
cy = height - bezier_points[1][1] * scale
x = bezier_points[2][0] * scale
y = height - bezier_points[2][1] * scale
svg += f' Q {cx} {cy} {x} {y}'
# svg += end_path(self.attributes, document)
last_point = bezier_points[2].tolist()
if len(bezier_points) == 4:
cx = bezier_points[1][0] * scale
cy = height - bezier_points[1][1] * scale
cx2 = bezier_points[2][0] * scale
cy2 = height - bezier_points[2][1] * scale
x = bezier_points[3][0] * scale
y = height - bezier_points[3][1] * scale
svg += f' C {cx} {cy} {cx2} {cy2} {x} {y}'
last_point = bezier_points[3].tolist()
else:
generic_path(self, height, scale, document)
if self.node.cyclic:
svg += ' Z'
svg += '" '
svg += end_path(self.attributes, document)
else:
svg = '<path\n'
svg += generic_path(self, height, scale, document, start=True)
if self.node.cyclic:
svg += ' Z'
svg += '" '
svg += end_path(self.attributes, document)
return svg
Functions
def draw_path_linear_mode(verts, height, scale, start)
-
Expand source code
def draw_path_linear_mode(verts, height, scale, start): svg = '' if start: x = verts[0][0] * scale y = height - verts[0][1] * scale svg += f'd="M {x} {y}' for v in verts[1:]: x = scale * v[0] y = height - scale * v[1] svg += f' L {x} {y}' return svg
def end_path(attributes, document)
-
Expand source code
def end_path(attributes, document): svg = '' if attributes: svg += attributes.draw(document) else: svg += 'fill="none" ' svg += 'stroke-width="1px"' svg += ' stroke="rgb(0,0,0)"' svg += '/>' return svg
def generic_path(self, height, scale, document, start=False)
-
Expand source code
def generic_path(self, height, scale, document, start=False): t_min, t_max = self.curve.get_u_bounds() ts = np_linspace(t_min, t_max, num=self.node.curve_samples, dtype=np_float64) verts = self.curve.evaluate_array(ts).tolist() svg = draw_path_linear_mode(verts, height, scale, start) return svg
Classes
class SvgCurve (curve, attributes, node)
-
Expand source code
class SvgCurve(): def __repr__(self): return "<SVG Curve>" def __init__(self, curve, attributes, node): self.curve = curve self.attributes = attributes self.node = node def draw(self, document): height = document.height scale = document.scale if isinstance(self.curve, SvLine): svg = '<path\n' u_min, u_max = self.curve.get_u_bounds() p1 = self.curve.evaluate(u_min) p2 = self.curve.evaluate(u_max) x = p1[0] * scale y = height - p1[1] * scale svg += f'd="M {x} {y}' x = scale * p2[0] y = height - scale * p2[1] svg += f' L {x} {y}" ' svg += end_path(self.attributes, document) elif hasattr(self.curve, 'to_bezier_segments'): svg = '' last_point = [] for segment in self.curve.to_bezier_segments(): if segment.get_degree() not in {2, 3}: print("SVG supports only curves of 2nd and 3rd degree Linear Interpretation used") bezier_points = segment.get_control_points() if last_point != bezier_points[0].tolist(): if last_point: svg += '" ' svg += end_path(self.attributes, document) svg += '<path\n' x = bezier_points[0][0] * scale y = height - bezier_points[0][1] * scale svg += f'd="M {x} {y}' if len(bezier_points) == 3: cx = bezier_points[1][0] * scale cy = height - bezier_points[1][1] * scale x = bezier_points[2][0] * scale y = height - bezier_points[2][1] * scale svg += f' Q {cx} {cy} {x} {y}' # svg += end_path(self.attributes, document) last_point = bezier_points[2].tolist() if len(bezier_points) == 4: cx = bezier_points[1][0] * scale cy = height - bezier_points[1][1] * scale cx2 = bezier_points[2][0] * scale cy2 = height - bezier_points[2][1] * scale x = bezier_points[3][0] * scale y = height - bezier_points[3][1] * scale svg += f' C {cx} {cy} {cx2} {cy2} {x} {y}' last_point = bezier_points[3].tolist() else: generic_path(self, height, scale, document) if self.node.cyclic: svg += ' Z' svg += '" ' svg += end_path(self.attributes, document) else: svg = '<path\n' svg += generic_path(self, height, scale, document, start=True) if self.node.cyclic: svg += ' Z' svg += '" ' svg += end_path(self.attributes, document) return svg
Methods
def draw(self, document)
-
Expand source code
def draw(self, document): height = document.height scale = document.scale if isinstance(self.curve, SvLine): svg = '<path\n' u_min, u_max = self.curve.get_u_bounds() p1 = self.curve.evaluate(u_min) p2 = self.curve.evaluate(u_max) x = p1[0] * scale y = height - p1[1] * scale svg += f'd="M {x} {y}' x = scale * p2[0] y = height - scale * p2[1] svg += f' L {x} {y}" ' svg += end_path(self.attributes, document) elif hasattr(self.curve, 'to_bezier_segments'): svg = '' last_point = [] for segment in self.curve.to_bezier_segments(): if segment.get_degree() not in {2, 3}: print("SVG supports only curves of 2nd and 3rd degree Linear Interpretation used") bezier_points = segment.get_control_points() if last_point != bezier_points[0].tolist(): if last_point: svg += '" ' svg += end_path(self.attributes, document) svg += '<path\n' x = bezier_points[0][0] * scale y = height - bezier_points[0][1] * scale svg += f'd="M {x} {y}' if len(bezier_points) == 3: cx = bezier_points[1][0] * scale cy = height - bezier_points[1][1] * scale x = bezier_points[2][0] * scale y = height - bezier_points[2][1] * scale svg += f' Q {cx} {cy} {x} {y}' # svg += end_path(self.attributes, document) last_point = bezier_points[2].tolist() if len(bezier_points) == 4: cx = bezier_points[1][0] * scale cy = height - bezier_points[1][1] * scale cx2 = bezier_points[2][0] * scale cy2 = height - bezier_points[2][1] * scale x = bezier_points[3][0] * scale y = height - bezier_points[3][1] * scale svg += f' C {cx} {cy} {cx2} {cy2} {x} {y}' last_point = bezier_points[3].tolist() else: generic_path(self, height, scale, document) if self.node.cyclic: svg += ' Z' svg += '" ' svg += end_path(self.attributes, document) else: svg = '<path\n' svg += generic_path(self, height, scale, document, start=True) if self.node.cyclic: svg += ' Z' svg += '" ' svg += end_path(self.attributes, document) return svg
class SvgGroup (shapes, name=None, location=None)
-
Expand source code
class SvgGroup(): def __repr__(self): return "<SVG Group of {} {}>".format(len(self.shapes), self.shapes[0].__repr__()) def __init__(self, shapes, name=None, location=None): self.shapes = shapes self.name = name self.location=location def draw(self, document): scale = document.scale svg = '<g ' if self.name: svg += f'id="{self.name}" ' if self.location: svg += f'transform="translate({self.location[0]*scale},{-self.location[1]*scale})"' svg += '>\n' for shape in self.shapes: svg += shape.draw(document) svg += '\n' svg += '</g>' return svg
Methods
def draw(self, document)
-
Expand source code
def draw(self, document): scale = document.scale svg = '<g ' if self.name: svg += f'id="{self.name}" ' if self.location: svg += f'transform="translate({self.location[0]*scale},{-self.location[1]*scale})"' svg += '>\n' for shape in self.shapes: svg += shape.draw(document) svg += '\n' svg += '</g>' return svg