Update SVG handling for new example images
This commit is contained in:
@@ -71,6 +71,8 @@ ros2 run drawing_controller drawing_controller svg/test.svg
|
|||||||
## Creating compatible SVG images
|
## Creating compatible SVG images
|
||||||
https://github.com/visioncortex/vtracer
|
https://github.com/visioncortex/vtracer
|
||||||
|
|
||||||
|
Use single layer (g) SVGs
|
||||||
|
|
||||||
## ROS2 rpi4
|
## ROS2 rpi4
|
||||||
https://github.com/ros-realtime/ros-realtime-rpi4-image/releases
|
https://github.com/ros-realtime/ros-realtime-rpi4-image/releases
|
||||||
|
|
||||||
|
|||||||
@@ -138,12 +138,13 @@ class DrawingController(Node):
|
|||||||
def timer_callback(self):
|
def timer_callback(self):
|
||||||
if self.busy:
|
if self.busy:
|
||||||
return
|
return
|
||||||
|
if self.i >= len(self.svg):
|
||||||
|
self.get_logger().info('Finished executing all motions from SVG')
|
||||||
|
exit()
|
||||||
next_motion = self.svg[self.i]
|
next_motion = self.svg[self.i]
|
||||||
motion = Motion()
|
motion = Motion()
|
||||||
self.append_points(motion, next_motion)
|
self.append_points(motion, next_motion)
|
||||||
self.i = self.i + 1
|
self.i = self.i + 1
|
||||||
if self.i >= len(self.svg):
|
|
||||||
exit()
|
|
||||||
self.get_logger().info('Executing motion: {}...'.format(motion.path[:10]))
|
self.get_logger().info('Executing motion: {}...'.format(motion.path[:10]))
|
||||||
self.send_goal(motion)
|
self.send_goal(motion)
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class SVGProcessor():
|
|||||||
Returns:
|
Returns:
|
||||||
primitive_fn ():
|
primitive_fn ():
|
||||||
'''
|
'''
|
||||||
def log_error(p, _):
|
def log_error(p):
|
||||||
self.logger.error("'{}' not supported".format(p.tag))
|
self.logger.error("'{}' not supported".format(p.tag))
|
||||||
return []
|
return []
|
||||||
return self.primitive_fns.get(primitive.tag, log_error)
|
return self.primitive_fns.get(primitive.tag, log_error)
|
||||||
@@ -104,19 +104,20 @@ class SVGProcessor():
|
|||||||
i = 0
|
i = 0
|
||||||
while i < len(pathstr):
|
while i < len(pathstr):
|
||||||
c = pathstr[i]
|
c = pathstr[i]
|
||||||
|
i += 1
|
||||||
# Single letter commands
|
# Single letter commands
|
||||||
if c.isalpha():
|
if c.isalpha():
|
||||||
path.append(c)
|
path.append(c)
|
||||||
|
|
||||||
# Numbers
|
# Numbers
|
||||||
if c == '-' or c.isdecimal():
|
if c == '-' or c.isdecimal():
|
||||||
s = ""
|
s = c
|
||||||
while i < len(pathstr) and not c.isspace():
|
while i < len(pathstr) and not (c.isspace() or c == ','):
|
||||||
|
c = pathstr[i]
|
||||||
|
if c != ',' and not c.isspace():
|
||||||
s = s + c
|
s = s + c
|
||||||
i += 1
|
i += 1
|
||||||
c = pathstr[i]
|
|
||||||
path.append(s)
|
path.append(s)
|
||||||
i += 1
|
|
||||||
|
|
||||||
# Parser
|
# Parser
|
||||||
self.logger.info("Parsing path :'{}...' with {} tokens".format(path[:20], len(path)))
|
self.logger.info("Parsing path :'{}...' with {} tokens".format(path[:20], len(path)))
|
||||||
@@ -128,8 +129,19 @@ class SVGProcessor():
|
|||||||
nonlocal i
|
nonlocal i
|
||||||
i += 1
|
i += 1
|
||||||
return float(path[i])
|
return float(path[i])
|
||||||
|
|
||||||
|
def isfloat(element):
|
||||||
|
#If you expect None to be passed:
|
||||||
|
if element is None:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
float(element)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
def nextisnum():
|
def nextisnum():
|
||||||
return path[i+1].isdecimal()
|
return i + 1 < len(path) and isfloat(path[i + 1])
|
||||||
def setpointup():
|
def setpointup():
|
||||||
nonlocal output
|
nonlocal output
|
||||||
p = self.map_point(x,y)
|
p = self.map_point(x,y)
|
||||||
@@ -198,7 +210,26 @@ class SVGProcessor():
|
|||||||
i += 1
|
i += 1
|
||||||
continue
|
continue
|
||||||
if (w == "c"):
|
if (w == "c"):
|
||||||
self.logger.error("SVG path parser '{}' not implemented".format(w))
|
while True:
|
||||||
|
# https://github.com/sintef/Splipy/tree/master/examples
|
||||||
|
control_points = [(x,y),
|
||||||
|
(x + getnum(), y + getnum()),
|
||||||
|
(x + getnum(), y + getnum()),
|
||||||
|
(x + getnum(), y + getnum())]
|
||||||
|
control_points = np.array(control_points)
|
||||||
|
n = 10
|
||||||
|
curve = cf.cubic_curve(control_points)
|
||||||
|
lin = np.linspace(curve.start(0), curve.end(0), n)
|
||||||
|
coordinates = curve(lin)
|
||||||
|
coordinates = np.nan_to_num(coordinates)
|
||||||
|
#self.logger.info("Appending curve points: {}".format(coordinates))
|
||||||
|
x = coordinates[-1][0]
|
||||||
|
y = coordinates[-1][1]
|
||||||
|
appendpoints(coordinates)
|
||||||
|
if not nextisnum():
|
||||||
|
break
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
if (w == "S"):
|
if (w == "S"):
|
||||||
self.logger.error("SVG path parser '{}' not implemented".format(w))
|
self.logger.error("SVG path parser '{}' not implemented".format(w))
|
||||||
if (w == "s"):
|
if (w == "s"):
|
||||||
@@ -226,7 +257,7 @@ class SVGProcessor():
|
|||||||
self.logger.error("SVG path parser panic mode at '{}'".format(w))
|
self.logger.error("SVG path parser panic mode at '{}'".format(w))
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
self.logger.info("Finished parsing path :'{}...' with {} points".format(output[:20], len(output)))
|
self.logger.info("Finished parsing path :'{}...' with {} points".format(output[:3], len(output)))
|
||||||
return output
|
return output
|
||||||
|
|
||||||
# https://stackoverflow.com/questions/30232031/how-can-i-strip-namespaces-out-of-an-lxml-tree
|
# https://stackoverflow.com/questions/30232031/how-can-i-strip-namespaces-out-of-an-lxml-tree
|
||||||
@@ -245,28 +276,38 @@ class SVGProcessor():
|
|||||||
svg = xml.getroot()
|
svg = xml.getroot()
|
||||||
svg = self.strip_ns_prefix(svg)
|
svg = self.strip_ns_prefix(svg)
|
||||||
|
|
||||||
if 'width' in svg.attrib:
|
if 'viewBox' in svg.attrib:
|
||||||
|
vb = svg.get('viewBox').split(' ')
|
||||||
|
self.map_point = self.map_point_function(float(vb[2]),
|
||||||
|
float(vb[3]))
|
||||||
|
self.logger.info("Got width:{} and height:{} from viewBox".format(vb[2],vb[3]))
|
||||||
|
elif 'width' in svg.attrib:
|
||||||
self.map_point = self.map_point_function(float(svg.get('width')),
|
self.map_point = self.map_point_function(float(svg.get('width')),
|
||||||
float(svg.get('height')))
|
float(svg.get('height')))
|
||||||
elif 'viewBox' in svg.attrib:
|
self.logger.info("Got width:{} and height:{} from width and height attributes".format(svg.get('width'),svg.get('height')))
|
||||||
# TODO parse viewBox
|
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
self.logger.error("Unable to get SVG dimensions")
|
self.logger.error("Unable to get SVG dimensions")
|
||||||
|
|
||||||
motions = []
|
motions = []
|
||||||
|
def process_tags(svg):
|
||||||
|
nonlocal motions
|
||||||
for child in svg:
|
for child in svg:
|
||||||
self.logger.info("Attempting to process SVG primitive:'{}'".format(child.tag))
|
self.logger.debug("Attempting to process SVG primitive:'{}'".format(child.tag))
|
||||||
primitive_fn = self.primitive_line
|
primitive_fn = self.primitive_line
|
||||||
# path can consist of multiple primitives
|
# path can consist of multiple primitives
|
||||||
if (child.tag == 'path'):
|
if (child.tag == 'path'):
|
||||||
#for m in self.path_parser(child):
|
#for m in self.path_parser(child):
|
||||||
# motions.append(m)
|
# motions.append(m)
|
||||||
motions.append(self.path_parser(child))
|
motions.append(self.path_parser(child))
|
||||||
|
elif (child.tag == 'g'):
|
||||||
|
self.logger.info("Recursively processing SVG primitive:'{}'".format(child.tag))
|
||||||
|
process_tags(child)
|
||||||
else:
|
else:
|
||||||
primitive_fn = self.get_primitive_fn(child)
|
primitive_fn = self.get_primitive_fn(child)
|
||||||
motions.append(primitive_fn(child))
|
motions.append(primitive_fn(child))
|
||||||
|
|
||||||
|
process_tags(svg)
|
||||||
|
|
||||||
motions_refined = []
|
motions_refined = []
|
||||||
for m in motions:
|
for m in motions:
|
||||||
if m == []:
|
if m == []:
|
||||||
|
|||||||
Reference in New Issue
Block a user