Implement basic SVG primitives and 'path' skeleton
This commit is contained in:
@@ -71,8 +71,12 @@ class DrawingController(Node):
|
|||||||
xml = ET.parse(svgpath)
|
xml = ET.parse(svgpath)
|
||||||
svg = xml.getroot()
|
svg = xml.getroot()
|
||||||
#self.map_point = map_point_function(float(svg.get('width')), float(svg.get('height')), 0.1, 0.5, -0.2, 0.2)
|
#self.map_point = map_point_function(float(svg.get('width')), float(svg.get('height')), 0.1, 0.5, -0.2, 0.2)
|
||||||
self.map_point = map_point_function(float(svg.get('width')), float(svg.get('height')), 0.0, 1.0, 0.0, 1.0)
|
try:
|
||||||
self.lines = []
|
self.map_point = map_point_function(float(svg.get('width')), float(svg.get('height')), 0.0, 1.0, 0.0, 1.0)
|
||||||
|
except:
|
||||||
|
self.map_point = map_point_function(1000, 1000, 0.0, 1.0, 0.0, 1.0)
|
||||||
|
|
||||||
|
self.lines = [ ((0,0),(0,0)) ]
|
||||||
for child in svg:
|
for child in svg:
|
||||||
if (child.tag == 'line'):
|
if (child.tag == 'line'):
|
||||||
p1 = (float(child.get('x1')), float(child.get('y1')))
|
p1 = (float(child.get('x1')), float(child.get('y1')))
|
||||||
@@ -80,7 +84,7 @@ class DrawingController(Node):
|
|||||||
self.lines.append((p1,p2))
|
self.lines.append((p1,p2))
|
||||||
|
|
||||||
self.svg_processor = SVGProcessor(self.get_logger())
|
self.svg_processor = SVGProcessor(self.get_logger())
|
||||||
self.svg_processor.process_svg(svgpath)
|
print(self.svg_processor.process_svg(svgpath))
|
||||||
|
|
||||||
def send_goal(self, motion):
|
def send_goal(self, motion):
|
||||||
self.busy = True
|
self.busy = True
|
||||||
|
|||||||
@@ -1,31 +1,136 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import rclpy
|
|
||||||
import lxml.etree as ET
|
import lxml.etree as ET
|
||||||
|
|
||||||
class SVGProcessor():
|
class SVGProcessor():
|
||||||
|
"""
|
||||||
|
Class which reads an svg file and creates sequences of points
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
primitive_fns : dict
|
||||||
|
contains a mapping of SVG primitive names to functions that produce points
|
||||||
|
|
||||||
|
Methods
|
||||||
|
-------
|
||||||
|
|
||||||
|
Services
|
||||||
|
-------
|
||||||
|
|
||||||
|
Topics
|
||||||
|
-------
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, logger):
|
def __init__(self, logger):
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
# A dict containing svg primitive names mapping to functions that handle them
|
# A dict containing svg primitive names mapping to functions that handle them
|
||||||
self.primitives = {
|
self.primitive_fns = {
|
||||||
# Reference:
|
# Reference:
|
||||||
# https://developer.mozilla.org/en-US/docs/Web/SVG/Element
|
# https://developer.mozilla.org/en-US/docs/Web/SVG/Element
|
||||||
#"line": lambda p: print("LINE"),
|
"line": self.primitive_line,
|
||||||
|
"polyline": self.primitive_polyline,
|
||||||
|
"polygon": self.primitive_polygon,
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_primitive(self, primitive):
|
def get_primitive_fn(self, primitive):
|
||||||
log_error = lambda p: self.logger.error("'{}' not supported".format(p.tag))
|
'''
|
||||||
return self.primitives.get(primitive.tag, log_error)
|
Looks up the primitive tag name in the dictionary of functions.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
primitive (lxml child): the primitive from the svg file
|
||||||
|
Returns:
|
||||||
|
primitive_fn ():
|
||||||
|
'''
|
||||||
|
def log_error(p, _):
|
||||||
|
self.logger.error("'{}' not supported".format(p.tag))
|
||||||
|
return []
|
||||||
|
return self.primitive_fns.get(primitive.tag, log_error)
|
||||||
|
|
||||||
|
|
||||||
|
def down_and_up(self, points):
|
||||||
|
down = (points[0][0], points[0][1], 1)
|
||||||
|
up = (points[-1][0], points[-1][1], 1)
|
||||||
|
|
||||||
|
return [down] + points + [up]
|
||||||
|
|
||||||
|
def primitive_line(self, child, map_point):
|
||||||
|
p1 = map_point(float(child.get('x1')), float(child.get('y1')))
|
||||||
|
p2 = map_point(float(child.get('x2')), float(child.get('y2')))
|
||||||
|
return [
|
||||||
|
(p1[0],p1[1],0),
|
||||||
|
(p2[0],p2[1],0),
|
||||||
|
]
|
||||||
|
|
||||||
|
def primitive_polyline(self, child, map_point):
|
||||||
|
points = child.get('points').split(' ')
|
||||||
|
points = [(map_point(float(p[0])),
|
||||||
|
map_point(float(p[1])))
|
||||||
|
for p in points.split(',')]
|
||||||
|
output = []
|
||||||
|
for p in points:
|
||||||
|
output.append((p[0],p[1],0))
|
||||||
|
return output
|
||||||
|
|
||||||
|
def primitive_polygon(self, child, map_point):
|
||||||
|
output = self.primitive_polyline(child, map_point)
|
||||||
|
output.append((output[0][0],output[0][1],0))
|
||||||
|
return output
|
||||||
|
|
||||||
|
# https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d
|
||||||
|
def path_parser(self, child, map_point):
|
||||||
|
path = child.get('d')
|
||||||
|
self.logger.info("Parsing path :'{}...' with {} characters".format(path[:40], len(path)))
|
||||||
|
self.logger.error("Path parser not implemented")
|
||||||
|
return []
|
||||||
|
|
||||||
|
# https://stackoverflow.com/questions/30232031/how-can-i-strip-namespaces-out-of-an-lxml-tree
|
||||||
|
def strip_ns_prefix(self, tree):
|
||||||
|
#xpath query for selecting all element nodes in namespace
|
||||||
|
query = "descendant-or-self::*[namespace-uri()!='']"
|
||||||
|
#for each element returned by the above xpath query...
|
||||||
|
for element in tree.xpath(query):
|
||||||
|
#replace element name with its local name
|
||||||
|
element.tag = ET.QName(element).localname
|
||||||
|
return tree
|
||||||
|
|
||||||
def process_svg(self, svg_path):
|
def process_svg(self, svg_path):
|
||||||
with open(svg_path) as svg:
|
with open(svg_path) as svg_file:
|
||||||
xml = ET.parse(svg)
|
xml = ET.parse(svg_file)
|
||||||
svg = xml.getroot()
|
svg = xml.getroot()
|
||||||
|
svg = self.strip_ns_prefix(svg)
|
||||||
|
|
||||||
|
map_point = self.map_point_function(1000,
|
||||||
|
1000)
|
||||||
|
if 'width' in svg.attrib:
|
||||||
|
map_point = self.map_point_function(float(svg.get('width')),
|
||||||
|
float(svg.get('height')))
|
||||||
|
elif 'viewBox' in svg.attrib:
|
||||||
|
# TODO parse viewBox
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.logger.error("Unable to get SVG dimensions")
|
||||||
|
|
||||||
|
motions = []
|
||||||
for child in svg:
|
for child in svg:
|
||||||
f = self.get_primitive(child)
|
self.logger.info("Attempting to process SVG primitive:'{}'".format(child.tag))
|
||||||
f(child)
|
primitive_fn = self.primitive_line
|
||||||
|
# path can consist of multiple primitives
|
||||||
|
if (child.tag == 'path'):
|
||||||
|
for m in self.path_parser(child, map_point):
|
||||||
|
motions.append(m)
|
||||||
|
else:
|
||||||
|
primitive_fn = self.get_primitive_fn(child)
|
||||||
|
motions.append(primitive_fn(child, map_point))
|
||||||
|
|
||||||
|
motions_refined = []
|
||||||
|
for m in motions:
|
||||||
|
if m == []:
|
||||||
|
continue
|
||||||
|
self.logger.info("Refining:'{}'".format(m))
|
||||||
|
motions_refined.append(self.down_and_up(m))
|
||||||
|
return motions_refined
|
||||||
|
|
||||||
def translate(self, val, lmin, lmax, rmin, rmax):
|
def translate(self, val, lmin, lmax, rmin, rmax):
|
||||||
lspan = lmax - lmin
|
lspan = lmax - lmin
|
||||||
@@ -39,3 +144,4 @@ class SVGProcessor():
|
|||||||
y = self.translate(ypix, 0, y_pixels, 0, 1)
|
y = self.translate(ypix, 0, y_pixels, 0, 1)
|
||||||
return (x,y)
|
return (x,y)
|
||||||
return map_point
|
return map_point
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user