3 import lxml
.etree
as ET
6 SVG path linearization library (eg. for SVG2gcode use)
7 Made by Tomas 'Harvie' Mudrunka 2018
9 heavily based on SVG compression library
11 by Gen Del Raye (July 22, 2014): https://pypi.org/project/SVGCompress/
16 def __init__(self
, svg_file
):
18 Read svg from path 'svg_file' (e.g. 'test/test_vector.svg')
20 #self.filename, self.extension = svg_file.split('.') # Get filename and extension
21 #assert self.extension == 'svg', 'File must be an svg'
23 self
.figure_data
= ET
.parse(svg_file
) # Parse svg data as xml
24 except ET
.XMLSyntaxError
, e
:
26 Large svgs may trigger lxml's 'excessive depth in document' exception
28 warnings
.warn('lxml error: %s - Trying huge xml parser' %(e
.message
))
29 huge_parser
= ET
.XMLParser(huge_tree
= True)
30 self
.figure_data
= ET
.parse(svg_file
, parser
= huge_parser
)
31 self
.root
= self
.figure_data
.getroot() # Root object in svg
35 Find and parse nodes in the xml that correspond to paths in the svg
38 self
.path_nodes
= self
.root
.findall('.//%spath' %(tag_prefix))
40 self
.paths
= [svg
.path
.parse_path(p
.attrib
.get('d', 'M 0,0 z')) for p
in self
.path_nodes
]
42 def linearize_paths(self
, curve_fidelity
= 10):
44 Turn svg paths into discrete lines
46 curve_fidelity(int) - number of lines with which to approximate curves
47 in svg. Higher values necessitates longer computation time.
49 self
.linear_coords
= [self
.linearize(p
, curve_fidelity
) for p
in self
.paths
]
52 def linearize_line(self
, segment
, n_interpolate
= None):
54 Turn svg line into set of coordinates by returning
55 start and end coordinates of the line segment.
56 n_interpolate is only used for consistency of use
57 with linearize_curve()
59 return numpy
.array([segment
.start
, segment
.end
])
61 def linearize_curve(self
, segment
, n_interpolate
= 10):
63 Estimate svg curve (e.g. Bezier, Arc, etc.) using
64 a set of n discrete lines. n_interpolate sets the
65 number of discrete lines per curve.
67 interpolation_pts
= numpy
.linspace(0, 1, n_interpolate
, endpoint
= False)[1:]
68 interpolated
= numpy
.zeros(n_interpolate
+ 1, dtype
= complex)
69 interpolated
[0] = segment
.start
70 interpolated
[-1] = segment
.end
71 for i
, pt
in enumerate(interpolation_pts
):
72 interpolated
[i
+ 1] = segment
.point(pt
)
75 def complex2coord(self
, complexnum
):
76 return (complexnum
.real
, complexnum
.imag
)
78 def linearize(self
, path
, n_interpolate
= 10):
79 segmenttype2func
= {'CubicBezier': self
.linearize_curve
,
80 'Line': self
.linearize_line
,
81 'QuadraticBezier': self
.linearize_curve
,
82 'Arc': self
.linearize_curve
}
84 More sophisticated linearization option
85 compared to endpts2line().
86 Turn svg path into discrete coordinates
87 with number of coordinates per curve set
88 by n_interpolate. i.e. if n_interpolate
89 is 100, each curve is approximated by
92 segments
= path
._segments
93 complex_coords
= list()
94 for segment
in segments
:
95 # Output coordinates for each segment, minus last point (because
96 # point is same as first point of next segment)
97 segment_type
= type(segment
).__name
__
98 segment_linearize
= segmenttype2func
[segment_type
]
99 linearized
= segment_linearize(segment
, n_interpolate
)
100 complex_coords
.extend(linearized
[:-1])
101 # Append last point of final segment to close the polygon
102 complex_coords
.append(linearized
[-1])
103 return [self
.complex2coord(complexnum
) for complexnum
in complex_coords
] # Output list of (x, y) tuples
This page took 0.876495 seconds and 4 git commands to generate.