kopia lustrzana https://github.com/evil-mad/EggBot
Convert tab indents to spaces.
rodzic
e98ed3a91e
commit
e5c5477253
|
@ -43,29 +43,29 @@ LINE_SKIP = 6 # Baseline skip between lines of text
|
|||
# be used to deal with a face being removed from hersheydata.py
|
||||
|
||||
map_our_names_to_hersheydata = {
|
||||
'astrology': 'astrology',
|
||||
'cursive': 'cursive',
|
||||
'cyrillic': 'cyrillic',
|
||||
'futural': 'futural',
|
||||
'futuram': 'futuram',
|
||||
'gothiceng': 'gothiceng',
|
||||
'gothicger': 'gothicger',
|
||||
'gothicita': 'gothicita',
|
||||
'greek': 'greek',
|
||||
'japanese': 'japanese',
|
||||
'markers': 'markers',
|
||||
'mathlow': 'mathlow',
|
||||
'mathupp': 'mathupp',
|
||||
'meteorology': 'meteorology',
|
||||
'music': 'music',
|
||||
'scriptc': 'scriptc',
|
||||
'scripts': 'scripts',
|
||||
'symbolic': 'symbolic',
|
||||
'timesg': 'timesg',
|
||||
'timesi': 'timesi',
|
||||
'timesib': 'timesib',
|
||||
'timesr': 'timesr',
|
||||
'timesrb': 'timesrb'}
|
||||
'astrology': 'astrology',
|
||||
'cursive': 'cursive',
|
||||
'cyrillic': 'cyrillic',
|
||||
'futural': 'futural',
|
||||
'futuram': 'futuram',
|
||||
'gothiceng': 'gothiceng',
|
||||
'gothicger': 'gothicger',
|
||||
'gothicita': 'gothicita',
|
||||
'greek': 'greek',
|
||||
'japanese': 'japanese',
|
||||
'markers': 'markers',
|
||||
'mathlow': 'mathlow',
|
||||
'mathupp': 'mathupp',
|
||||
'meteorology': 'meteorology',
|
||||
'music': 'music',
|
||||
'scriptc': 'scriptc',
|
||||
'scripts': 'scripts',
|
||||
'symbolic': 'symbolic',
|
||||
'timesg': 'timesg',
|
||||
'timesi': 'timesi',
|
||||
'timesib': 'timesib',
|
||||
'timesr': 'timesr',
|
||||
'timesrb': 'timesrb'}
|
||||
|
||||
|
||||
# The following two routines are lifted with impunity from Windell H. Oskay's
|
||||
|
@ -73,198 +73,198 @@ map_our_names_to_hersheydata = {
|
|||
# Copyright 2011, Windell H. Oskay, www.evilmadscientist.com
|
||||
|
||||
def draw_svg_text(char, face, offset, vertoffset, parent):
|
||||
style = {'stroke': '#000000', 'fill': 'none'}
|
||||
pathString = face[char]
|
||||
splitString = pathString.split()
|
||||
midpoint = offset - int(splitString[0])
|
||||
i = pathString.find("M")
|
||||
if i >= 0:
|
||||
pathString = pathString[i:] # portion after first move
|
||||
trans = 'translate(' + str(midpoint) + ',' + str(vertoffset) + ')'
|
||||
text_attribs = {'style': simplestyle.formatStyle(style), 'd': pathString, 'transform': trans}
|
||||
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), text_attribs)
|
||||
return midpoint + int(splitString[1]) # new offset value
|
||||
style = {'stroke': '#000000', 'fill': 'none'}
|
||||
pathString = face[char]
|
||||
splitString = pathString.split()
|
||||
midpoint = offset - int(splitString[0])
|
||||
i = pathString.find("M")
|
||||
if i >= 0:
|
||||
pathString = pathString[i:] # portion after first move
|
||||
trans = 'translate(' + str(midpoint) + ',' + str(vertoffset) + ')'
|
||||
text_attribs = {'style': simplestyle.formatStyle(style), 'd': pathString, 'transform': trans}
|
||||
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), text_attribs)
|
||||
return midpoint + int(splitString[1]) # new offset value
|
||||
|
||||
|
||||
def renderText(parent, w, y, text, typeface):
|
||||
"""
|
||||
Render a string of text starting from the point (w, y) and using
|
||||
the supplied typeface data.
|
||||
"""
|
||||
"""
|
||||
Render a string of text starting from the point (w, y) and using
|
||||
the supplied typeface data.
|
||||
"""
|
||||
|
||||
if (text is None) or (text == ''):
|
||||
return
|
||||
if (text is None) or (text == ''):
|
||||
return
|
||||
|
||||
spacing = 3 # spacing between letters
|
||||
letterVals = [ord(q) - 32 for q in text]
|
||||
spacing = 3 # spacing between letters
|
||||
letterVals = [ord(q) - 32 for q in text]
|
||||
|
||||
for q in letterVals:
|
||||
if (q < 0) or (q > 95):
|
||||
w += 2 * spacing
|
||||
else:
|
||||
w = draw_svg_text(q, typeface, w, y, parent)
|
||||
for q in letterVals:
|
||||
if (q < 0) or (q > 95):
|
||||
w += 2 * spacing
|
||||
else:
|
||||
w = draw_svg_text(q, typeface, w, y, parent)
|
||||
|
||||
return w
|
||||
return w
|
||||
|
||||
|
||||
def renderLine(parent, x, y, line, typeface1, typeface2):
|
||||
"""
|
||||
Render a single line of text:
|
||||
+ The text runs horizontally from left to right starting at the point (x,y)
|
||||
+ The first character of the line is written using "typeface1"
|
||||
+ The remainder of the line of text is written using "typeface2"
|
||||
"""
|
||||
Render a single line of text:
|
||||
+ The text runs horizontally from left to right starting at the point (x,y)
|
||||
+ The first character of the line is written using "typeface1"
|
||||
+ The remainder of the line of text is written using "typeface2"
|
||||
|
||||
The entire line is stored as individual paths which are child elements
|
||||
of the SVG element "parent". The text in typeface2 (line[1:]) is also
|
||||
placed in it's own subgroup. The leading character is not placed in
|
||||
that subgroup. The reasoning is that the user may want to pick out
|
||||
the first character of each line of text and put them in another layer
|
||||
for plotting in a different color.
|
||||
"""
|
||||
The entire line is stored as individual paths which are child elements
|
||||
of the SVG element "parent". The text in typeface2 (line[1:]) is also
|
||||
placed in it's own subgroup. The leading character is not placed in
|
||||
that subgroup. The reasoning is that the user may want to pick out
|
||||
the first character of each line of text and put them in another layer
|
||||
for plotting in a different color.
|
||||
"""
|
||||
|
||||
# Return now if there's nothing to do
|
||||
if (line is None) or (line == ''):
|
||||
return
|
||||
# Return now if there's nothing to do
|
||||
if (line is None) or (line == ''):
|
||||
return
|
||||
|
||||
# Render the first character
|
||||
x = renderText(parent, x, y, [line[0]], typeface1)
|
||||
# Render the first character
|
||||
x = renderText(parent, x, y, [line[0]], typeface1)
|
||||
|
||||
# Render the rest of the line
|
||||
line = line[1:]
|
||||
if line == '':
|
||||
return
|
||||
g = inkex.etree.SubElement(parent, 'g')
|
||||
renderText(g, x, y, line, typeface2)
|
||||
# Render the rest of the line
|
||||
line = line[1:]
|
||||
if line == '':
|
||||
return
|
||||
g = inkex.etree.SubElement(parent, 'g')
|
||||
renderText(g, x, y, line, typeface2)
|
||||
|
||||
|
||||
class AcrosticText(inkex.Effect):
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self):
|
||||
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option("--tab", # NOTE: value is not used.
|
||||
action="store", type="string", dest="tab", default="splash",
|
||||
help="The active tab when Apply was pressed")
|
||||
self.OptionParser.add_option("--line01", action="store",
|
||||
type="string", dest="line1", default="")
|
||||
self.OptionParser.add_option("--line02", action="store",
|
||||
type="string", dest="line2", default="")
|
||||
self.OptionParser.add_option("--line03", action="store",
|
||||
type="string", dest="line3", default="")
|
||||
self.OptionParser.add_option("--line04", action="store",
|
||||
type="string", dest="line4", default="")
|
||||
self.OptionParser.add_option("--line05", action="store",
|
||||
type="string", dest="line5", default="")
|
||||
self.OptionParser.add_option("--line06", action="store",
|
||||
type="string", dest="line6", default="")
|
||||
self.OptionParser.add_option("--line07", action="store",
|
||||
type="string", dest="line7", default="")
|
||||
self.OptionParser.add_option("--line08", action="store",
|
||||
type="string", dest="line8", default="")
|
||||
self.OptionParser.add_option("--line09", action="store",
|
||||
type="string", dest="line9", default="")
|
||||
self.OptionParser.add_option("--line10", action="store",
|
||||
type="string", dest="line10", default="")
|
||||
self.OptionParser.add_option("--line11", action="store",
|
||||
type="string", dest="line11", default="")
|
||||
self.OptionParser.add_option("--line12", action="store",
|
||||
type="string", dest="line12", default="")
|
||||
self.OptionParser.add_option("--face1",
|
||||
action="store", type="string", dest="face1", default="scriptc",
|
||||
help="Leading font typeface")
|
||||
self.OptionParser.add_option("--face2", action="store",
|
||||
type="string", dest="face2", default="scripts",
|
||||
help="Secondary typeface")
|
||||
self.OptionParser.add_option("--flip", action="store", type="inkbool",
|
||||
dest="flip", default=False,
|
||||
help="Flip the text for plotting with the egg's bottom at the egg motor")
|
||||
self.OptionParser.add_option("--stretch",
|
||||
action="store", type="inkbool", dest="stretch", default=True,
|
||||
help="Stretch the text horizontally to account for egg distortions")
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option("--tab", # NOTE: value is not used.
|
||||
action="store", type="string", dest="tab", default="splash",
|
||||
help="The active tab when Apply was pressed")
|
||||
self.OptionParser.add_option("--line01", action="store",
|
||||
type="string", dest="line1", default="")
|
||||
self.OptionParser.add_option("--line02", action="store",
|
||||
type="string", dest="line2", default="")
|
||||
self.OptionParser.add_option("--line03", action="store",
|
||||
type="string", dest="line3", default="")
|
||||
self.OptionParser.add_option("--line04", action="store",
|
||||
type="string", dest="line4", default="")
|
||||
self.OptionParser.add_option("--line05", action="store",
|
||||
type="string", dest="line5", default="")
|
||||
self.OptionParser.add_option("--line06", action="store",
|
||||
type="string", dest="line6", default="")
|
||||
self.OptionParser.add_option("--line07", action="store",
|
||||
type="string", dest="line7", default="")
|
||||
self.OptionParser.add_option("--line08", action="store",
|
||||
type="string", dest="line8", default="")
|
||||
self.OptionParser.add_option("--line09", action="store",
|
||||
type="string", dest="line9", default="")
|
||||
self.OptionParser.add_option("--line10", action="store",
|
||||
type="string", dest="line10", default="")
|
||||
self.OptionParser.add_option("--line11", action="store",
|
||||
type="string", dest="line11", default="")
|
||||
self.OptionParser.add_option("--line12", action="store",
|
||||
type="string", dest="line12", default="")
|
||||
self.OptionParser.add_option("--face1",
|
||||
action="store", type="string", dest="face1", default="scriptc",
|
||||
help="Leading font typeface")
|
||||
self.OptionParser.add_option("--face2", action="store",
|
||||
type="string", dest="face2", default="scripts",
|
||||
help="Secondary typeface")
|
||||
self.OptionParser.add_option("--flip", action="store", type="inkbool",
|
||||
dest="flip", default=False,
|
||||
help="Flip the text for plotting with the egg's bottom at the egg motor")
|
||||
self.OptionParser.add_option("--stretch",
|
||||
action="store", type="inkbool", dest="stretch", default=True,
|
||||
help="Stretch the text horizontally to account for egg distortions")
|
||||
|
||||
def effect(self):
|
||||
def effect(self):
|
||||
|
||||
# Process the lines, ignoring leading or trailing blank lines
|
||||
# and collapsing multiple internal runs of blank lines into a
|
||||
# single blank line.
|
||||
lines = []
|
||||
prior_empty = False
|
||||
for i in range(1, 13):
|
||||
line = eval('self.options.line' + str(i)).strip()
|
||||
if line == '':
|
||||
if len(lines) != 0:
|
||||
prior_empty = True
|
||||
else:
|
||||
if prior_empty:
|
||||
lines.append('')
|
||||
prior_empty = False
|
||||
lines.append(line)
|
||||
# Process the lines, ignoring leading or trailing blank lines
|
||||
# and collapsing multiple internal runs of blank lines into a
|
||||
# single blank line.
|
||||
lines = []
|
||||
prior_empty = False
|
||||
for i in range(1, 13):
|
||||
line = eval('self.options.line' + str(i)).strip()
|
||||
if line == '':
|
||||
if len(lines) != 0:
|
||||
prior_empty = True
|
||||
else:
|
||||
if prior_empty:
|
||||
lines.append('')
|
||||
prior_empty = False
|
||||
lines.append(line)
|
||||
|
||||
# Return now if there are no lines to print
|
||||
line_count = len(lines)
|
||||
if line_count == 0:
|
||||
return
|
||||
# Return now if there are no lines to print
|
||||
line_count = len(lines)
|
||||
if line_count == 0:
|
||||
return
|
||||
|
||||
# Determine how much vertical room we need for our text
|
||||
h = line_count * MAX_H + (line_count - 1) * LINE_SKIP
|
||||
# Determine how much vertical room we need for our text
|
||||
h = line_count * MAX_H + (line_count - 1) * LINE_SKIP
|
||||
|
||||
svg = self.document.getroot()
|
||||
doc_height = self.unittouu(svg.attrib['height'])
|
||||
if doc_height <= 0:
|
||||
doc_height = HEIGHT
|
||||
doc_width = self.unittouu(svg.attrib['width'])
|
||||
if doc_width <= 0:
|
||||
doc_width = WIDTH
|
||||
svg = self.document.getroot()
|
||||
doc_height = self.unittouu(svg.attrib['height'])
|
||||
if doc_height <= 0:
|
||||
doc_height = HEIGHT
|
||||
doc_width = self.unittouu(svg.attrib['width'])
|
||||
if doc_width <= 0:
|
||||
doc_width = WIDTH
|
||||
|
||||
# Scale to doc_height pixels high
|
||||
scale_y = float(doc_height) / float(h)
|
||||
if self.options.stretch:
|
||||
scale_x = scale_y * 1.5
|
||||
else:
|
||||
scale_x = scale_y
|
||||
# Scale to doc_height pixels high
|
||||
scale_y = float(doc_height) / float(h)
|
||||
if self.options.stretch:
|
||||
scale_x = scale_y * 1.5
|
||||
else:
|
||||
scale_x = scale_y
|
||||
|
||||
# Determine where to position the text
|
||||
# We do not bother centering the text horizontally
|
||||
# to do that we would need to pre-render the text to determine
|
||||
# the length of the longest line. That's too much bother so
|
||||
# we just skip that potential nice-to-have.
|
||||
x = float(doc_width) / (2.0 * scale_x)
|
||||
y = float(MAX_H) / scale_y
|
||||
# Determine where to position the text
|
||||
# We do not bother centering the text horizontally
|
||||
# to do that we would need to pre-render the text to determine
|
||||
# the length of the longest line. That's too much bother so
|
||||
# we just skip that potential nice-to-have.
|
||||
x = float(doc_width) / (2.0 * scale_x)
|
||||
y = float(MAX_H) / scale_y
|
||||
|
||||
# Get the two type faces
|
||||
name1 = self.options.face1
|
||||
if name1 in map_our_names_to_hersheydata:
|
||||
name1 = map_our_names_to_hersheydata[name1]
|
||||
face1 = eval('hersheydata.' + name1)
|
||||
name2 = self.options.face2
|
||||
if name2 in map_our_names_to_hersheydata:
|
||||
name2 = map_our_names_to_hersheydata[name2]
|
||||
face2 = eval('hersheydata.' + name2)
|
||||
# Get the two type faces
|
||||
name1 = self.options.face1
|
||||
if name1 in map_our_names_to_hersheydata:
|
||||
name1 = map_our_names_to_hersheydata[name1]
|
||||
face1 = eval('hersheydata.' + name1)
|
||||
name2 = self.options.face2
|
||||
if name2 in map_our_names_to_hersheydata:
|
||||
name2 = map_our_names_to_hersheydata[name2]
|
||||
face2 = eval('hersheydata.' + name2)
|
||||
|
||||
# Create the group which will contain all of the text
|
||||
# We DO NOT make this a child of the current layer as that
|
||||
# would subject us to any transforms it might have. That's
|
||||
# an issue even in the simple case of someone opening a default
|
||||
# document and then changing its dimensions to 3200 x 800:
|
||||
# Inkscape imposes a transform in that situation. While that
|
||||
# transform is no big deal, it's another complication in trying
|
||||
# to just make the resulting text look right (right size, right
|
||||
# approximate position, etc.).
|
||||
# Create the group which will contain all of the text
|
||||
# We DO NOT make this a child of the current layer as that
|
||||
# would subject us to any transforms it might have. That's
|
||||
# an issue even in the simple case of someone opening a default
|
||||
# document and then changing its dimensions to 3200 x 800:
|
||||
# Inkscape imposes a transform in that situation. While that
|
||||
# transform is no big deal, it's another complication in trying
|
||||
# to just make the resulting text look right (right size, right
|
||||
# approximate position, etc.).
|
||||
|
||||
if self.options.flip:
|
||||
attribs = {'transform': 'matrix(-%f,0,0,-%f,%d,%d)' % (scale_x, scale_y, doc_width, doc_height)}
|
||||
else:
|
||||
attribs = {'transform': 'scale(%f,%f)' % (scale_x, scale_y)}
|
||||
container = inkex.etree.SubElement(self.document.getroot(), 'g', attribs)
|
||||
if self.options.flip:
|
||||
attribs = {'transform': 'matrix(-%f,0,0,-%f,%d,%d)' % (scale_x, scale_y, doc_width, doc_height)}
|
||||
else:
|
||||
attribs = {'transform': 'scale(%f,%f)' % (scale_x, scale_y)}
|
||||
container = inkex.etree.SubElement(self.document.getroot(), 'g', attribs)
|
||||
|
||||
# Finally, we render each line of text
|
||||
for i in range(0, len(lines)):
|
||||
if lines[i] != '':
|
||||
g = inkex.etree.SubElement(container, 'g')
|
||||
renderLine(g, x, y, lines[i], face1, face2)
|
||||
y += MAX_H + LINE_SKIP
|
||||
# Finally, we render each line of text
|
||||
for i in range(0, len(lines)):
|
||||
if lines[i] != '':
|
||||
g = inkex.etree.SubElement(container, 'g')
|
||||
renderLine(g, x, y, lines[i], face1, face2)
|
||||
y += MAX_H + LINE_SKIP
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
e = AcrosticText()
|
||||
e.affect()
|
||||
e = AcrosticText()
|
||||
e.affect()
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -34,84 +34,84 @@ import simplestyle
|
|||
|
||||
class EggBot_PostProcessTraceBitmap(inkex.Effect):
|
||||
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option(
|
||||
"--outlineRegions", action="store", dest="outlineRegions",
|
||||
type="inkbool", default=True,
|
||||
help="Outline the regions with a stroked line of the same color as the region itself")
|
||||
self.OptionParser.add_option(
|
||||
"--fillRegions", action="store", dest="fillRegions",
|
||||
type="inkbool", default=True,
|
||||
help="Fill regions with color")
|
||||
self.OptionParser.add_option(
|
||||
"--removeImage", action="store", dest="removeImage",
|
||||
type="inkbool", default=True,
|
||||
help="Remove the traced bitmap image from the drawing")
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option(
|
||||
"--outlineRegions", action="store", dest="outlineRegions",
|
||||
type="inkbool", default=True,
|
||||
help="Outline the regions with a stroked line of the same color as the region itself")
|
||||
self.OptionParser.add_option(
|
||||
"--fillRegions", action="store", dest="fillRegions",
|
||||
type="inkbool", default=True,
|
||||
help="Fill regions with color")
|
||||
self.OptionParser.add_option(
|
||||
"--removeImage", action="store", dest="removeImage",
|
||||
type="inkbool", default=True,
|
||||
help="Remove the traced bitmap image from the drawing")
|
||||
|
||||
def effect(self):
|
||||
def effect(self):
|
||||
|
||||
root = self.document.getroot()
|
||||
root = self.document.getroot()
|
||||
|
||||
count = 0
|
||||
for path in self.document.xpath('//svg:path', namespaces=inkex.NSS):
|
||||
count = 0
|
||||
for path in self.document.xpath('//svg:path', namespaces=inkex.NSS):
|
||||
|
||||
# Default settings for now
|
||||
stroke, fill, color = ('none', 'none', 'unknown')
|
||||
# Default settings for now
|
||||
stroke, fill, color = ('none', 'none', 'unknown')
|
||||
|
||||
# Get the paths style attribute
|
||||
style = simplestyle.parseStyle(path.get('style', ''))
|
||||
# Obtain the fill color from the path's style attribute
|
||||
if 'fill' in style:
|
||||
color = style['fill']
|
||||
if self.options.fillRegions:
|
||||
fill = color
|
||||
if self.options.outlineRegions:
|
||||
stroke = color
|
||||
# Get the paths style attribute
|
||||
style = simplestyle.parseStyle(path.get('style', ''))
|
||||
# Obtain the fill color from the path's style attribute
|
||||
if 'fill' in style:
|
||||
color = style['fill']
|
||||
if self.options.fillRegions:
|
||||
fill = color
|
||||
if self.options.outlineRegions:
|
||||
stroke = color
|
||||
|
||||
# Now add or change the fill color in the path's style
|
||||
style['fill'] = fill
|
||||
# Now add or change the fill color in the path's style
|
||||
style['fill'] = fill
|
||||
|
||||
# Add or change the stroke behavior in the path's style
|
||||
style['stroke'] = stroke
|
||||
# Add or change the stroke behavior in the path's style
|
||||
style['stroke'] = stroke
|
||||
|
||||
# And change the style attribute for the path
|
||||
path.set('style', simplestyle.formatStyle(style))
|
||||
# And change the style attribute for the path
|
||||
path.set('style', simplestyle.formatStyle(style))
|
||||
|
||||
# Create a group <g> element under the document root
|
||||
layer = inkex.etree.SubElement(root, inkex.addNS('g', 'svg'))
|
||||
# Create a group <g> element under the document root
|
||||
layer = inkex.etree.SubElement(root, inkex.addNS('g', 'svg'))
|
||||
|
||||
# Add Inkscape layer attributes to this new group
|
||||
count += 1
|
||||
layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
|
||||
layer.set(inkex.addNS('label', 'inkscape'), '%d - %s' % (count, color))
|
||||
# Add Inkscape layer attributes to this new group
|
||||
count += 1
|
||||
layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
|
||||
layer.set(inkex.addNS('label', 'inkscape'), '%d - %s' % (count, color))
|
||||
|
||||
# Now move this path from where it was to being a child
|
||||
# of this new group/layer we just made
|
||||
layer.append(path)
|
||||
# Now move this path from where it was to being a child
|
||||
# of this new group/layer we just made
|
||||
layer.append(path)
|
||||
|
||||
# Remove any image
|
||||
# For color scans, Trace Bitmap seems to put the
|
||||
# image in the same layer & group as the traced regions.
|
||||
# BUT, for gray scans, it seems to leave the image by
|
||||
# itself as a child of the root document
|
||||
# Remove any image
|
||||
# For color scans, Trace Bitmap seems to put the
|
||||
# image in the same layer & group as the traced regions.
|
||||
# BUT, for gray scans, it seems to leave the image by
|
||||
# itself as a child of the root document
|
||||
|
||||
if self.options.removeImage:
|
||||
for node in self.document.xpath('//svg:image', namespaces=inkex.NSS):
|
||||
parent = node.getparent()
|
||||
if (parent.tag == 'svg') or \
|
||||
(parent.tag == inkex.addNS('svg', 'svg')):
|
||||
parent.remove(node)
|
||||
else:
|
||||
gparent = parent.getparent()
|
||||
try:
|
||||
gparent.remove(parent)
|
||||
except:
|
||||
parent.remove(node)
|
||||
if self.options.removeImage:
|
||||
for node in self.document.xpath('//svg:image', namespaces=inkex.NSS):
|
||||
parent = node.getparent()
|
||||
if (parent.tag == 'svg') or \
|
||||
(parent.tag == inkex.addNS('svg', 'svg')):
|
||||
parent.remove(node)
|
||||
else:
|
||||
gparent = parent.getparent()
|
||||
try:
|
||||
gparent.remove(parent)
|
||||
except:
|
||||
parent.remove(node)
|
||||
|
||||
inkex.errormsg(gettext.gettext('Finished. Created %d layers') % count)
|
||||
inkex.errormsg(gettext.gettext('Finished. Created %d layers') % count)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
e = EggBot_PostProcessTraceBitmap()
|
||||
e.affect()
|
||||
e = EggBot_PostProcessTraceBitmap()
|
||||
e.affect()
|
||||
|
|
|
@ -37,336 +37,336 @@ VERSION = 1
|
|||
|
||||
|
||||
def parseDesc(str):
|
||||
"""
|
||||
Create a dictionary from string description
|
||||
"""
|
||||
"""
|
||||
Create a dictionary from string description
|
||||
"""
|
||||
|
||||
if str is None:
|
||||
return {}
|
||||
else:
|
||||
return dict([tok.split(':') for tok in str.split(';') if len(tok)])
|
||||
if str is None:
|
||||
return {}
|
||||
else:
|
||||
return dict([tok.split(':') for tok in str.split(';') if len(tok)])
|
||||
|
||||
|
||||
def formatDesc(d):
|
||||
"""
|
||||
Format an inline name1:value1;name2:value2;... style attribute value
|
||||
from a dictionary
|
||||
"""
|
||||
"""
|
||||
Format an inline name1:value1;name2:value2;... style attribute value
|
||||
from a dictionary
|
||||
"""
|
||||
|
||||
return ';'.join([atr + ':' + str(val) for atr, val in d.iteritems()])
|
||||
return ';'.join([atr + ':' + str(val) for atr, val in d.iteritems()])
|
||||
|
||||
|
||||
def drawSine(cycles=8, rn=0, rm=0, nPoints=50, offset=[0, 0],
|
||||
height=200, width=3200, rescale=0.98, bound1='', bound2='', fun='sine', spline=True):
|
||||
"""
|
||||
cycles
|
||||
Number of periods to plot within the rectangle of width 'width'
|
||||
height=200, width=3200, rescale=0.98, bound1='', bound2='', fun='sine', spline=True):
|
||||
"""
|
||||
cycles
|
||||
Number of periods to plot within the rectangle of width 'width'
|
||||
|
||||
rn, rm
|
||||
Start the function (on the left edge) at the value x = 2 * pi * rn / rm.
|
||||
When rm = 0, function is started at x = 0.
|
||||
rn, rm
|
||||
Start the function (on the left edge) at the value x = 2 * pi * rn / rm.
|
||||
When rm = 0, function is started at x = 0.
|
||||
|
||||
nPoints
|
||||
The number of points to sample the function at. Since the function is
|
||||
approximated using Bezier cubic splines, this isn't the number of points
|
||||
to plot.
|
||||
nPoints
|
||||
The number of points to sample the function at. Since the function is
|
||||
approximated using Bezier cubic splines, this isn't the number of points
|
||||
to plot.
|
||||
|
||||
offset
|
||||
(x, y) coordinate of the lower left corner of the bounding rectangle
|
||||
in which to plot the function.
|
||||
offset
|
||||
(x, y) coordinate of the lower left corner of the bounding rectangle
|
||||
in which to plot the function.
|
||||
|
||||
height, width
|
||||
The height and width of the rectangle in which to plot the function.
|
||||
Ignored when bounding functions, bound1 and bound2, are supplied.
|
||||
height, width
|
||||
The height and width of the rectangle in which to plot the function.
|
||||
Ignored when bounding functions, bound1 and bound2, are supplied.
|
||||
|
||||
rescale
|
||||
Multiplicative Y-scaling factor by which to rescale the plotted function
|
||||
so that it does not fully reach the vertical limits of its bounds. This
|
||||
aids in Eggbot plots by preventing lines from touching and overlapping.
|
||||
rescale
|
||||
Multiplicative Y-scaling factor by which to rescale the plotted function
|
||||
so that it does not fully reach the vertical limits of its bounds. This
|
||||
aids in Eggbot plots by preventing lines from touching and overlapping.
|
||||
|
||||
bound1, bound2
|
||||
Descriptions of upper and lower bounding functions by which to limit the
|
||||
vertical range of the function to be plotted.
|
||||
bound1, bound2
|
||||
Descriptions of upper and lower bounding functions by which to limit the
|
||||
vertical range of the function to be plotted.
|
||||
|
||||
fun
|
||||
May be either 'sine' or 'lace'.
|
||||
"""
|
||||
fun
|
||||
May be either 'sine' or 'lace'.
|
||||
"""
|
||||
|
||||
"""
|
||||
A complicated way of plotting y = sin(x)
|
||||
"""
|
||||
A complicated way of plotting y = sin(x)
|
||||
|
||||
Complicated because we wish to generate the sine wave using a
|
||||
parametric representation. For plotting a single sine wave in
|
||||
Cartesian coordinates, this is overkill. However, it's useful
|
||||
for when we want to compress and expand the amplitude of the
|
||||
sine wave in accord with upper and lower boundaries which themselves
|
||||
are functions. By parameterizing everything in sight with the
|
||||
same parameter s and restricting s to the range [0, 1], our life
|
||||
is made much easier.
|
||||
"""
|
||||
Complicated because we wish to generate the sine wave using a
|
||||
parametric representation. For plotting a single sine wave in
|
||||
Cartesian coordinates, this is overkill. However, it's useful
|
||||
for when we want to compress and expand the amplitude of the
|
||||
sine wave in accord with upper and lower boundaries which themselves
|
||||
are functions. By parameterizing everything in sight with the
|
||||
same parameter s and restricting s to the range [0, 1], our life
|
||||
is made much easier.
|
||||
"""
|
||||
|
||||
bounded = False
|
||||
bounded = False
|
||||
|
||||
if (not (bound1 is None)) and (not (bound2 is None)) and \
|
||||
(len(bound1) > 0) and (len(bound2) > 0):
|
||||
if (not (bound1 is None)) and (not (bound2 is None)) and \
|
||||
(len(bound1) > 0) and (len(bound2) > 0):
|
||||
|
||||
func = parseDesc(bound1)
|
||||
if len(func) == 0:
|
||||
return None, None
|
||||
m1 = int(func['rm'])
|
||||
if m1 == 0:
|
||||
xMin1 = float(0)
|
||||
else:
|
||||
xMin1 = 2 * pi * float(func['rn']) / float(m1)
|
||||
xMax1 = xMin1 + 2 * pi * float(func['cycles'])
|
||||
yMin1 = float(-1)
|
||||
yMax1 = float(+1)
|
||||
yScale1 = float(func['height']) / (yMax1 - yMin1)
|
||||
yOffset1 = float(func['y'])
|
||||
Y1s = lambda s: yOffset1 - yScale1 * sin(xMin1 + (xMax1 - xMin1) * s)
|
||||
func = parseDesc(bound1)
|
||||
if len(func) == 0:
|
||||
return None, None
|
||||
m1 = int(func['rm'])
|
||||
if m1 == 0:
|
||||
xMin1 = float(0)
|
||||
else:
|
||||
xMin1 = 2 * pi * float(func['rn']) / float(m1)
|
||||
xMax1 = xMin1 + 2 * pi * float(func['cycles'])
|
||||
yMin1 = float(-1)
|
||||
yMax1 = float(+1)
|
||||
yScale1 = float(func['height']) / (yMax1 - yMin1)
|
||||
yOffset1 = float(func['y'])
|
||||
Y1s = lambda s: yOffset1 - yScale1 * sin(xMin1 + (xMax1 - xMin1) * s)
|
||||
|
||||
func = parseDesc(bound2)
|
||||
if len(func) == 0:
|
||||
return None, None
|
||||
m2 = int(func['rm'])
|
||||
if m2 == 0:
|
||||
xMin2 = float(0)
|
||||
else:
|
||||
xMin2 = 2 * pi * float(func['rn']) / float(m2)
|
||||
xMax2 = xMin2 + 2 * pi * float(func['cycles'])
|
||||
yMin2 = float(-1)
|
||||
yMax2 = float(+1)
|
||||
yScale2 = float(func['height']) / (yMax2 - yMin2)
|
||||
yOffset2 = float(func['y'])
|
||||
Y2s = lambda s: yOffset2 - yScale2 * sin(xMin2 + (xMax2 - xMin2) * s)
|
||||
func = parseDesc(bound2)
|
||||
if len(func) == 0:
|
||||
return None, None
|
||||
m2 = int(func['rm'])
|
||||
if m2 == 0:
|
||||
xMin2 = float(0)
|
||||
else:
|
||||
xMin2 = 2 * pi * float(func['rn']) / float(m2)
|
||||
xMax2 = xMin2 + 2 * pi * float(func['cycles'])
|
||||
yMin2 = float(-1)
|
||||
yMax2 = float(+1)
|
||||
yScale2 = float(func['height']) / (yMax2 - yMin2)
|
||||
yOffset2 = float(func['y'])
|
||||
Y2s = lambda s: yOffset2 - yScale2 * sin(xMin2 + (xMax2 - xMin2) * s)
|
||||
|
||||
bounded = True
|
||||
bounded = True
|
||||
|
||||
rescale = float(rescale)
|
||||
xOffset = float(offset[0])
|
||||
yOffset = float(offset[1])
|
||||
rescale = float(rescale)
|
||||
xOffset = float(offset[0])
|
||||
yOffset = float(offset[1])
|
||||
|
||||
# Each cycle is 2pi
|
||||
n, m = int(0), int(0)
|
||||
r = 2 * pi * float(cycles)
|
||||
if (int(rm) == 0) or (int(rn) == 0):
|
||||
xMin = float(0)
|
||||
else:
|
||||
xMin = 2 * pi * float(rn) / float(rm)
|
||||
xMax = xMin + r
|
||||
xScale = float(width) / r # width / ( xMax - xMin )
|
||||
# Each cycle is 2pi
|
||||
n, m = int(0), int(0)
|
||||
r = 2 * pi * float(cycles)
|
||||
if (int(rm) == 0) or (int(rn) == 0):
|
||||
xMin = float(0)
|
||||
else:
|
||||
xMin = 2 * pi * float(rn) / float(rm)
|
||||
xMax = xMin + r
|
||||
xScale = float(width) / r # width / ( xMax - xMin )
|
||||
|
||||
yMin = float(-1)
|
||||
yMax = float(+1)
|
||||
yScale = float(height) / (yMax - yMin)
|
||||
yMin = float(-1)
|
||||
yMax = float(+1)
|
||||
yScale = float(height) / (yMax - yMin)
|
||||
|
||||
# Our parametric equations which map the results to our drawing window
|
||||
# Note the "-yScale" that's because in SVG, the y-axis runs "backwards"
|
||||
if (fun is None) or (fun == ''):
|
||||
fun = 'sine'
|
||||
fun = fun.lower()
|
||||
if fun == 'sine':
|
||||
Xs = lambda s: xOffset + xScale * (xMax - xMin) * s
|
||||
Ys = lambda s: yOffset - yScale * sin(xMin + (xMax - xMin) * s)
|
||||
dYdXs = lambda s: -yScale * cos(xMin + (xMax - xMin) * s) / xScale
|
||||
elif fun == 'lace':
|
||||
Xs = lambda s: xOffset + xScale * ((xMax - xMin) * s + 2 * sin(2 * s * (xMax - xMin) + pi))
|
||||
dXs = lambda s: xScale * (xMax - xMin) * (1.0 + 4.0 * cos(2 * s * (xMax - xMin) + pi))
|
||||
Ys = lambda s: yOffset - yScale * sin(xMin + (xMax - xMin) * s)
|
||||
dYs = lambda s: -yScale * cos(xMin + (xMax - xMin) * s) * (xMax - xMin)
|
||||
dYdXs = lambda s: dYs(s) / dXs(s)
|
||||
else:
|
||||
inkex.errormsg('Unknown function %s specified' % fun)
|
||||
return
|
||||
# Our parametric equations which map the results to our drawing window
|
||||
# Note the "-yScale" that's because in SVG, the y-axis runs "backwards"
|
||||
if (fun is None) or (fun == ''):
|
||||
fun = 'sine'
|
||||
fun = fun.lower()
|
||||
if fun == 'sine':
|
||||
Xs = lambda s: xOffset + xScale * (xMax - xMin) * s
|
||||
Ys = lambda s: yOffset - yScale * sin(xMin + (xMax - xMin) * s)
|
||||
dYdXs = lambda s: -yScale * cos(xMin + (xMax - xMin) * s) / xScale
|
||||
elif fun == 'lace':
|
||||
Xs = lambda s: xOffset + xScale * ((xMax - xMin) * s + 2 * sin(2 * s * (xMax - xMin) + pi))
|
||||
dXs = lambda s: xScale * (xMax - xMin) * (1.0 + 4.0 * cos(2 * s * (xMax - xMin) + pi))
|
||||
Ys = lambda s: yOffset - yScale * sin(xMin + (xMax - xMin) * s)
|
||||
dYs = lambda s: -yScale * cos(xMin + (xMax - xMin) * s) * (xMax - xMin)
|
||||
dYdXs = lambda s: dYs(s) / dXs(s)
|
||||
else:
|
||||
inkex.errormsg('Unknown function %s specified' % fun)
|
||||
return
|
||||
|
||||
# Derivatives: remember the chain rule....
|
||||
# dXs = lambda s: xScale * ( xMax - xMin )
|
||||
# dYs = lambda s: -yScale * cos( xMin + ( xMax - xMin ) * s ) * ( xMax - xMin )
|
||||
# Derivatives: remember the chain rule....
|
||||
# dXs = lambda s: xScale * ( xMax - xMin )
|
||||
# dYs = lambda s: -yScale * cos( xMin + ( xMax - xMin ) * s ) * ( xMax - xMin )
|
||||
|
||||
# xThird is 1/3 the step size
|
||||
nPoints = int(nPoints)
|
||||
# xThird is 1/3 the step size
|
||||
nPoints = int(nPoints)
|
||||
|
||||
# xThird is 1/3 the step size; note that Xs(1) - Xs(0) = xScale * ( xMax - xMin )
|
||||
xThird = (Xs(float(1)) - Xs(float(0))) / float(3 * (nPoints - 1))
|
||||
# xThird is 1/3 the step size; note that Xs(1) - Xs(0) = xScale * ( xMax - xMin )
|
||||
xThird = (Xs(float(1)) - Xs(float(0))) / float(3 * (nPoints - 1))
|
||||
|
||||
if bounded:
|
||||
yUpper = Y2s(float(0))
|
||||
yLower = Y1s(float(0))
|
||||
yOffset = 0.5 * (yUpper + yLower)
|
||||
yUpper = yOffset + rescale * (yUpper - yOffset)
|
||||
yLower = yOffset + rescale * (yLower - yOffset)
|
||||
yScale = (yUpper - yLower) / (yMax - yMin)
|
||||
if bounded:
|
||||
yUpper = Y2s(float(0))
|
||||
yLower = Y1s(float(0))
|
||||
yOffset = 0.5 * (yUpper + yLower)
|
||||
yUpper = yOffset + rescale * (yUpper - yOffset)
|
||||
yLower = yOffset + rescale * (yLower - yOffset)
|
||||
yScale = (yUpper - yLower) / (yMax - yMin)
|
||||
|
||||
x1 = Xs(float(0))
|
||||
y1 = Ys(float(0))
|
||||
dx1 = float(1)
|
||||
dy1 = dYdXs(float(0))
|
||||
x1 = Xs(float(0))
|
||||
y1 = Ys(float(0))
|
||||
dx1 = float(1)
|
||||
dy1 = dYdXs(float(0))
|
||||
|
||||
# Starting point in the path is ( x, sin(x) )
|
||||
pathData = []
|
||||
pathData.append(['M ', [x1, y1]])
|
||||
# Starting point in the path is ( x, sin(x) )
|
||||
pathData = []
|
||||
pathData.append(['M ', [x1, y1]])
|
||||
|
||||
for i in range(1, nPoints):
|
||||
for i in range(1, nPoints):
|
||||
|
||||
s = float(i) / float(nPoints - 1)
|
||||
if bounded:
|
||||
yUpper = Y2s(s)
|
||||
yLower = Y1s(s)
|
||||
yOffset = 0.5 * (yUpper + yLower)
|
||||
yUpper = yOffset + rescale * (yUpper - yOffset)
|
||||
yLower = yOffset + rescale * (yLower - yOffset)
|
||||
yScale = (yUpper - yLower) / (yMax - yMin)
|
||||
s = float(i) / float(nPoints - 1)
|
||||
if bounded:
|
||||
yUpper = Y2s(s)
|
||||
yLower = Y1s(s)
|
||||
yOffset = 0.5 * (yUpper + yLower)
|
||||
yUpper = yOffset + rescale * (yUpper - yOffset)
|
||||
yLower = yOffset + rescale * (yLower - yOffset)
|
||||
yScale = (yUpper - yLower) / (yMax - yMin)
|
||||
|
||||
x2 = Xs(s)
|
||||
y2 = Ys(s)
|
||||
dx2 = float(1)
|
||||
dy2 = dYdXs(s)
|
||||
if dy2 > float(10):
|
||||
dy2 = float(10)
|
||||
elif dy2 < float(-10):
|
||||
dy2 = float(-10)
|
||||
x2 = Xs(s)
|
||||
y2 = Ys(s)
|
||||
dx2 = float(1)
|
||||
dy2 = dYdXs(s)
|
||||
if dy2 > float(10):
|
||||
dy2 = float(10)
|
||||
elif dy2 < float(-10):
|
||||
dy2 = float(-10)
|
||||
|
||||
# Add another segment to the plot
|
||||
if spline:
|
||||
pathData.append([' C ',
|
||||
[x1 + (dx1 * xThird),
|
||||
y1 + (dy1 * xThird),
|
||||
x2 - (dx2 * xThird),
|
||||
y2 - (dy2 * xThird),
|
||||
x2, y2]])
|
||||
else:
|
||||
pathData.append([' L ', [x1, y1]])
|
||||
pathData.append([' L ', [x2, y2]])
|
||||
x1 = x2
|
||||
y1 = y2
|
||||
dx1 = dx2
|
||||
dy1 = dy2
|
||||
# Add another segment to the plot
|
||||
if spline:
|
||||
pathData.append([' C ',
|
||||
[x1 + (dx1 * xThird),
|
||||
y1 + (dy1 * xThird),
|
||||
x2 - (dx2 * xThird),
|
||||
y2 - (dy2 * xThird),
|
||||
x2, y2]])
|
||||
else:
|
||||
pathData.append([' L ', [x1, y1]])
|
||||
pathData.append([' L ', [x2, y2]])
|
||||
x1 = x2
|
||||
y1 = y2
|
||||
dx1 = dx2
|
||||
dy1 = dy2
|
||||
|
||||
pathDesc = \
|
||||
'version:%d;style:linear;function:sin(x);' % VERSION + \
|
||||
'cycles:%f;rn:%d;rm:%d;points:%d' % (cycles, rn, rm, nPoints) + \
|
||||
';width:%d;height:%d;x:%d;y:%d' % (width, height, offset[0], offset[1])
|
||||
pathDesc = \
|
||||
'version:%d;style:linear;function:sin(x);' % VERSION + \
|
||||
'cycles:%f;rn:%d;rm:%d;points:%d' % (cycles, rn, rm, nPoints) + \
|
||||
';width:%d;height:%d;x:%d;y:%d' % (width, height, offset[0], offset[1])
|
||||
|
||||
return pathData, pathDesc
|
||||
return pathData, pathDesc
|
||||
|
||||
|
||||
class SpiroSine(inkex.Effect):
|
||||
nsURI = 'http://sample.com/ns'
|
||||
nsPrefix = 'doof'
|
||||
nsURI = 'http://sample.com/ns'
|
||||
nsPrefix = 'doof'
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self):
|
||||
|
||||
inkex.Effect.__init__(self)
|
||||
inkex.Effect.__init__(self)
|
||||
|
||||
self.OptionParser.add_option("--tab", # NOTE: value is not used.
|
||||
action="store", type="string",
|
||||
dest="tab", default="splash",
|
||||
help="The active tab when Apply was pressed")
|
||||
self.OptionParser.add_option("--tab", # NOTE: value is not used.
|
||||
action="store", type="string",
|
||||
dest="tab", default="splash",
|
||||
help="The active tab when Apply was pressed")
|
||||
|
||||
self.OptionParser.add_option('--fCycles', dest='fCycles',
|
||||
type='float', default=float(10), action='store',
|
||||
help='Number of cycles (periods)')
|
||||
self.OptionParser.add_option('--fCycles', dest='fCycles',
|
||||
type='float', default=float(10), action='store',
|
||||
help='Number of cycles (periods)')
|
||||
|
||||
self.OptionParser.add_option('--nrN', dest='nrN',
|
||||
type='int', default=int(0), action='store',
|
||||
help='Start x at 2 * pi * n / m')
|
||||
self.OptionParser.add_option('--nrN', dest='nrN',
|
||||
type='int', default=int(0), action='store',
|
||||
help='Start x at 2 * pi * n / m')
|
||||
|
||||
self.OptionParser.add_option('--nrM', dest='nrM',
|
||||
type='int', default=int(0), action='store',
|
||||
help='Start x at 2 * pi * n / m')
|
||||
self.OptionParser.add_option('--nrM', dest='nrM',
|
||||
type='int', default=int(0), action='store',
|
||||
help='Start x at 2 * pi * n / m')
|
||||
|
||||
self.OptionParser.add_option('--fRecess', dest='fRecess',
|
||||
type='float', default=float(2), action='store',
|
||||
help='Recede from envelope by factor')
|
||||
self.OptionParser.add_option('--fRecess', dest='fRecess',
|
||||
type='float', default=float(2), action='store',
|
||||
help='Recede from envelope by factor')
|
||||
|
||||
self.OptionParser.add_option("--nSamples", dest="nSamples",
|
||||
type="int", default=float(50), action="store",
|
||||
help="Number of points to sample")
|
||||
self.OptionParser.add_option("--nSamples", dest="nSamples",
|
||||
type="int", default=float(50), action="store",
|
||||
help="Number of points to sample")
|
||||
|
||||
self.OptionParser.add_option("--nWidth", dest="nWidth",
|
||||
type="int", default=int(3200), action="store",
|
||||
help="Width in pixels")
|
||||
self.OptionParser.add_option("--nWidth", dest="nWidth",
|
||||
type="int", default=int(3200), action="store",
|
||||
help="Width in pixels")
|
||||
|
||||
self.OptionParser.add_option("--nHeight", dest="nHeight",
|
||||
type="int", default=int(100), action="store",
|
||||
help="Height in pixels")
|
||||
self.OptionParser.add_option("--nHeight", dest="nHeight",
|
||||
type="int", default=int(100), action="store",
|
||||
help="Height in pixels")
|
||||
|
||||
self.OptionParser.add_option("--nOffsetX", dest="nOffsetX",
|
||||
type="int", default=int(0), action="store",
|
||||
help="Starting x coordinate (pixels)")
|
||||
self.OptionParser.add_option("--nOffsetX", dest="nOffsetX",
|
||||
type="int", default=int(0), action="store",
|
||||
help="Starting x coordinate (pixels)")
|
||||
|
||||
self.OptionParser.add_option("--nOffsetY", dest="nOffsetY",
|
||||
type="int", default=int(400), action="store",
|
||||
help="Starting y coordinate (pixels)")
|
||||
self.OptionParser.add_option("--nOffsetY", dest="nOffsetY",
|
||||
type="int", default=int(400), action="store",
|
||||
help="Starting y coordinate (pixels)")
|
||||
|
||||
self.OptionParser.add_option('--bLace', dest='bLace',
|
||||
type='inkbool', default=False, action='store',
|
||||
help='Lace')
|
||||
self.OptionParser.add_option('--bLace', dest='bLace',
|
||||
type='inkbool', default=False, action='store',
|
||||
help='Lace')
|
||||
|
||||
self.OptionParser.add_option('--bSpline', dest='bSpline',
|
||||
type='inkbool', default=True, action='store',
|
||||
help='Spline')
|
||||
self.OptionParser.add_option('--bSpline', dest='bSpline',
|
||||
type='inkbool', default=True, action='store',
|
||||
help='Spline')
|
||||
|
||||
self.recess = float(0.95)
|
||||
self.recess = float(0.95)
|
||||
|
||||
def effect(self):
|
||||
def effect(self):
|
||||
|
||||
inkex.NSS[self.nsPrefix] = self.nsURI
|
||||
inkex.NSS[self.nsPrefix] = self.nsURI
|
||||
|
||||
if self.options.bLace:
|
||||
func = 'lace'
|
||||
else:
|
||||
func = 'sine'
|
||||
if self.options.bLace:
|
||||
func = 'lace'
|
||||
else:
|
||||
func = 'sine'
|
||||
|
||||
fRecess = float(1)
|
||||
if self.options.fRecess > 0.0:
|
||||
fRecess = 1.0 - self.options.fRecess / float(100)
|
||||
if fRecess <= 0.0:
|
||||
fRecess = float(0)
|
||||
fRecess = float(1)
|
||||
if self.options.fRecess > 0.0:
|
||||
fRecess = 1.0 - self.options.fRecess / float(100)
|
||||
if fRecess <= 0.0:
|
||||
fRecess = float(0)
|
||||
|
||||
if self.options.ids:
|
||||
if len(self.options.ids) == 2:
|
||||
attr = self.selected[self.options.ids[0]].attrib
|
||||
desc1 = self.selected[self.options.ids[0]].get(inkex.addNS('desc', self.nsPrefix))
|
||||
desc2 = self.selected[self.options.ids[1]].get(inkex.addNS('desc', self.nsPrefix))
|
||||
if (not desc1) or (not desc2):
|
||||
inkex.errormsg('Selected objects do not smell right')
|
||||
return
|
||||
path_data, path_desc = drawSine(self.options.fCycles,
|
||||
self.options.nrN,
|
||||
self.options.nrM,
|
||||
self.options.nSamples,
|
||||
[self.options.nOffsetX, self.options.nOffsetY],
|
||||
self.options.nHeight,
|
||||
self.options.nWidth,
|
||||
fRecess,
|
||||
desc1, desc2, func, self.options.bSpline)
|
||||
else:
|
||||
inkex.errormsg('Exactly two objects must be selected')
|
||||
return
|
||||
else:
|
||||
self.document.getroot().set(inkex.addNS(self.nsPrefix, 'xmlns'), self.nsURI)
|
||||
if self.options.ids:
|
||||
if len(self.options.ids) == 2:
|
||||
attr = self.selected[self.options.ids[0]].attrib
|
||||
desc1 = self.selected[self.options.ids[0]].get(inkex.addNS('desc', self.nsPrefix))
|
||||
desc2 = self.selected[self.options.ids[1]].get(inkex.addNS('desc', self.nsPrefix))
|
||||
if (not desc1) or (not desc2):
|
||||
inkex.errormsg('Selected objects do not smell right')
|
||||
return
|
||||
path_data, path_desc = drawSine(self.options.fCycles,
|
||||
self.options.nrN,
|
||||
self.options.nrM,
|
||||
self.options.nSamples,
|
||||
[self.options.nOffsetX, self.options.nOffsetY],
|
||||
self.options.nHeight,
|
||||
self.options.nWidth,
|
||||
fRecess,
|
||||
desc1, desc2, func, self.options.bSpline)
|
||||
else:
|
||||
inkex.errormsg('Exactly two objects must be selected')
|
||||
return
|
||||
else:
|
||||
self.document.getroot().set(inkex.addNS(self.nsPrefix, 'xmlns'), self.nsURI)
|
||||
|
||||
path_data, path_desc = drawSine(self.options.fCycles,
|
||||
self.options.nrN,
|
||||
self.options.nrM,
|
||||
self.options.nSamples,
|
||||
[self.options.nOffsetX, self.options.nOffsetY],
|
||||
self.options.nHeight,
|
||||
self.options.nWidth,
|
||||
fRecess,
|
||||
None, None, func, self.options.bSpline)
|
||||
path_data, path_desc = drawSine(self.options.fCycles,
|
||||
self.options.nrN,
|
||||
self.options.nrM,
|
||||
self.options.nSamples,
|
||||
[self.options.nOffsetX, self.options.nOffsetY],
|
||||
self.options.nHeight,
|
||||
self.options.nWidth,
|
||||
fRecess,
|
||||
None, None, func, self.options.bSpline)
|
||||
|
||||
style = {'stroke': 'black', 'stroke-width': '1', 'fill': 'none'}
|
||||
path_attrs = {
|
||||
'style': simplestyle.formatStyle(style),
|
||||
'd': simplepath.formatPath(path_data),
|
||||
inkex.addNS('desc', self.nsPrefix): path_desc}
|
||||
newpath = inkex.etree.SubElement(self.document.getroot(),
|
||||
inkex.addNS('path', 'svg '), path_attrs, nsmap=inkex.NSS)
|
||||
style = {'stroke': 'black', 'stroke-width': '1', 'fill': 'none'}
|
||||
path_attrs = {
|
||||
'style': simplestyle.formatStyle(style),
|
||||
'd': simplepath.formatPath(path_data),
|
||||
inkex.addNS('desc', self.nsPrefix): path_desc}
|
||||
newpath = inkex.etree.SubElement(self.document.getroot(),
|
||||
inkex.addNS('path', 'svg '), path_attrs, nsmap=inkex.NSS)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
e = SpiroSine()
|
||||
e.affect()
|
||||
e = SpiroSine()
|
||||
e.affect()
|
||||
|
|
|
@ -82,29 +82,29 @@ import simplestyle
|
|||
# be used to deal with a face being removed from hersheydata.py
|
||||
|
||||
map_our_names_to_hersheydata = {
|
||||
'astrology': 'astrology',
|
||||
'cursive': 'cursive',
|
||||
'cyrillic': 'cyrillic',
|
||||
'futural': 'futural',
|
||||
'futuram': 'futuram',
|
||||
'gothiceng': 'gothiceng',
|
||||
'gothicger': 'gothicger',
|
||||
'gothicita': 'gothicita',
|
||||
'greek': 'greek',
|
||||
'japanese': 'japanese',
|
||||
'markers': 'markers',
|
||||
'mathlow': 'mathlow',
|
||||
'mathupp': 'mathupp',
|
||||
'meteorology': 'meteorology',
|
||||
'music': 'music',
|
||||
'scriptc': 'scriptc',
|
||||
'scripts': 'scripts',
|
||||
'symbolic': 'symbolic',
|
||||
'timesg': 'timesg',
|
||||
'timesi': 'timesi',
|
||||
'timesib': 'timesib',
|
||||
'timesr': 'timesr',
|
||||
'timesrb': 'timesrb'}
|
||||
'astrology': 'astrology',
|
||||
'cursive': 'cursive',
|
||||
'cyrillic': 'cyrillic',
|
||||
'futural': 'futural',
|
||||
'futuram': 'futuram',
|
||||
'gothiceng': 'gothiceng',
|
||||
'gothicger': 'gothicger',
|
||||
'gothicita': 'gothicita',
|
||||
'greek': 'greek',
|
||||
'japanese': 'japanese',
|
||||
'markers': 'markers',
|
||||
'mathlow': 'mathlow',
|
||||
'mathupp': 'mathupp',
|
||||
'meteorology': 'meteorology',
|
||||
'music': 'music',
|
||||
'scriptc': 'scriptc',
|
||||
'scripts': 'scripts',
|
||||
'symbolic': 'symbolic',
|
||||
'timesg': 'timesg',
|
||||
'timesi': 'timesi',
|
||||
'timesib': 'timesib',
|
||||
'timesr': 'timesr',
|
||||
'timesrb': 'timesrb'}
|
||||
|
||||
|
||||
# The following two routines are lifted with impunity from Windell H. Oskay's
|
||||
|
@ -112,39 +112,39 @@ map_our_names_to_hersheydata = {
|
|||
# Copyright 2011, Windell H. Oskay, www.evilmadscientist.com
|
||||
|
||||
def draw_svg_text(char, face, offset, vertoffset, parent):
|
||||
style = {'stroke': '#000000', 'fill': 'none', 'stroke-linecap': 'round', 'stroke-linejoin': 'round'}
|
||||
# Apply rounding to ends so that user gets best impression of final printed text appearance.
|
||||
pathString = face[char]
|
||||
splitString = pathString.split()
|
||||
midpoint = offset - float(splitString[0])
|
||||
i = pathString.find("M")
|
||||
if i >= 0:
|
||||
pathString = pathString[i:] # portion after first move
|
||||
trans = 'translate(' + str(midpoint) + ',' + str(vertoffset) + ')'
|
||||
text_attribs = {'style': simplestyle.formatStyle(style), 'd': pathString, 'transform': trans}
|
||||
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), text_attribs)
|
||||
return midpoint + float(splitString[1]) # new offset value
|
||||
style = {'stroke': '#000000', 'fill': 'none', 'stroke-linecap': 'round', 'stroke-linejoin': 'round'}
|
||||
# Apply rounding to ends so that user gets best impression of final printed text appearance.
|
||||
pathString = face[char]
|
||||
splitString = pathString.split()
|
||||
midpoint = offset - float(splitString[0])
|
||||
i = pathString.find("M")
|
||||
if i >= 0:
|
||||
pathString = pathString[i:] # portion after first move
|
||||
trans = 'translate(' + str(midpoint) + ',' + str(vertoffset) + ')'
|
||||
text_attribs = {'style': simplestyle.formatStyle(style), 'd': pathString, 'transform': trans}
|
||||
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), text_attribs)
|
||||
return midpoint + float(splitString[1]) # new offset value
|
||||
|
||||
|
||||
def renderText(parent, markup):
|
||||
# Embed text in group to make manipulation easier:
|
||||
g_attribs = {inkex.addNS('label', 'inkscape'): 'Hershey Text'}
|
||||
g = inkex.etree.SubElement(parent, 'g', g_attribs)
|
||||
# Embed text in group to make manipulation easier:
|
||||
g_attribs = {inkex.addNS('label', 'inkscape'): 'Hershey Text'}
|
||||
g = inkex.etree.SubElement(parent, 'g', g_attribs)
|
||||
|
||||
w = 0 # Initial spacing offset
|
||||
spacing = 3 # spacing between letters
|
||||
w = 0 # Initial spacing offset
|
||||
spacing = 3 # spacing between letters
|
||||
|
||||
for Face, Text in markup:
|
||||
if Face in map_our_names_to_hersheydata:
|
||||
Face = map_our_names_to_hersheydata[Face]
|
||||
font = eval('hersheydata.' + Face)
|
||||
letterVals = [ord(q) - 32 for q in Text]
|
||||
for q in letterVals:
|
||||
if (q < 0) or (q > 95):
|
||||
w += 2 * spacing
|
||||
else:
|
||||
w = draw_svg_text(q, font, w, 0, g)
|
||||
return g, w
|
||||
for Face, Text in markup:
|
||||
if Face in map_our_names_to_hersheydata:
|
||||
Face = map_our_names_to_hersheydata[Face]
|
||||
font = eval('hersheydata.' + Face)
|
||||
letterVals = [ord(q) - 32 for q in Text]
|
||||
for q in letterVals:
|
||||
if (q < 0) or (q > 95):
|
||||
w += 2 * spacing
|
||||
else:
|
||||
w = draw_svg_text(q, font, w, 0, g)
|
||||
return g, w
|
||||
|
||||
|
||||
# The generic font "families" we support
|
||||
|
@ -152,9 +152,9 @@ generic_families = ('sans', 'script', 'times')
|
|||
|
||||
# Convert "family-name" + "bold-0-or-1" + "italic-0-or-1" to a typeface name
|
||||
family_to_font = {
|
||||
'sans00': 'futural', 'sans10': 'futuram', 'sans01': 'futural', 'sans11': 'futuram',
|
||||
'times00': 'timesr', 'times10': 'timesrb', 'times01': 'timesi', 'times11': 'timesib',
|
||||
'script00': 'scripts', 'script10': 'scriptc', 'script01': 'scripts', 'script11': 'scriptc'}
|
||||
'sans00': 'futural', 'sans10': 'futuram', 'sans01': 'futural', 'sans11': 'futuram',
|
||||
'times00': 'timesr', 'times10': 'timesrb', 'times01': 'timesi', 'times11': 'timesib',
|
||||
'script00': 'scripts', 'script10': 'scriptc', 'script01': 'scripts', 'script11': 'scriptc'}
|
||||
emphasis_is_bold = {'sans': True, 'times': False, 'script': True}
|
||||
|
||||
# Short list of entity references
|
||||
|
@ -162,282 +162,282 @@ entity_refs = {'<': '<', '>': '>', '&': '&', '"': '"', '&apos': "
|
|||
|
||||
|
||||
def normalize_possible_EMS_string(tag):
|
||||
# Normalizes tag name by removing any spaces
|
||||
sNormalizedTag = tag.replace(' ', '')
|
||||
return sNormalizedTag
|
||||
# Normalizes tag name by removing any spaces
|
||||
sNormalizedTag = tag.replace(' ', '')
|
||||
return sNormalizedTag
|
||||
|
||||
|
||||
def is_valid_EMS_name(tag):
|
||||
# returns true if family is one of the "EMS" faces in hersheydata.py
|
||||
# else false
|
||||
sNormalizedTag = normalize_possible_EMS_string(tag)
|
||||
bRetVal = False # default assumption
|
||||
try:
|
||||
fontgroup = hersheydata.group_allfonts
|
||||
except:
|
||||
# User probably has old version of hersheydata.py
|
||||
pass
|
||||
else:
|
||||
for f in fontgroup:
|
||||
if f[0] == sNormalizedTag:
|
||||
bRetVal = True
|
||||
break
|
||||
# returns true if family is one of the "EMS" faces in hersheydata.py
|
||||
# else false
|
||||
sNormalizedTag = normalize_possible_EMS_string(tag)
|
||||
bRetVal = False # default assumption
|
||||
try:
|
||||
fontgroup = hersheydata.group_allfonts
|
||||
except:
|
||||
# User probably has old version of hersheydata.py
|
||||
pass
|
||||
else:
|
||||
for f in fontgroup:
|
||||
if f[0] == sNormalizedTag:
|
||||
bRetVal = True
|
||||
break
|
||||
|
||||
return bRetVal
|
||||
return bRetVal
|
||||
|
||||
|
||||
def pickFace(family, bold=False, italics=False, emphasis=False):
|
||||
if (family is None) or (family == ''):
|
||||
return None
|
||||
if (family is None) or (family == ''):
|
||||
return None
|
||||
|
||||
b = '0'
|
||||
i = '0'
|
||||
b = '0'
|
||||
i = '0'
|
||||
|
||||
# If using a generic font family, then determine how to map <em>
|
||||
if emphasis and (family in generic_families):
|
||||
if emphasis_is_bold[family]:
|
||||
bold = True
|
||||
else:
|
||||
italics = True
|
||||
# If using a generic font family, then determine how to map <em>
|
||||
if emphasis and (family in generic_families):
|
||||
if emphasis_is_bold[family]:
|
||||
bold = True
|
||||
else:
|
||||
italics = True
|
||||
|
||||
if bold:
|
||||
b = '1'
|
||||
if bold:
|
||||
b = '1'
|
||||
|
||||
if italics:
|
||||
i = '1'
|
||||
if italics:
|
||||
i = '1'
|
||||
|
||||
if (family + b + i) in family_to_font:
|
||||
return family_to_font[family + b + i]
|
||||
if (family + b + i) in family_to_font:
|
||||
return family_to_font[family + b + i]
|
||||
|
||||
return family
|
||||
return family
|
||||
|
||||
|
||||
def processMarkup(text, family='sans'):
|
||||
if text is None:
|
||||
text = ''
|
||||
if text is None:
|
||||
text = ''
|
||||
|
||||
# By default we assume 'sans'
|
||||
if (family is None) or (family == ''):
|
||||
family = 'sans'
|
||||
family_default = family
|
||||
face_stack = [family]
|
||||
# By default we assume 'sans'
|
||||
if (family is None) or (family == ''):
|
||||
family = 'sans'
|
||||
family_default = family
|
||||
face_stack = [family]
|
||||
|
||||
# Bold and italics off
|
||||
bold = False
|
||||
emphasis = False
|
||||
italic = False
|
||||
# Bold and italics off
|
||||
bold = False
|
||||
emphasis = False
|
||||
italic = False
|
||||
|
||||
# Set the current typeface
|
||||
face = pickFace(family, bold, italic, emphasis)
|
||||
# Set the current typeface
|
||||
face = pickFace(family, bold, italic, emphasis)
|
||||
|
||||
# And the result of markup processing so far
|
||||
markup = []
|
||||
# And the result of markup processing so far
|
||||
markup = []
|
||||
|
||||
# We keep a queue / list of the open markup tags
|
||||
# When a tag is closed, we expect it to be well nested. To enforce
|
||||
# that expectation, we make sure that we are closing the most recently
|
||||
# opened tag. While this may seem overly picky, it's easier than worrying
|
||||
# issues like, "Does closing a typeface imply implicitly closing <b> or <it>?"
|
||||
# And, "Does starting a new typeface imply closing the prior selected face?"
|
||||
tags_used = []
|
||||
# We keep a queue / list of the open markup tags
|
||||
# When a tag is closed, we expect it to be well nested. To enforce
|
||||
# that expectation, we make sure that we are closing the most recently
|
||||
# opened tag. While this may seem overly picky, it's easier than worrying
|
||||
# issues like, "Does closing a typeface imply implicitly closing <b> or <it>?"
|
||||
# And, "Does starting a new typeface imply closing the prior selected face?"
|
||||
tags_used = []
|
||||
|
||||
outstr = ''
|
||||
i = 0
|
||||
while i < len(text):
|
||||
# An entity reference?
|
||||
if text[i] == '&':
|
||||
j = text.find(';', i + 1)
|
||||
if j != -1:
|
||||
eref = text[i:j + 1]
|
||||
if eref in entity_refs:
|
||||
outstr += entity_refs[eref]
|
||||
i = j + 1
|
||||
else:
|
||||
inkex.errormsg('Ignoring the unrecognized entity reference %s.' % eref)
|
||||
outstr += eref
|
||||
i = j + 1
|
||||
else:
|
||||
inkex.errormsg('An unescaped "&" was encountered; please replace it with "&".')
|
||||
break
|
||||
outstr = ''
|
||||
i = 0
|
||||
while i < len(text):
|
||||
# An entity reference?
|
||||
if text[i] == '&':
|
||||
j = text.find(';', i + 1)
|
||||
if j != -1:
|
||||
eref = text[i:j + 1]
|
||||
if eref in entity_refs:
|
||||
outstr += entity_refs[eref]
|
||||
i = j + 1
|
||||
else:
|
||||
inkex.errormsg('Ignoring the unrecognized entity reference %s.' % eref)
|
||||
outstr += eref
|
||||
i = j + 1
|
||||
else:
|
||||
inkex.errormsg('An unescaped "&" was encountered; please replace it with "&".')
|
||||
break
|
||||
|
||||
# Start of a tag (start-tag or end-tag? self-closing tags not supported)
|
||||
elif text[i] == '<':
|
||||
j = text.find('>', i + 1)
|
||||
if (j != -1) and (j > (i + 1)):
|
||||
# Start of a tag (start-tag or end-tag? self-closing tags not supported)
|
||||
elif text[i] == '<':
|
||||
j = text.find('>', i + 1)
|
||||
if (j != -1) and (j > (i + 1)):
|
||||
|
||||
tag = text[i + 1:j]
|
||||
i = j + 1
|
||||
tag = text[i + 1:j]
|
||||
i = j + 1
|
||||
|
||||
if tag[0] == '/':
|
||||
# This is an end-tag (closing tag)
|
||||
close = True
|
||||
tag = tag[1:]
|
||||
if tag[0] == '/':
|
||||
# This is an end-tag (closing tag)
|
||||
close = True
|
||||
tag = tag[1:]
|
||||
|
||||
# Ensure that the most recently opened tag is that which we are closing here
|
||||
# We'll pop the most recent tag from the queue of opened tags and see if
|
||||
# it matches
|
||||
if len(tags_used) == 0:
|
||||
inkex.errormsg('The ending tag </%s> appeared before any start tag <%s>.' % (tag, tag))
|
||||
break
|
||||
else:
|
||||
old_tag = tags_used.pop()
|
||||
if old_tag != tag:
|
||||
inkex.errormsg('The ending tag </%s> does not appear to be correctly nested; it tried to close the tag <%s>. Sorry, but all tags must be properly nested.' % (tag, old_tag))
|
||||
break
|
||||
else:
|
||||
# Start tag (opening tag)
|
||||
# Push it onto the queue of opened tags
|
||||
close = False
|
||||
tags_used.append(tag)
|
||||
# Ensure that the most recently opened tag is that which we are closing here
|
||||
# We'll pop the most recent tag from the queue of opened tags and see if
|
||||
# it matches
|
||||
if len(tags_used) == 0:
|
||||
inkex.errormsg('The ending tag </%s> appeared before any start tag <%s>.' % (tag, tag))
|
||||
break
|
||||
else:
|
||||
old_tag = tags_used.pop()
|
||||
if old_tag != tag:
|
||||
inkex.errormsg('The ending tag </%s> does not appear to be correctly nested; it tried to close the tag <%s>. Sorry, but all tags must be properly nested.' % (tag, old_tag))
|
||||
break
|
||||
else:
|
||||
# Start tag (opening tag)
|
||||
# Push it onto the queue of opened tags
|
||||
close = False
|
||||
tags_used.append(tag)
|
||||
|
||||
if (tag == 'b') or (tag == 'strong'):
|
||||
if bold == close:
|
||||
# Push prior string and font onto the stack
|
||||
if outstr != '':
|
||||
markup.append([face, outstr])
|
||||
outstr = ''
|
||||
if (tag == 'b') or (tag == 'strong'):
|
||||
if bold == close:
|
||||
# Push prior string and font onto the stack
|
||||
if outstr != '':
|
||||
markup.append([face, outstr])
|
||||
outstr = ''
|
||||
|
||||
# Start a new boldface string
|
||||
bold = not bold
|
||||
face = pickFace(family, bold, italic, emphasis)
|
||||
# Start a new boldface string
|
||||
bold = not bold
|
||||
face = pickFace(family, bold, italic, emphasis)
|
||||
|
||||
elif tag == 'i':
|
||||
if italic == close:
|
||||
# Push the prior string and font unto the stack
|
||||
if outstr != '':
|
||||
markup.append([face, outstr])
|
||||
outstr = ''
|
||||
elif tag == 'i':
|
||||
if italic == close:
|
||||
# Push the prior string and font unto the stack
|
||||
if outstr != '':
|
||||
markup.append([face, outstr])
|
||||
outstr = ''
|
||||
|
||||
# Start a new italicized string
|
||||
italic = not italic
|
||||
face = pickFace(family, bold, italic, emphasis)
|
||||
# Start a new italicized string
|
||||
italic = not italic
|
||||
face = pickFace(family, bold, italic, emphasis)
|
||||
|
||||
elif tag == 'em':
|
||||
if emphasis == close:
|
||||
# Push the prior string and font unto the stack
|
||||
if outstr != '':
|
||||
markup.append([face, outstr])
|
||||
outstr = ''
|
||||
elif tag == 'em':
|
||||
if emphasis == close:
|
||||
# Push the prior string and font unto the stack
|
||||
if outstr != '':
|
||||
markup.append([face, outstr])
|
||||
outstr = ''
|
||||
|
||||
# Start a new italicized string
|
||||
emphasis = not emphasis
|
||||
face = pickFace(family, bold, italic, emphasis)
|
||||
# Start a new italicized string
|
||||
emphasis = not emphasis
|
||||
face = pickFace(family, bold, italic, emphasis)
|
||||
|
||||
else:
|
||||
bValidEMSName = is_valid_EMS_name(tag)
|
||||
if bValidEMSName:
|
||||
tag = normalize_possible_EMS_string(tag)
|
||||
if (
|
||||
(tag not in generic_families)
|
||||
and (tag not in map_our_names_to_hersheydata)
|
||||
and (not bValidEMSName)
|
||||
):
|
||||
if close:
|
||||
inkex.errormsg('Ignoring the unrecognized tag </%s>.' % tag)
|
||||
else:
|
||||
inkex.errormsg('Ignoring the unrecognized tag <%s>.' % tag)
|
||||
else:
|
||||
if outstr != '':
|
||||
markup.append([face, outstr])
|
||||
outstr = ''
|
||||
if not close:
|
||||
family = tag
|
||||
face_stack.append(family)
|
||||
else:
|
||||
if len(face_stack) > 0:
|
||||
# Current face on the stack should be the one we just closed
|
||||
face_stack.pop()
|
||||
if len(face_stack) > 0:
|
||||
family = face_stack[len(face_stack) - 1]
|
||||
else:
|
||||
family = default_family
|
||||
else:
|
||||
family = default_family
|
||||
face = pickFace(family, bold, italic, emphasis)
|
||||
else:
|
||||
inkex.errormsg('Ignoring unescaped "<"')
|
||||
outstr += '<'
|
||||
i += 1
|
||||
else:
|
||||
outstr += text[i]
|
||||
i += 1
|
||||
else:
|
||||
bValidEMSName = is_valid_EMS_name(tag)
|
||||
if bValidEMSName:
|
||||
tag = normalize_possible_EMS_string(tag)
|
||||
if (
|
||||
(tag not in generic_families)
|
||||
and (tag not in map_our_names_to_hersheydata)
|
||||
and (not bValidEMSName)
|
||||
):
|
||||
if close:
|
||||
inkex.errormsg('Ignoring the unrecognized tag </%s>.' % tag)
|
||||
else:
|
||||
inkex.errormsg('Ignoring the unrecognized tag <%s>.' % tag)
|
||||
else:
|
||||
if outstr != '':
|
||||
markup.append([face, outstr])
|
||||
outstr = ''
|
||||
if not close:
|
||||
family = tag
|
||||
face_stack.append(family)
|
||||
else:
|
||||
if len(face_stack) > 0:
|
||||
# Current face on the stack should be the one we just closed
|
||||
face_stack.pop()
|
||||
if len(face_stack) > 0:
|
||||
family = face_stack[len(face_stack) - 1]
|
||||
else:
|
||||
family = default_family
|
||||
else:
|
||||
family = default_family
|
||||
face = pickFace(family, bold, italic, emphasis)
|
||||
else:
|
||||
inkex.errormsg('Ignoring unescaped "<"')
|
||||
outstr += '<'
|
||||
i += 1
|
||||
else:
|
||||
outstr += text[i]
|
||||
i += 1
|
||||
|
||||
# We won't worry about unclosed tags -- we're not trying to be an XML or XHTML parser
|
||||
# We won't worry about unclosed tags -- we're not trying to be an XML or XHTML parser
|
||||
|
||||
# See if there was a hard error
|
||||
if i < len(text):
|
||||
return None
|
||||
# See if there was a hard error
|
||||
if i < len(text):
|
||||
return None
|
||||
|
||||
# And push the last text into the list of processed markup
|
||||
if outstr != '':
|
||||
markup.append([face, outstr])
|
||||
# And push the last text into the list of processed markup
|
||||
if outstr != '':
|
||||
markup.append([face, outstr])
|
||||
|
||||
return markup
|
||||
return markup
|
||||
|
||||
|
||||
class SpiralText(inkex.Effect):
|
||||
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option("--tab", # NOTE: value is not used.
|
||||
action="store", type="string",
|
||||
dest="tab", default="splash",
|
||||
help="The active tab when Apply was pressed")
|
||||
self.OptionParser.add_option("--text",
|
||||
action="store", type="string",
|
||||
dest="text", default="Hershey Text for Inkscape",
|
||||
help="The input text to render")
|
||||
self.OptionParser.add_option("--fontfamily",
|
||||
action="store", type="string",
|
||||
dest="fontfamily", default="sans",
|
||||
help="The selected font face when Apply was pressed")
|
||||
self.OptionParser.add_option("--wrap",
|
||||
action="store", type="float",
|
||||
dest="wrap", default=float(10),
|
||||
help="Number of times to wrap the text around the egg")
|
||||
self.OptionParser.add_option("--flip",
|
||||
action="store", type="inkbool",
|
||||
dest="flip", default=False,
|
||||
help="Flip the text for plotting with the egg's bottom at the egg motor")
|
||||
self.OptionParser.add_option("--stretch",
|
||||
action="store", type="inkbool",
|
||||
dest="stretch", default=True,
|
||||
help="Stretch the text horizontally to account for egg distortions")
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option("--tab", # NOTE: value is not used.
|
||||
action="store", type="string",
|
||||
dest="tab", default="splash",
|
||||
help="The active tab when Apply was pressed")
|
||||
self.OptionParser.add_option("--text",
|
||||
action="store", type="string",
|
||||
dest="text", default="Hershey Text for Inkscape",
|
||||
help="The input text to render")
|
||||
self.OptionParser.add_option("--fontfamily",
|
||||
action="store", type="string",
|
||||
dest="fontfamily", default="sans",
|
||||
help="The selected font face when Apply was pressed")
|
||||
self.OptionParser.add_option("--wrap",
|
||||
action="store", type="float",
|
||||
dest="wrap", default=float(10),
|
||||
help="Number of times to wrap the text around the egg")
|
||||
self.OptionParser.add_option("--flip",
|
||||
action="store", type="inkbool",
|
||||
dest="flip", default=False,
|
||||
help="Flip the text for plotting with the egg's bottom at the egg motor")
|
||||
self.OptionParser.add_option("--stretch",
|
||||
action="store", type="inkbool",
|
||||
dest="stretch", default=True,
|
||||
help="Stretch the text horizontally to account for egg distortions")
|
||||
|
||||
def effect(self):
|
||||
def effect(self):
|
||||
|
||||
markup = processMarkup(self.options.text, self.options.fontfamily)
|
||||
g, w = renderText(self.current_layer, markup)
|
||||
markup = processMarkup(self.options.text, self.options.fontfamily)
|
||||
g, w = renderText(self.current_layer, markup)
|
||||
|
||||
# Now to wrap the text N times around the egg, we need to scale it to have
|
||||
# length 3200 * N. It's current width is w so the scale factor is (3200 * N) / w.
|
||||
# Now to wrap the text N times around the egg, we need to scale it to have
|
||||
# length 3200 * N. It's current width is w so the scale factor is (3200 * N) / w.
|
||||
|
||||
scale_x = float(3200 * self.options.wrap) / float(w)
|
||||
scale_y = scale_x
|
||||
if self.options.stretch:
|
||||
scale_y = scale_y * 2.0 / 3.0
|
||||
scale_x = float(3200 * self.options.wrap) / float(w)
|
||||
scale_y = scale_x
|
||||
if self.options.stretch:
|
||||
scale_y = scale_y * 2.0 / 3.0
|
||||
|
||||
# In planning the scaling, we'd like to know the height of our line of text.
|
||||
# Rather than computing its bounding box, we'll just use the height of the
|
||||
# parens from the Simplex Roman font. And, we could compute that but we'll
|
||||
# just use our prior knowledge of it being 32.
|
||||
# In planning the scaling, we'd like to know the height of our line of text.
|
||||
# Rather than computing its bounding box, we'll just use the height of the
|
||||
# parens from the Simplex Roman font. And, we could compute that but we'll
|
||||
# just use our prior knowledge of it being 32.
|
||||
|
||||
h = float(32.0)
|
||||
h = float(32.0)
|
||||
|
||||
# And the angular tilt will be arcsine( height / (3200 * fWrap) )
|
||||
svg = self.document.getroot()
|
||||
height = float(self.unittouu(svg.attrib['height'])) - h * scale_y
|
||||
angle = (float(180) / math.pi) * math.asin(height / float(3200 * self.options.wrap))
|
||||
# And the angular tilt will be arcsine( height / (3200 * fWrap) )
|
||||
svg = self.document.getroot()
|
||||
height = float(self.unittouu(svg.attrib['height'])) - h * scale_y
|
||||
angle = (float(180) / math.pi) * math.asin(height / float(3200 * self.options.wrap))
|
||||
|
||||
if self.options.flip:
|
||||
angle += float(180.0)
|
||||
t = 'translate(%f,%f) rotate(%f,%f,0) scale(%f,%f)' % (-w * scale_x, h * scale_y, angle,
|
||||
w * scale_x, scale_x, scale_y)
|
||||
else:
|
||||
t = 'translate(0,%f) rotate(%f,0,0) scale(%f,%f)' % (h, angle, scale_x, scale_y)
|
||||
g.set('transform', t)
|
||||
if self.options.flip:
|
||||
angle += float(180.0)
|
||||
t = 'translate(%f,%f) rotate(%f,%f,0) scale(%f,%f)' % (-w * scale_x, h * scale_y, angle,
|
||||
w * scale_x, scale_x, scale_y)
|
||||
else:
|
||||
t = 'translate(0,%f) rotate(%f,0,0) scale(%f,%f)' % (h, angle, scale_x, scale_y)
|
||||
g.set('transform', t)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
e = SpiralText()
|
||||
e.affect()
|
||||
e = SpiralText()
|
||||
e.affect()
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -42,501 +42,501 @@ from simpletransform import applyTransformToPath, composeTransform, parseTransfo
|
|||
|
||||
|
||||
def subdivideCubicPath(sp, flat, i=1):
|
||||
"""
|
||||
[ Lifted from eggbot.py with impunity ]
|
||||
"""
|
||||
[ Lifted from eggbot.py with impunity ]
|
||||
|
||||
Break up a bezier curve into smaller curves, each of which
|
||||
is approximately a straight line within a given tolerance
|
||||
(the "smoothness" defined by [flat]).
|
||||
Break up a bezier curve into smaller curves, each of which
|
||||
is approximately a straight line within a given tolerance
|
||||
(the "smoothness" defined by [flat]).
|
||||
|
||||
This is a modified version of cspsubdiv.cspsubdiv(): rewritten
|
||||
because recursion-depth errors on complicated line segments
|
||||
could occur with cspsubdiv.cspsubdiv().
|
||||
"""
|
||||
This is a modified version of cspsubdiv.cspsubdiv(): rewritten
|
||||
because recursion-depth errors on complicated line segments
|
||||
could occur with cspsubdiv.cspsubdiv().
|
||||
"""
|
||||
|
||||
while True:
|
||||
while True:
|
||||
if i >= len(sp):
|
||||
return
|
||||
while True:
|
||||
while True:
|
||||
if i >= len(sp):
|
||||
return
|
||||
|
||||
p0 = sp[i - 1][1]
|
||||
p1 = sp[i - 1][2]
|
||||
p2 = sp[i][0]
|
||||
p3 = sp[i][1]
|
||||
p0 = sp[i - 1][1]
|
||||
p1 = sp[i - 1][2]
|
||||
p2 = sp[i][0]
|
||||
p3 = sp[i][1]
|
||||
|
||||
b = (p0, p1, p2, p3)
|
||||
b = (p0, p1, p2, p3)
|
||||
|
||||
if cspsubdiv.maxdist(b) > flat:
|
||||
break
|
||||
if cspsubdiv.maxdist(b) > flat:
|
||||
break
|
||||
|
||||
i += 1
|
||||
i += 1
|
||||
|
||||
one, two = bezmisc.beziersplitatt(b, 0.5)
|
||||
sp[i - 1][2] = one[1]
|
||||
sp[i][0] = two[2]
|
||||
p = [one[2], one[3], two[1]]
|
||||
sp[i:1] = [p]
|
||||
one, two = bezmisc.beziersplitatt(b, 0.5)
|
||||
sp[i - 1][2] = one[1]
|
||||
sp[i][0] = two[2]
|
||||
p = [one[2], one[3], two[1]]
|
||||
sp[i:1] = [p]
|
||||
|
||||
|
||||
def distanceSquared(P1, P2):
|
||||
"""
|
||||
Pythagorean distance formula WITHOUT the square root. Since
|
||||
we just want to know if the distance is less than some fixed
|
||||
fudge factor, we can just square the fudge factor once and run
|
||||
with it rather than compute square roots over and over.
|
||||
"""
|
||||
"""
|
||||
Pythagorean distance formula WITHOUT the square root. Since
|
||||
we just want to know if the distance is less than some fixed
|
||||
fudge factor, we can just square the fudge factor once and run
|
||||
with it rather than compute square roots over and over.
|
||||
"""
|
||||
|
||||
dx = P2[0] - P1[0]
|
||||
dy = P2[1] - P1[1]
|
||||
dx = P2[0] - P1[0]
|
||||
dy = P2[1] - P1[1]
|
||||
|
||||
return dx * dx + dy * dy
|
||||
return dx * dx + dy * dy
|
||||
|
||||
|
||||
class Twist(inkex.Effect):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option(
|
||||
"--nSteps", action="store", type="int",
|
||||
dest="nSteps", default=8,
|
||||
help="Number of iterations to take")
|
||||
self.OptionParser.add_option(
|
||||
"--fRatio", action="store", type="float",
|
||||
dest="fRatio", default=float(0.2),
|
||||
help="Some ratio")
|
||||
|
||||
"""
|
||||
Store each path in an associative array (dictionary) indexed
|
||||
by the lxml.etree pointer for the SVG document element
|
||||
containing the path. Looking up the path in the dictionary
|
||||
yields a list of lists. Each of these lists is a subpath
|
||||
# of the path. E.g., for the SVG path
|
||||
|
||||
<path d="M 10,10 l 0,5 l 5,0 l 0,-5 Z M 30,30 L 30,60"/>
|
||||
|
||||
we'd have two subpaths which will be reduced to absolute
|
||||
coordinates.
|
||||
|
||||
subpath_1 = [ [10, 10], [10, 15], [15, 15], [15, 10], [10,10] ]
|
||||
subpath_2 = [ [30, 30], [30, 60] ]
|
||||
self.paths[<node pointer>] = [ subpath_1, subpath_2 ]
|
||||
|
||||
All of the paths and their subpaths could be drawn as follows:
|
||||
|
||||
for path in self.paths:
|
||||
for subpath in self.paths[path]:
|
||||
first = True
|
||||
for vertex in subpath:
|
||||
if first:
|
||||
moveto( vertex[0], vertex[1] )
|
||||
first = False
|
||||
else:
|
||||
lineto( vertex[0], vertex[1] )
|
||||
|
||||
NOTE: drawing all the paths like the above would not in general
|
||||
give the correct rendering of the document UNLESS path transforms
|
||||
were also tracked and applied.
|
||||
"""
|
||||
|
||||
self.paths = {}
|
||||
self.paths_clone_transform = {}
|
||||
|
||||
def addPathVertices(self, path, node=None, transform=None, cloneTransform=None):
|
||||
|
||||
"""
|
||||
Decompose the path data from an SVG element into individual
|
||||
subpaths, each subpath consisting of absolute move to and line
|
||||
to coordinates. Place these coordinates into a list of polygon
|
||||
vertices.
|
||||
"""
|
||||
|
||||
if (not path) or (len(path) == 0):
|
||||
# Nothing to do
|
||||
return
|
||||
|
||||
# parsePath() may raise an exception. This is okay
|
||||
sp = simplepath.parsePath(path)
|
||||
if (not sp) or (len(sp) == 0):
|
||||
# Path must have been devoid of any real content
|
||||
return
|
||||
|
||||
# Get a cubic super path
|
||||
p = cubicsuperpath.CubicSuperPath(sp)
|
||||
if (not p) or (len(p) == 0):
|
||||
# Probably never happens, but...
|
||||
return
|
||||
|
||||
# if transform:
|
||||
# simpletransform.applyTransformToPath( transform, p )
|
||||
|
||||
# Now traverse the cubic super path
|
||||
subpath_list = []
|
||||
subpath_vertices = []
|
||||
for sp in p:
|
||||
if len(subpath_vertices):
|
||||
# There's a prior subpath: see if it is closed and should be saved
|
||||
if distanceSquared(subpath_vertices[0], subpath_vertices[-1]) < 1:
|
||||
# Keep the prior subpath: it appears to be a closed path
|
||||
subpath_list.append(subpath_vertices)
|
||||
subpath_vertices = []
|
||||
subdivideCubicPath(sp, float(0.2))
|
||||
for csp in sp:
|
||||
# Add this vertex to the list of vetices
|
||||
subpath_vertices.append(csp[1])
|
||||
|
||||
# Handle final subpath
|
||||
if len(subpath_vertices):
|
||||
if distanceSquared(subpath_vertices[0], subpath_vertices[-1]) < 1:
|
||||
# Path appears to be closed so let's keep it
|
||||
subpath_list.append(subpath_vertices)
|
||||
|
||||
# Empty path?
|
||||
if len(subpath_list) == 0:
|
||||
return
|
||||
|
||||
# Store the list of subpaths in a dictionary keyed off of the path's node pointer
|
||||
self.paths[node] = subpath_list
|
||||
self.paths_clone_transform[node] = cloneTransform
|
||||
|
||||
def recursivelyTraverseSvg(self, aNodeList,
|
||||
matCurrent=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
|
||||
parent_visibility='visible', cloneTransform=None):
|
||||
|
||||
"""
|
||||
[ This too is largely lifted from eggbot.py ]
|
||||
|
||||
Recursively walk the SVG document, building polygon vertex lists
|
||||
for each graphical element we support.
|
||||
|
||||
Rendered SVG elements:
|
||||
<circle>, <ellipse>, <line>, <path>, <polygon>, <polyline>, <rect>
|
||||
|
||||
Supported SVG elements:
|
||||
<group>, <use>
|
||||
|
||||
Ignored SVG elements:
|
||||
<defs>, <eggbot>, <metadata>, <namedview>, <pattern>,
|
||||
processing directives
|
||||
|
||||
All other SVG elements trigger an error (including <text>)
|
||||
"""
|
||||
|
||||
for node in aNodeList:
|
||||
|
||||
# Ignore invisible nodes
|
||||
v = node.get('visibility', parent_visibility)
|
||||
if v == 'inherit':
|
||||
v = parent_visibility
|
||||
if v == 'hidden' or v == 'collapse':
|
||||
pass
|
||||
|
||||
# First apply the current matrix transform to this node's tranform
|
||||
matNew = composeTransform(matCurrent, parseTransform(node.get("transform")))
|
||||
|
||||
if node.tag == inkex.addNS('g', 'svg') or node.tag == 'g':
|
||||
|
||||
self.recursivelyTraverseSvg(node, matNew, parent_visibility=v)
|
||||
|
||||
elif node.tag == inkex.addNS('use', 'svg') or node.tag == 'use':
|
||||
|
||||
# A <use> element refers to another SVG element via an xlink:href="#blah"
|
||||
# attribute. We will handle the element by doing an XPath search through
|
||||
# the document, looking for the element with the matching id="blah"
|
||||
# attribute. We then recursively process that element after applying
|
||||
# any necessary (x,y) translation.
|
||||
#
|
||||
# Notes:
|
||||
# 1. We ignore the height and width attributes as they do not apply to
|
||||
# path-like elements, and
|
||||
# 2. Even if the use element has visibility="hidden", SVG still calls
|
||||
# for processing the referenced element. The referenced element is
|
||||
# hidden only if its visibility is "inherit" or "hidden".
|
||||
|
||||
refid = node.get(inkex.addNS('href', 'xlink'))
|
||||
if not refid:
|
||||
pass
|
||||
|
||||
# [1:] to ignore leading '#' in reference
|
||||
path = '//*[@id="%s"]' % refid[1:]
|
||||
refnode = node.xpath(path)
|
||||
if refnode:
|
||||
x = float(node.get('x', '0'))
|
||||
y = float(node.get('y', '0'))
|
||||
# Note: the transform has already been applied
|
||||
if (x != 0) or (y != 0):
|
||||
matNew2 = composeTransform(matNew, parseTransform('translate(%f,%f)' % (x, y)))
|
||||
else:
|
||||
matNew2 = matNew
|
||||
v = node.get('visibility', v)
|
||||
self.recursivelyTraverseSvg(refnode, matNew2,
|
||||
parent_visibility=v, cloneTransform=node.get('transform'))
|
||||
|
||||
elif node.tag == inkex.addNS('path', 'svg'):
|
||||
|
||||
path_data = node.get('d')
|
||||
if path_data:
|
||||
self.addPathVertices(path_data, node, matNew, cloneTransform)
|
||||
|
||||
elif node.tag == inkex.addNS('rect', 'svg') or node.tag == 'rect':
|
||||
|
||||
# Manually transform
|
||||
#
|
||||
# <rect x="X" y="Y" width="W" height="H"/>
|
||||
#
|
||||
# into
|
||||
#
|
||||
# <path d="MX,Y lW,0 l0,H l-W,0 z"/>
|
||||
#
|
||||
# I.e., explicitly draw three sides of the rectangle and the
|
||||
# fourth side implicitly
|
||||
|
||||
# Create a path with the outline of the rectangle
|
||||
x = float(node.get('x'))
|
||||
y = float(node.get('y'))
|
||||
if (not x) or (not y):
|
||||
pass
|
||||
w = float(node.get('width', '0'))
|
||||
h = float(node.get('height', '0'))
|
||||
a = []
|
||||
a.append(['M ', [x, y]])
|
||||
a.append([' l ', [w, 0]])
|
||||
a.append([' l ', [0, h]])
|
||||
a.append([' l ', [-w, 0]])
|
||||
a.append([' Z', []])
|
||||
self.addPathVertices(simplepath.formatPath(a), node, matNew, cloneTransform)
|
||||
|
||||
elif node.tag == inkex.addNS('line', 'svg') or node.tag == 'line':
|
||||
|
||||
# Convert
|
||||
#
|
||||
# <line x1="X1" y1="Y1" x2="X2" y2="Y2/>
|
||||
#
|
||||
# to
|
||||
#
|
||||
# <path d="MX1,Y1 LX2,Y2"/>
|
||||
|
||||
x1 = float(node.get('x1'))
|
||||
y1 = float(node.get('y1'))
|
||||
x2 = float(node.get('x2'))
|
||||
y2 = float(node.get('y2'))
|
||||
if (not x1) or (not y1) or (not x2) or (not y2):
|
||||
pass
|
||||
a = []
|
||||
a.append(['M ', [x1, y1]])
|
||||
a.append([' L ', [x2, y2]])
|
||||
self.addPathVertices(simplepath.formatPath(a), node, matNew, cloneTransform)
|
||||
|
||||
elif node.tag == inkex.addNS('polyline', 'svg') or node.tag == 'polyline':
|
||||
|
||||
# Convert
|
||||
#
|
||||
# <polyline points="x1,y1 x2,y2 x3,y3 [...]"/>
|
||||
#
|
||||
# to
|
||||
#
|
||||
# <path d="Mx1,y1 Lx2,y2 Lx3,y3 [...]"/>
|
||||
#
|
||||
# Note: we ignore polylines with no points
|
||||
|
||||
pl = node.get('points', '').strip()
|
||||
if pl == '':
|
||||
pass
|
||||
|
||||
pa = pl.split()
|
||||
d = "".join(["M " + pa[i] if i == 0 else " L " + pa[i] for i in range(0, len(pa))])
|
||||
self.addPathVertices(d, node, matNew, cloneTransform)
|
||||
|
||||
elif node.tag == inkex.addNS('polygon', 'svg') or node.tag == 'polygon':
|
||||
|
||||
# Convert
|
||||
#
|
||||
# <polygon points="x1,y1 x2,y2 x3,y3 [...]"/>
|
||||
#
|
||||
# to
|
||||
#
|
||||
# <path d="Mx1,y1 Lx2,y2 Lx3,y3 [...] Z"/>
|
||||
#
|
||||
# Note: we ignore polygons with no points
|
||||
|
||||
pl = node.get('points', '').strip()
|
||||
if pl == '':
|
||||
pass
|
||||
|
||||
pa = pl.split()
|
||||
d = "".join(["M " + pa[i] if i == 0 else " L " + pa[i] for i in range(0, len(pa))])
|
||||
d += " Z"
|
||||
self.addPathVertices(d, node, matNew, cloneTransform)
|
||||
|
||||
elif node.tag == inkex.addNS('ellipse', 'svg') or \
|
||||
node.tag == 'ellipse' or \
|
||||
node.tag == inkex.addNS('circle', 'svg') or \
|
||||
node.tag == 'circle':
|
||||
|
||||
# Convert circles and ellipses to a path with two 180 degree arcs.
|
||||
# In general (an ellipse), we convert
|
||||
#
|
||||
# <ellipse rx="RX" ry="RY" cx="X" cy="Y"/>
|
||||
#
|
||||
# to
|
||||
#
|
||||
# <path d="MX1,CY A RX,RY 0 1 0 X2,CY A RX,RY 0 1 0 X1,CY"/>
|
||||
#
|
||||
# where
|
||||
#
|
||||
# X1 = CX - RX
|
||||
# X2 = CX + RX
|
||||
#
|
||||
# Note: ellipses or circles with a radius attribute of value 0 are ignored
|
||||
|
||||
if node.tag == inkex.addNS('ellipse', 'svg') or node.tag == 'ellipse':
|
||||
rx = float(node.get('rx', '0'))
|
||||
ry = float(node.get('ry', '0'))
|
||||
else:
|
||||
rx = float(node.get('r', '0'))
|
||||
ry = rx
|
||||
if rx == 0 or ry == 0:
|
||||
pass
|
||||
|
||||
cx = float(node.get('cx', '0'))
|
||||
cy = float(node.get('cy', '0'))
|
||||
x1 = cx - rx
|
||||
x2 = cx + rx
|
||||
d = 'M %f,%f ' % (x1, cy) + \
|
||||
'A %f,%f ' % (rx, ry) + \
|
||||
'0 1 0 %f,%f ' % (x2, cy) + \
|
||||
'A %f,%f ' % (rx, ry) + \
|
||||
'0 1 0 %f,%f' % (x1, cy)
|
||||
self.addPathVertices(d, node, matNew, cloneTransform)
|
||||
|
||||
elif node.tag == inkex.addNS('pattern', 'svg') or node.tag == 'pattern':
|
||||
|
||||
pass
|
||||
|
||||
elif node.tag == inkex.addNS('metadata', 'svg') or node.tag == 'metadata':
|
||||
|
||||
pass
|
||||
|
||||
elif node.tag == inkex.addNS('defs', 'svg') or node.tag == 'defs':
|
||||
|
||||
pass
|
||||
|
||||
elif node.tag == inkex.addNS('namedview', 'sodipodi') or node.tag == 'namedview':
|
||||
|
||||
pass
|
||||
|
||||
elif node.tag == inkex.addNS('eggbot', 'svg') or node.tag == 'eggbot':
|
||||
|
||||
pass
|
||||
|
||||
elif node.tag == inkex.addNS('text', 'svg') or node.tag == 'text':
|
||||
|
||||
inkex.errormsg('Warning: unable to draw text, please convert it to a path first.')
|
||||
|
||||
pass
|
||||
|
||||
elif not isinstance(node.tag, basestring):
|
||||
|
||||
pass
|
||||
|
||||
else:
|
||||
|
||||
inkex.errormsg('Warning: unable to draw object <%s>, please convert it to a path first.' % node.tag)
|
||||
pass
|
||||
|
||||
def joinWithNode(self, node, path, makeGroup=False, cloneTransform=None):
|
||||
|
||||
"""
|
||||
Generate a SVG <path> element containing the path data "path".
|
||||
Then put this new <path> element into a <group> with the supplied
|
||||
node. This means making a new <group> element and making the
|
||||
node a child of it with the new <path> as a sibling.
|
||||
"""
|
||||
|
||||
if (not path) or (len(path) == 0):
|
||||
return
|
||||
|
||||
if makeGroup:
|
||||
# Make a new SVG <group> element whose parent is the parent of node
|
||||
parent = node.getparent()
|
||||
# was: if not parent:
|
||||
if parent is None:
|
||||
parent = self.document.getroot()
|
||||
g = inkex.etree.SubElement(parent, inkex.addNS('g', 'svg'))
|
||||
|
||||
# Move node to be a child of this new <g> element
|
||||
g.append(node)
|
||||
|
||||
# Promote the node's transform to the new parent group
|
||||
# This way, it will apply to the original paths and the
|
||||
# "twisted" paths
|
||||
transform = node.get('transform')
|
||||
if transform:
|
||||
g.set('transform', transform)
|
||||
del node.attrib['transform']
|
||||
else:
|
||||
g = node.getparent()
|
||||
|
||||
# Now make a <path> element which contains the twist & is a child
|
||||
# of the new <g> element
|
||||
style = {'stroke': '#000000', 'fill': 'none', 'stroke-width': '1'}
|
||||
line_attribs = {'style': simplestyle.formatStyle(style), 'd': path}
|
||||
if (cloneTransform is not None) and (cloneTransform != ''):
|
||||
line_attribs['transform'] = cloneTransform
|
||||
inkex.etree.SubElement(g, inkex.addNS('path', 'svg'), line_attribs)
|
||||
|
||||
def twist(self, ratio):
|
||||
|
||||
if not self.paths:
|
||||
return
|
||||
|
||||
# Now iterate over all of the polygons
|
||||
for path in self.paths:
|
||||
for subpath in self.paths[path]:
|
||||
for i in range(0, len(subpath) - 1):
|
||||
x = subpath[i][0] + ratio * (subpath[i + 1][0] - subpath[i][0])
|
||||
y = subpath[i][1] + ratio * (subpath[i + 1][1] - subpath[i][1])
|
||||
subpath[i] = [x, y]
|
||||
subpath[-1] = subpath[0]
|
||||
def __init__(self):
|
||||
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option(
|
||||
"--nSteps", action="store", type="int",
|
||||
dest="nSteps", default=8,
|
||||
help="Number of iterations to take")
|
||||
self.OptionParser.add_option(
|
||||
"--fRatio", action="store", type="float",
|
||||
dest="fRatio", default=float(0.2),
|
||||
help="Some ratio")
|
||||
|
||||
"""
|
||||
Store each path in an associative array (dictionary) indexed
|
||||
by the lxml.etree pointer for the SVG document element
|
||||
containing the path. Looking up the path in the dictionary
|
||||
yields a list of lists. Each of these lists is a subpath
|
||||
# of the path. E.g., for the SVG path
|
||||
|
||||
<path d="M 10,10 l 0,5 l 5,0 l 0,-5 Z M 30,30 L 30,60"/>
|
||||
|
||||
we'd have two subpaths which will be reduced to absolute
|
||||
coordinates.
|
||||
|
||||
subpath_1 = [ [10, 10], [10, 15], [15, 15], [15, 10], [10,10] ]
|
||||
subpath_2 = [ [30, 30], [30, 60] ]
|
||||
self.paths[<node pointer>] = [ subpath_1, subpath_2 ]
|
||||
|
||||
All of the paths and their subpaths could be drawn as follows:
|
||||
|
||||
for path in self.paths:
|
||||
for subpath in self.paths[path]:
|
||||
first = True
|
||||
for vertex in subpath:
|
||||
if first:
|
||||
moveto( vertex[0], vertex[1] )
|
||||
first = False
|
||||
else:
|
||||
lineto( vertex[0], vertex[1] )
|
||||
|
||||
NOTE: drawing all the paths like the above would not in general
|
||||
give the correct rendering of the document UNLESS path transforms
|
||||
were also tracked and applied.
|
||||
"""
|
||||
|
||||
self.paths = {}
|
||||
self.paths_clone_transform = {}
|
||||
|
||||
def addPathVertices(self, path, node=None, transform=None, cloneTransform=None):
|
||||
|
||||
"""
|
||||
Decompose the path data from an SVG element into individual
|
||||
subpaths, each subpath consisting of absolute move to and line
|
||||
to coordinates. Place these coordinates into a list of polygon
|
||||
vertices.
|
||||
"""
|
||||
|
||||
if (not path) or (len(path) == 0):
|
||||
# Nothing to do
|
||||
return
|
||||
|
||||
# parsePath() may raise an exception. This is okay
|
||||
sp = simplepath.parsePath(path)
|
||||
if (not sp) or (len(sp) == 0):
|
||||
# Path must have been devoid of any real content
|
||||
return
|
||||
|
||||
# Get a cubic super path
|
||||
p = cubicsuperpath.CubicSuperPath(sp)
|
||||
if (not p) or (len(p) == 0):
|
||||
# Probably never happens, but...
|
||||
return
|
||||
|
||||
# if transform:
|
||||
# simpletransform.applyTransformToPath( transform, p )
|
||||
|
||||
# Now traverse the cubic super path
|
||||
subpath_list = []
|
||||
subpath_vertices = []
|
||||
for sp in p:
|
||||
if len(subpath_vertices):
|
||||
# There's a prior subpath: see if it is closed and should be saved
|
||||
if distanceSquared(subpath_vertices[0], subpath_vertices[-1]) < 1:
|
||||
# Keep the prior subpath: it appears to be a closed path
|
||||
subpath_list.append(subpath_vertices)
|
||||
subpath_vertices = []
|
||||
subdivideCubicPath(sp, float(0.2))
|
||||
for csp in sp:
|
||||
# Add this vertex to the list of vetices
|
||||
subpath_vertices.append(csp[1])
|
||||
|
||||
# Handle final subpath
|
||||
if len(subpath_vertices):
|
||||
if distanceSquared(subpath_vertices[0], subpath_vertices[-1]) < 1:
|
||||
# Path appears to be closed so let's keep it
|
||||
subpath_list.append(subpath_vertices)
|
||||
|
||||
# Empty path?
|
||||
if len(subpath_list) == 0:
|
||||
return
|
||||
|
||||
# Store the list of subpaths in a dictionary keyed off of the path's node pointer
|
||||
self.paths[node] = subpath_list
|
||||
self.paths_clone_transform[node] = cloneTransform
|
||||
|
||||
def recursivelyTraverseSvg(self, aNodeList,
|
||||
matCurrent=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
|
||||
parent_visibility='visible', cloneTransform=None):
|
||||
|
||||
"""
|
||||
[ This too is largely lifted from eggbot.py ]
|
||||
|
||||
Recursively walk the SVG document, building polygon vertex lists
|
||||
for each graphical element we support.
|
||||
|
||||
Rendered SVG elements:
|
||||
<circle>, <ellipse>, <line>, <path>, <polygon>, <polyline>, <rect>
|
||||
|
||||
Supported SVG elements:
|
||||
<group>, <use>
|
||||
|
||||
Ignored SVG elements:
|
||||
<defs>, <eggbot>, <metadata>, <namedview>, <pattern>,
|
||||
processing directives
|
||||
|
||||
All other SVG elements trigger an error (including <text>)
|
||||
"""
|
||||
|
||||
for node in aNodeList:
|
||||
|
||||
# Ignore invisible nodes
|
||||
v = node.get('visibility', parent_visibility)
|
||||
if v == 'inherit':
|
||||
v = parent_visibility
|
||||
if v == 'hidden' or v == 'collapse':
|
||||
pass
|
||||
|
||||
# First apply the current matrix transform to this node's tranform
|
||||
matNew = composeTransform(matCurrent, parseTransform(node.get("transform")))
|
||||
|
||||
if node.tag == inkex.addNS('g', 'svg') or node.tag == 'g':
|
||||
|
||||
self.recursivelyTraverseSvg(node, matNew, parent_visibility=v)
|
||||
|
||||
elif node.tag == inkex.addNS('use', 'svg') or node.tag == 'use':
|
||||
|
||||
# A <use> element refers to another SVG element via an xlink:href="#blah"
|
||||
# attribute. We will handle the element by doing an XPath search through
|
||||
# the document, looking for the element with the matching id="blah"
|
||||
# attribute. We then recursively process that element after applying
|
||||
# any necessary (x,y) translation.
|
||||
#
|
||||
# Notes:
|
||||
# 1. We ignore the height and width attributes as they do not apply to
|
||||
# path-like elements, and
|
||||
# 2. Even if the use element has visibility="hidden", SVG still calls
|
||||
# for processing the referenced element. The referenced element is
|
||||
# hidden only if its visibility is "inherit" or "hidden".
|
||||
|
||||
refid = node.get(inkex.addNS('href', 'xlink'))
|
||||
if not refid:
|
||||
pass
|
||||
|
||||
# [1:] to ignore leading '#' in reference
|
||||
path = '//*[@id="%s"]' % refid[1:]
|
||||
refnode = node.xpath(path)
|
||||
if refnode:
|
||||
x = float(node.get('x', '0'))
|
||||
y = float(node.get('y', '0'))
|
||||
# Note: the transform has already been applied
|
||||
if (x != 0) or (y != 0):
|
||||
matNew2 = composeTransform(matNew, parseTransform('translate(%f,%f)' % (x, y)))
|
||||
else:
|
||||
matNew2 = matNew
|
||||
v = node.get('visibility', v)
|
||||
self.recursivelyTraverseSvg(refnode, matNew2,
|
||||
parent_visibility=v, cloneTransform=node.get('transform'))
|
||||
|
||||
elif node.tag == inkex.addNS('path', 'svg'):
|
||||
|
||||
path_data = node.get('d')
|
||||
if path_data:
|
||||
self.addPathVertices(path_data, node, matNew, cloneTransform)
|
||||
|
||||
elif node.tag == inkex.addNS('rect', 'svg') or node.tag == 'rect':
|
||||
|
||||
# Manually transform
|
||||
#
|
||||
# <rect x="X" y="Y" width="W" height="H"/>
|
||||
#
|
||||
# into
|
||||
#
|
||||
# <path d="MX,Y lW,0 l0,H l-W,0 z"/>
|
||||
#
|
||||
# I.e., explicitly draw three sides of the rectangle and the
|
||||
# fourth side implicitly
|
||||
|
||||
# Create a path with the outline of the rectangle
|
||||
x = float(node.get('x'))
|
||||
y = float(node.get('y'))
|
||||
if (not x) or (not y):
|
||||
pass
|
||||
w = float(node.get('width', '0'))
|
||||
h = float(node.get('height', '0'))
|
||||
a = []
|
||||
a.append(['M ', [x, y]])
|
||||
a.append([' l ', [w, 0]])
|
||||
a.append([' l ', [0, h]])
|
||||
a.append([' l ', [-w, 0]])
|
||||
a.append([' Z', []])
|
||||
self.addPathVertices(simplepath.formatPath(a), node, matNew, cloneTransform)
|
||||
|
||||
elif node.tag == inkex.addNS('line', 'svg') or node.tag == 'line':
|
||||
|
||||
# Convert
|
||||
#
|
||||
# <line x1="X1" y1="Y1" x2="X2" y2="Y2/>
|
||||
#
|
||||
# to
|
||||
#
|
||||
# <path d="MX1,Y1 LX2,Y2"/>
|
||||
|
||||
x1 = float(node.get('x1'))
|
||||
y1 = float(node.get('y1'))
|
||||
x2 = float(node.get('x2'))
|
||||
y2 = float(node.get('y2'))
|
||||
if (not x1) or (not y1) or (not x2) or (not y2):
|
||||
pass
|
||||
a = []
|
||||
a.append(['M ', [x1, y1]])
|
||||
a.append([' L ', [x2, y2]])
|
||||
self.addPathVertices(simplepath.formatPath(a), node, matNew, cloneTransform)
|
||||
|
||||
elif node.tag == inkex.addNS('polyline', 'svg') or node.tag == 'polyline':
|
||||
|
||||
# Convert
|
||||
#
|
||||
# <polyline points="x1,y1 x2,y2 x3,y3 [...]"/>
|
||||
#
|
||||
# to
|
||||
#
|
||||
# <path d="Mx1,y1 Lx2,y2 Lx3,y3 [...]"/>
|
||||
#
|
||||
# Note: we ignore polylines with no points
|
||||
|
||||
pl = node.get('points', '').strip()
|
||||
if pl == '':
|
||||
pass
|
||||
|
||||
pa = pl.split()
|
||||
d = "".join(["M " + pa[i] if i == 0 else " L " + pa[i] for i in range(0, len(pa))])
|
||||
self.addPathVertices(d, node, matNew, cloneTransform)
|
||||
|
||||
elif node.tag == inkex.addNS('polygon', 'svg') or node.tag == 'polygon':
|
||||
|
||||
# Convert
|
||||
#
|
||||
# <polygon points="x1,y1 x2,y2 x3,y3 [...]"/>
|
||||
#
|
||||
# to
|
||||
#
|
||||
# <path d="Mx1,y1 Lx2,y2 Lx3,y3 [...] Z"/>
|
||||
#
|
||||
# Note: we ignore polygons with no points
|
||||
|
||||
pl = node.get('points', '').strip()
|
||||
if pl == '':
|
||||
pass
|
||||
|
||||
pa = pl.split()
|
||||
d = "".join(["M " + pa[i] if i == 0 else " L " + pa[i] for i in range(0, len(pa))])
|
||||
d += " Z"
|
||||
self.addPathVertices(d, node, matNew, cloneTransform)
|
||||
|
||||
elif node.tag == inkex.addNS('ellipse', 'svg') or \
|
||||
node.tag == 'ellipse' or \
|
||||
node.tag == inkex.addNS('circle', 'svg') or \
|
||||
node.tag == 'circle':
|
||||
|
||||
# Convert circles and ellipses to a path with two 180 degree arcs.
|
||||
# In general (an ellipse), we convert
|
||||
#
|
||||
# <ellipse rx="RX" ry="RY" cx="X" cy="Y"/>
|
||||
#
|
||||
# to
|
||||
#
|
||||
# <path d="MX1,CY A RX,RY 0 1 0 X2,CY A RX,RY 0 1 0 X1,CY"/>
|
||||
#
|
||||
# where
|
||||
#
|
||||
# X1 = CX - RX
|
||||
# X2 = CX + RX
|
||||
#
|
||||
# Note: ellipses or circles with a radius attribute of value 0 are ignored
|
||||
|
||||
if node.tag == inkex.addNS('ellipse', 'svg') or node.tag == 'ellipse':
|
||||
rx = float(node.get('rx', '0'))
|
||||
ry = float(node.get('ry', '0'))
|
||||
else:
|
||||
rx = float(node.get('r', '0'))
|
||||
ry = rx
|
||||
if rx == 0 or ry == 0:
|
||||
pass
|
||||
|
||||
cx = float(node.get('cx', '0'))
|
||||
cy = float(node.get('cy', '0'))
|
||||
x1 = cx - rx
|
||||
x2 = cx + rx
|
||||
d = 'M %f,%f ' % (x1, cy) + \
|
||||
'A %f,%f ' % (rx, ry) + \
|
||||
'0 1 0 %f,%f ' % (x2, cy) + \
|
||||
'A %f,%f ' % (rx, ry) + \
|
||||
'0 1 0 %f,%f' % (x1, cy)
|
||||
self.addPathVertices(d, node, matNew, cloneTransform)
|
||||
|
||||
elif node.tag == inkex.addNS('pattern', 'svg') or node.tag == 'pattern':
|
||||
|
||||
pass
|
||||
|
||||
elif node.tag == inkex.addNS('metadata', 'svg') or node.tag == 'metadata':
|
||||
|
||||
pass
|
||||
|
||||
elif node.tag == inkex.addNS('defs', 'svg') or node.tag == 'defs':
|
||||
|
||||
pass
|
||||
|
||||
elif node.tag == inkex.addNS('namedview', 'sodipodi') or node.tag == 'namedview':
|
||||
|
||||
pass
|
||||
|
||||
elif node.tag == inkex.addNS('eggbot', 'svg') or node.tag == 'eggbot':
|
||||
|
||||
pass
|
||||
|
||||
elif node.tag == inkex.addNS('text', 'svg') or node.tag == 'text':
|
||||
|
||||
inkex.errormsg('Warning: unable to draw text, please convert it to a path first.')
|
||||
|
||||
pass
|
||||
|
||||
elif not isinstance(node.tag, basestring):
|
||||
|
||||
pass
|
||||
|
||||
else:
|
||||
|
||||
inkex.errormsg('Warning: unable to draw object <%s>, please convert it to a path first.' % node.tag)
|
||||
pass
|
||||
|
||||
def joinWithNode(self, node, path, makeGroup=False, cloneTransform=None):
|
||||
|
||||
"""
|
||||
Generate a SVG <path> element containing the path data "path".
|
||||
Then put this new <path> element into a <group> with the supplied
|
||||
node. This means making a new <group> element and making the
|
||||
node a child of it with the new <path> as a sibling.
|
||||
"""
|
||||
|
||||
if (not path) or (len(path) == 0):
|
||||
return
|
||||
|
||||
if makeGroup:
|
||||
# Make a new SVG <group> element whose parent is the parent of node
|
||||
parent = node.getparent()
|
||||
# was: if not parent:
|
||||
if parent is None:
|
||||
parent = self.document.getroot()
|
||||
g = inkex.etree.SubElement(parent, inkex.addNS('g', 'svg'))
|
||||
|
||||
# Move node to be a child of this new <g> element
|
||||
g.append(node)
|
||||
|
||||
# Promote the node's transform to the new parent group
|
||||
# This way, it will apply to the original paths and the
|
||||
# "twisted" paths
|
||||
transform = node.get('transform')
|
||||
if transform:
|
||||
g.set('transform', transform)
|
||||
del node.attrib['transform']
|
||||
else:
|
||||
g = node.getparent()
|
||||
|
||||
# Now make a <path> element which contains the twist & is a child
|
||||
# of the new <g> element
|
||||
style = {'stroke': '#000000', 'fill': 'none', 'stroke-width': '1'}
|
||||
line_attribs = {'style': simplestyle.formatStyle(style), 'd': path}
|
||||
if (cloneTransform is not None) and (cloneTransform != ''):
|
||||
line_attribs['transform'] = cloneTransform
|
||||
inkex.etree.SubElement(g, inkex.addNS('path', 'svg'), line_attribs)
|
||||
|
||||
def twist(self, ratio):
|
||||
|
||||
if not self.paths:
|
||||
return
|
||||
|
||||
# Now iterate over all of the polygons
|
||||
for path in self.paths:
|
||||
for subpath in self.paths[path]:
|
||||
for i in range(0, len(subpath) - 1):
|
||||
x = subpath[i][0] + ratio * (subpath[i + 1][0] - subpath[i][0])
|
||||
y = subpath[i][1] + ratio * (subpath[i + 1][1] - subpath[i][1])
|
||||
subpath[i] = [x, y]
|
||||
subpath[-1] = subpath[0]
|
||||
|
||||
def draw(self, makeGroup=False):
|
||||
def draw(self, makeGroup=False):
|
||||
|
||||
"""
|
||||
Draw the edges of the current list of vertices
|
||||
"""
|
||||
"""
|
||||
Draw the edges of the current list of vertices
|
||||
"""
|
||||
|
||||
if not self.paths:
|
||||
return
|
||||
if not self.paths:
|
||||
return
|
||||
|
||||
# Now iterate over all of the polygons
|
||||
for path in self.paths:
|
||||
for subpath in self.paths[path]:
|
||||
pdata = ''
|
||||
for vertex in subpath:
|
||||
if pdata == '':
|
||||
pdata = 'M %f,%f' % (vertex[0], vertex[1])
|
||||
else:
|
||||
pdata += ' L %f,%f' % (vertex[0], vertex[1])
|
||||
self.joinWithNode(path, pdata, makeGroup, self.paths_clone_transform[path])
|
||||
# Now iterate over all of the polygons
|
||||
for path in self.paths:
|
||||
for subpath in self.paths[path]:
|
||||
pdata = ''
|
||||
for vertex in subpath:
|
||||
if pdata == '':
|
||||
pdata = 'M %f,%f' % (vertex[0], vertex[1])
|
||||
else:
|
||||
pdata += ' L %f,%f' % (vertex[0], vertex[1])
|
||||
self.joinWithNode(path, pdata, makeGroup, self.paths_clone_transform[path])
|
||||
|
||||
def effect(self):
|
||||
def effect(self):
|
||||
|
||||
# Build a list of the vertices for the document's graphical elements
|
||||
if self.options.ids:
|
||||
# Traverse the selected objects
|
||||
for id in self.options.ids:
|
||||
self.recursivelyTraverseSvg([self.selected[id]])
|
||||
else:
|
||||
# Traverse the entire document
|
||||
self.recursivelyTraverseSvg(self.document.getroot())
|
||||
|
||||
# Now iterate over the vertices N times
|
||||
for n in range(0, self.options.nSteps):
|
||||
self.twist(self.options.fRatio)
|
||||
self.draw(n == 0)
|
||||
# Build a list of the vertices for the document's graphical elements
|
||||
if self.options.ids:
|
||||
# Traverse the selected objects
|
||||
for id in self.options.ids:
|
||||
self.recursivelyTraverseSvg([self.selected[id]])
|
||||
else:
|
||||
# Traverse the entire document
|
||||
self.recursivelyTraverseSvg(self.document.getroot())
|
||||
|
||||
# Now iterate over the vertices N times
|
||||
for n in range(0, self.options.nSteps):
|
||||
self.twist(self.options.fRatio)
|
||||
self.draw(n == 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
e = Twist()
|
||||
e.affect()
|
||||
e = Twist()
|
||||
e.affect()
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -22,14 +22,14 @@ The STEP SCALING FACTOR below can be used to work with motor-driver combinations
|
|||
|
||||
|
||||
For 1600 steps/rev, use:
|
||||
STEP_SCALE = 4
|
||||
STEP_SCALE = 4
|
||||
|
||||
For NORMAL USE, with overall 3200 steps/rev, use the default value:
|
||||
STEP_SCALE = 2
|
||||
STEP_SCALE = 2
|
||||
|
||||
For 6400 steps/rev, use:
|
||||
STEP_SCALE = 1
|
||||
|
||||
STEP_SCALE = 1
|
||||
|
||||
Other _integer_ scaling values can be used as well, with similar scaling.
|
||||
|
||||
"""
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -21,35 +21,35 @@ import inkex
|
|||
|
||||
|
||||
class PresetHatch(inkex.Effect):
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option("--title")
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option("--title")
|
||||
|
||||
def effect(self):
|
||||
self.svgDefRead = False
|
||||
self.svg = self.document.getroot()
|
||||
self.recursiveDefDataScan(self.svg)
|
||||
def effect(self):
|
||||
self.svgDefRead = False
|
||||
self.svg = self.document.getroot()
|
||||
self.recursiveDefDataScan(self.svg)
|
||||
|
||||
def recursiveDefDataScan(self, aNodeList):
|
||||
for node in aNodeList:
|
||||
if node.tag == inkex.addNS('defs', 'svg') or node.tag == 'defs':
|
||||
self.recursiveDefDataScan(node)
|
||||
elif node.tag == inkex.addNS('path-effect', 'inkscape'):
|
||||
if node.get('effect') == 'rough_hatches':
|
||||
node.set('dist_rdm', '0;1')
|
||||
node.set('growth', str(0))
|
||||
# node.set( 'do_bend', 'false' )
|
||||
node.set('bottom_edge_variation', '0;1')
|
||||
node.set('top_edge_variation', '0;1')
|
||||
node.set('bottom_tgt_variation', '0;1')
|
||||
node.set('top_tgt_variation', '0;1')
|
||||
node.set('scale_bf', str(2))
|
||||
node.set('scale_bb', str(2))
|
||||
node.set('scale_tf', str(2))
|
||||
node.set('scale_tb', str(2))
|
||||
node.set('top_smth_variation', '0;1')
|
||||
node.set('bottom_smth_variation', '0;1')
|
||||
node.set('fat_output', 'false')
|
||||
def recursiveDefDataScan(self, aNodeList):
|
||||
for node in aNodeList:
|
||||
if node.tag == inkex.addNS('defs', 'svg') or node.tag == 'defs':
|
||||
self.recursiveDefDataScan(node)
|
||||
elif node.tag == inkex.addNS('path-effect', 'inkscape'):
|
||||
if node.get('effect') == 'rough_hatches':
|
||||
node.set('dist_rdm', '0;1')
|
||||
node.set('growth', str(0))
|
||||
# node.set( 'do_bend', 'false' )
|
||||
node.set('bottom_edge_variation', '0;1')
|
||||
node.set('top_edge_variation', '0;1')
|
||||
node.set('bottom_tgt_variation', '0;1')
|
||||
node.set('top_tgt_variation', '0;1')
|
||||
node.set('scale_bf', str(2))
|
||||
node.set('scale_bb', str(2))
|
||||
node.set('scale_tf', str(2))
|
||||
node.set('scale_tb', str(2))
|
||||
node.set('top_smth_variation', '0;1')
|
||||
node.set('bottom_smth_variation', '0;1')
|
||||
node.set('fat_output', 'false')
|
||||
|
||||
|
||||
e = PresetHatch()
|
||||
|
|
|
@ -31,146 +31,146 @@ import simpletransform
|
|||
|
||||
|
||||
def dist(x0, y0, x1, y1):
|
||||
return math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2)
|
||||
return math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2)
|
||||
|
||||
|
||||
def find_ordering_naive(objlist):
|
||||
"""
|
||||
Takes a list of (id, (startX, startY, endX, endY)), and finds the best ordering.
|
||||
Doesn't handle anything fancy, like reversing the ordering, but it's useful for now.
|
||||
Returns a list of JUST THE IDs, in a better order, as well as the original and optimized
|
||||
"air distance" which is just the distance traveled in the air. Perhaps we want to make
|
||||
these comparison distances into something more relevant such as degrees traveled?
|
||||
"""
|
||||
"""
|
||||
Takes a list of (id, (startX, startY, endX, endY)), and finds the best ordering.
|
||||
Doesn't handle anything fancy, like reversing the ordering, but it's useful for now.
|
||||
Returns a list of JUST THE IDs, in a better order, as well as the original and optimized
|
||||
"air distance" which is just the distance traveled in the air. Perhaps we want to make
|
||||
these comparison distances into something more relevant such as degrees traveled?
|
||||
"""
|
||||
|
||||
# let's figure out the default in-air length, so we know how much we improved
|
||||
air_length_default = 0
|
||||
try:
|
||||
oldx = objlist[0][1][2]
|
||||
oldy = objlist[0][1][3]
|
||||
except:
|
||||
inkex.errormsg(gettext.gettext(str(objlist[0])))
|
||||
sys.exit(1)
|
||||
for id, coords in objlist[1:]:
|
||||
air_length_default += dist(oldx, oldy, coords[0], coords[1])
|
||||
oldx = coords[2]
|
||||
oldy = coords[3]
|
||||
# fid.write("Default air distance: %d\n" % air_length_default)
|
||||
# let's figure out the default in-air length, so we know how much we improved
|
||||
air_length_default = 0
|
||||
try:
|
||||
oldx = objlist[0][1][2]
|
||||
oldy = objlist[0][1][3]
|
||||
except:
|
||||
inkex.errormsg(gettext.gettext(str(objlist[0])))
|
||||
sys.exit(1)
|
||||
for id, coords in objlist[1:]:
|
||||
air_length_default += dist(oldx, oldy, coords[0], coords[1])
|
||||
oldx = coords[2]
|
||||
oldy = coords[3]
|
||||
# fid.write("Default air distance: %d\n" % air_length_default)
|
||||
|
||||
air_length_ordered = 0
|
||||
# for now, start with a random one:
|
||||
sort_list = []
|
||||
random_index = random.randint(0, len(objlist) - 1)
|
||||
sort_list.append(objlist[random_index])
|
||||
objlist.remove(objlist[random_index])
|
||||
air_length_ordered = 0
|
||||
# for now, start with a random one:
|
||||
sort_list = []
|
||||
random_index = random.randint(0, len(objlist) - 1)
|
||||
sort_list.append(objlist[random_index])
|
||||
objlist.remove(objlist[random_index])
|
||||
|
||||
# for now, do this in the most naive way:
|
||||
# for the previous end point, iterate over each remaining path and pick the closest starting point
|
||||
while len(objlist) > 0:
|
||||
min_distance = 100000000 # TODO put something else here better?
|
||||
for path in objlist:
|
||||
# instead of having a prevX, prevY, we just look at the last item in sort_list
|
||||
this_distance = dist(sort_list[-1][1][2], sort_list[-1][1][3], path[1][0], path[1][1])
|
||||
# this is such a common thing to do, you'd think there would be a name for it...
|
||||
if this_distance < min_distance:
|
||||
min_distance = this_distance
|
||||
min_path = path
|
||||
air_length_ordered += min_distance
|
||||
sort_list.append(min_path)
|
||||
objlist.remove(min_path)
|
||||
# for now, do this in the most naive way:
|
||||
# for the previous end point, iterate over each remaining path and pick the closest starting point
|
||||
while len(objlist) > 0:
|
||||
min_distance = 100000000 # TODO put something else here better?
|
||||
for path in objlist:
|
||||
# instead of having a prevX, prevY, we just look at the last item in sort_list
|
||||
this_distance = dist(sort_list[-1][1][2], sort_list[-1][1][3], path[1][0], path[1][1])
|
||||
# this is such a common thing to do, you'd think there would be a name for it...
|
||||
if this_distance < min_distance:
|
||||
min_distance = this_distance
|
||||
min_path = path
|
||||
air_length_ordered += min_distance
|
||||
sort_list.append(min_path)
|
||||
objlist.remove(min_path)
|
||||
|
||||
# fid.write("optimized air distance: %d\n" % air_length_ordered)
|
||||
# fid.write("optimized air distance: %d\n" % air_length_ordered)
|
||||
|
||||
# remove the extraneous info from the list order
|
||||
sort_order = [id for id, coords in sort_list]
|
||||
return sort_order, air_length_default, air_length_ordered
|
||||
# remove the extraneous info from the list order
|
||||
sort_order = [id for id, coords in sort_list]
|
||||
return sort_order, air_length_default, air_length_ordered
|
||||
|
||||
|
||||
def conv(x, y, trans_matrix=None):
|
||||
"""
|
||||
not used currently, but can be used to apply a translation matrix to an (x, y) pair
|
||||
I'm sure there is a better way to do this using simpletransform or it's ilk
|
||||
"""
|
||||
"""
|
||||
not used currently, but can be used to apply a translation matrix to an (x, y) pair
|
||||
I'm sure there is a better way to do this using simpletransform or it's ilk
|
||||
"""
|
||||
|
||||
if trans_matrix:
|
||||
xt = trans_matrix[0][0] * x + trans_matrix[0][1] * y + trans_matrix[0][2]
|
||||
yt = trans_matrix[1][0] * x + trans_matrix[1][1] * y + trans_matrix[1][2]
|
||||
return xt, yt
|
||||
else:
|
||||
return x, y
|
||||
if trans_matrix:
|
||||
xt = trans_matrix[0][0] * x + trans_matrix[0][1] * y + trans_matrix[0][2]
|
||||
yt = trans_matrix[1][0] * x + trans_matrix[1][1] * y + trans_matrix[1][2]
|
||||
return xt, yt
|
||||
else:
|
||||
return x, y
|
||||
|
||||
|
||||
class EggBotReorderPaths(inkex.Effect):
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
|
||||
# self.OptionParser.add_option( '-r', '--reverse', action='store', type="inkbool",
|
||||
# dest="reverse", default=True, help="Enable 'reverse path direction' optimizations" )
|
||||
# self.OptionParser.add_option( '-w', '--wrap', action='store', type="inkbool",
|
||||
# dest="wrap", default=True, help="Enable 'wrap egg axis' optimizations" )
|
||||
# self.OptionParser.add_option( '-r', '--reverse', action='store', type="inkbool",
|
||||
# dest="reverse", default=True, help="Enable 'reverse path direction' optimizations" )
|
||||
# self.OptionParser.add_option( '-w', '--wrap', action='store', type="inkbool",
|
||||
# dest="wrap", default=True, help="Enable 'wrap egg axis' optimizations" )
|
||||
|
||||
def get_start_end(self, node, transform):
|
||||
"""Given a node, return the start and end points"""
|
||||
d = node.get('d')
|
||||
sp = simplepath.parsePath(d)
|
||||
def get_start_end(self, node, transform):
|
||||
"""Given a node, return the start and end points"""
|
||||
d = node.get('d')
|
||||
sp = simplepath.parsePath(d)
|
||||
|
||||
# simplepath converts coordinates to absolute and cleans them up, but
|
||||
# these are still some big assumptions here, are they always valid? TODO
|
||||
startX = sp[0][1][0]
|
||||
startY = sp[0][1][1]
|
||||
if sp[-1][0] == 'Z':
|
||||
# go back to start
|
||||
endX = startX
|
||||
endY = startY
|
||||
else:
|
||||
endX = sp[-1][1][-2]
|
||||
endY = sp[-1][1][-1]
|
||||
# simplepath converts coordinates to absolute and cleans them up, but
|
||||
# these are still some big assumptions here, are they always valid? TODO
|
||||
startX = sp[0][1][0]
|
||||
startY = sp[0][1][1]
|
||||
if sp[-1][0] == 'Z':
|
||||
# go back to start
|
||||
endX = startX
|
||||
endY = startY
|
||||
else:
|
||||
endX = sp[-1][1][-2]
|
||||
endY = sp[-1][1][-1]
|
||||
|
||||
sx, sy = conv(startX, startY, transform)
|
||||
ex, ey = conv(endX, endY, transform)
|
||||
return sx, sy, ex, ey
|
||||
sx, sy = conv(startX, startY, transform)
|
||||
ex, ey = conv(endX, endY, transform)
|
||||
return sx, sy, ex, ey
|
||||
|
||||
def effect(self):
|
||||
"""This is the main entry point"""
|
||||
def effect(self):
|
||||
"""This is the main entry point"""
|
||||
|
||||
# based partially on the restack.py extension
|
||||
if len(self.selected) > 0:
|
||||
svg = self.document.getroot()
|
||||
# based partially on the restack.py extension
|
||||
if len(self.selected) > 0:
|
||||
svg = self.document.getroot()
|
||||
|
||||
# TODO check for non-path elements?
|
||||
# TODO it seems like the order of selection is not consistent
|
||||
# TODO check for non-path elements?
|
||||
# TODO it seems like the order of selection is not consistent
|
||||
|
||||
# fid = open("/home/matthew/debug.txt", "w")
|
||||
# fid = open("/home/matthew/debug.txt", "w")
|
||||
|
||||
# for each selected item - TODO make this be all objects, everywhere
|
||||
# I can think of two options:
|
||||
# 1. Iterate over all paths in root, then iterate over all layers, and their paths
|
||||
# 2. Some magic with xpath? (would this limit us to specific node types?)
|
||||
# for each selected item - TODO make this be all objects, everywhere
|
||||
# I can think of two options:
|
||||
# 1. Iterate over all paths in root, then iterate over all layers, and their paths
|
||||
# 2. Some magic with xpath? (would this limit us to specific node types?)
|
||||
|
||||
objlist = []
|
||||
for id, node in self.selected.iteritems():
|
||||
transform = node.get('transform')
|
||||
if transform:
|
||||
transform = simpletransform.parseTransform(transform)
|
||||
objlist = []
|
||||
for id, node in self.selected.iteritems():
|
||||
transform = node.get('transform')
|
||||
if transform:
|
||||
transform = simpletransform.parseTransform(transform)
|
||||
|
||||
item = (id, self.get_start_end(node, transform))
|
||||
objlist.append(item)
|
||||
item = (id, self.get_start_end(node, transform))
|
||||
objlist.append(item)
|
||||
|
||||
# sort / order the objects
|
||||
sort_order, air_distance_default, air_distance_ordered = find_ordering_naive(objlist)
|
||||
# sort / order the objects
|
||||
sort_order, air_distance_default, air_distance_ordered = find_ordering_naive(objlist)
|
||||
|
||||
for id in sort_order:
|
||||
# There's some good magic here, that you can use an
|
||||
# object id to index into self.selected. Brilliant!
|
||||
self.current_layer.append(self.selected[id])
|
||||
for id in sort_order:
|
||||
# There's some good magic here, that you can use an
|
||||
# object id to index into self.selected. Brilliant!
|
||||
self.current_layer.append(self.selected[id])
|
||||
|
||||
# fid.close()
|
||||
# fid.close()
|
||||
|
||||
if air_distance_default > 0: # don't divide by zero. :P
|
||||
improvement_pct = 100 * ((air_distance_default - air_distance_ordered) / air_distance_default)
|
||||
inkex.errormsg(gettext.gettext("Selected paths have been reordered and optimized for quicker EggBot plotting.\n\nOriginal air-distance: %d\nOptimized air-distance: %d\nDistance reduced by: %1.2d%%\n\nHave a nice day!" % (air_distance_default, air_distance_ordered, improvement_pct)))
|
||||
else:
|
||||
inkex.errormsg(gettext.gettext("Unable to start. Please select multiple distinct paths. :)"))
|
||||
if air_distance_default > 0: # don't divide by zero. :P
|
||||
improvement_pct = 100 * ((air_distance_default - air_distance_ordered) / air_distance_default)
|
||||
inkex.errormsg(gettext.gettext("Selected paths have been reordered and optimized for quicker EggBot plotting.\n\nOriginal air-distance: %d\nOptimized air-distance: %d\nDistance reduced by: %1.2d%%\n\nHave a nice day!" % (air_distance_default, air_distance_ordered, improvement_pct)))
|
||||
else:
|
||||
inkex.errormsg(gettext.gettext("Unable to start. Please select multiple distinct paths. :)"))
|
||||
|
||||
|
||||
e = EggBotReorderPaths()
|
||||
|
|
|
@ -6,38 +6,38 @@ USB_DEVICE_TREE = '/sys/bus/usb/devices'
|
|||
|
||||
|
||||
def findEiBotBoards():
|
||||
"""Find only those USB devices that declare themselves to be EiBotBoards"""
|
||||
"""Find only those USB devices that declare themselves to be EiBotBoards"""
|
||||
|
||||
# find all USB devices whose product name is 'EiBotBoard'
|
||||
with os.popen('fgrep -l EiBotBoard %s/*/product' % USB_DEVICE_TREE) as pipe:
|
||||
for path in [os.path.split(path)[0] for path in pipe.readlines()]:
|
||||
device = os.path.split(path)[1]
|
||||
# find all USB devices whose product name is 'EiBotBoard'
|
||||
with os.popen('fgrep -l EiBotBoard %s/*/product' % USB_DEVICE_TREE) as pipe:
|
||||
for path in [os.path.split(path)[0] for path in pipe.readlines()]:
|
||||
device = os.path.split(path)[1]
|
||||
|
||||
# for each device endpoint ...
|
||||
for dir in os.listdir(path):
|
||||
if dir.startswith(device):
|
||||
# for each device endpoint ...
|
||||
for dir in os.listdir(path):
|
||||
if dir.startswith(device):
|
||||
|
||||
# find the endpoint that supports tty access
|
||||
ttydir = os.path.join(USB_DEVICE_TREE, device, dir, 'tty')
|
||||
if os.path.exists(ttydir):
|
||||
# find the endpoint that supports tty access
|
||||
ttydir = os.path.join(USB_DEVICE_TREE, device, dir, 'tty')
|
||||
if os.path.exists(ttydir):
|
||||
|
||||
# And emit each (the) interface name
|
||||
for ttyname in os.listdir(ttydir):
|
||||
yield os.path.join(DEV_TREE, ttyname)
|
||||
# And emit each (the) interface name
|
||||
for ttyname in os.listdir(ttydir):
|
||||
yield os.path.join(DEV_TREE, ttyname)
|
||||
|
||||
|
||||
def findPorts():
|
||||
for device in os.listdir(DEV_TREE):
|
||||
if not device.startswith('ttyACM'):
|
||||
continue
|
||||
yield os.path.join(DEV_TREE, device)
|
||||
for device in os.listdir(DEV_TREE):
|
||||
if not device.startswith('ttyACM'):
|
||||
continue
|
||||
yield os.path.join(DEV_TREE, device)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Looking for EiBotBoards")
|
||||
for port in findEiBotBoards():
|
||||
print(" ", port)
|
||||
print("Looking for EiBotBoards")
|
||||
for port in findEiBotBoards():
|
||||
print(" ", port)
|
||||
|
||||
print("Looking for COM ports")
|
||||
for port in findPorts():
|
||||
print(" ", port)
|
||||
print("Looking for COM ports")
|
||||
for port in findPorts():
|
||||
print(" ", port)
|
||||
|
|
|
@ -32,155 +32,154 @@ FONT_GROUP_V_SPACING = 45
|
|||
|
||||
|
||||
def draw_svg_text(char, face, offset, vertoffset, parent):
|
||||
style = {'stroke': '#000000', 'fill': 'none'}
|
||||
pathString = face[char]
|
||||
splitString = pathString.split()
|
||||
midpoint = offset - float(splitString[0])
|
||||
splitpoint = pathString.find("M")
|
||||
# Space glyphs have just widths with no moves, so their splitpoint is 0
|
||||
# We only want to generate paths for visible glyphs where splitpoint > 0
|
||||
if splitpoint > 0:
|
||||
pathString = pathString[splitpoint:] # portion after first move
|
||||
trans = 'translate(' + str(midpoint) + ',' + str(vertoffset) + ')'
|
||||
text_attribs = {'style': simplestyle.formatStyle(style), 'd': pathString, 'transform': trans}
|
||||
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), text_attribs)
|
||||
return midpoint + float(splitString[1]) # new offset value
|
||||
style = {'stroke': '#000000', 'fill': 'none'}
|
||||
pathString = face[char]
|
||||
splitString = pathString.split()
|
||||
midpoint = offset - float(splitString[0])
|
||||
splitpoint = pathString.find("M")
|
||||
# Space glyphs have just widths with no moves, so their splitpoint is 0
|
||||
# We only want to generate paths for visible glyphs where splitpoint > 0
|
||||
if splitpoint > 0:
|
||||
pathString = pathString[splitpoint:] # portion after first move
|
||||
trans = 'translate(' + str(midpoint) + ',' + str(vertoffset) + ')'
|
||||
text_attribs = {'style': simplestyle.formatStyle(style), 'd': pathString, 'transform': trans}
|
||||
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), text_attribs)
|
||||
return midpoint + float(splitString[1]) # new offset value
|
||||
|
||||
|
||||
def svg_text_width(char, face, offset):
|
||||
pathString = face[char]
|
||||
splitString = pathString.split()
|
||||
midpoint = offset - float(splitString[0])
|
||||
return midpoint + float(splitString[1]) # new offset value
|
||||
pathString = face[char]
|
||||
splitString = pathString.split()
|
||||
midpoint = offset - float(splitString[0])
|
||||
return midpoint + float(splitString[1]) # new offset value
|
||||
|
||||
|
||||
class Hershey(inkex.Effect):
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option("--tab", # NOTE: value is not used.
|
||||
action="store", type="string",
|
||||
dest="tab", default="splash",
|
||||
help="The active tab when Apply was pressed")
|
||||
self.OptionParser.add_option("--text",
|
||||
action="store", type="string",
|
||||
dest="text", default="Hershey Text for Inkscape",
|
||||
help="The input text to render")
|
||||
self.OptionParser.add_option("--action",
|
||||
action="store", type="string",
|
||||
dest="action", default="render",
|
||||
help="The active option when Apply was pressed")
|
||||
self.OptionParser.add_option("--fontface",
|
||||
action="store", type="string",
|
||||
dest="fontface", default="rowmans",
|
||||
help="The selected font face when Apply was pressed")
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option("--tab", # NOTE: value is not used.
|
||||
action="store", type="string",
|
||||
dest="tab", default="splash",
|
||||
help="The active tab when Apply was pressed")
|
||||
self.OptionParser.add_option("--text",
|
||||
action="store", type="string",
|
||||
dest="text", default="Hershey Text for Inkscape",
|
||||
help="The input text to render")
|
||||
self.OptionParser.add_option("--action",
|
||||
action="store", type="string",
|
||||
dest="action", default="render",
|
||||
help="The active option when Apply was pressed")
|
||||
self.OptionParser.add_option("--fontface",
|
||||
action="store", type="string",
|
||||
dest="fontface", default="rowmans",
|
||||
help="The selected font face when Apply was pressed")
|
||||
|
||||
def effect(self):
|
||||
def effect(self):
|
||||
|
||||
OutputGenerated = False
|
||||
OutputGenerated = False
|
||||
|
||||
# Embed text in group to make manipulation easier:
|
||||
g_attribs = {inkex.addNS('label', 'inkscape'): 'Hershey Text'}
|
||||
g = inkex.etree.SubElement(self.current_layer, 'g', g_attribs)
|
||||
# Embed text in group to make manipulation easier:
|
||||
g_attribs = {inkex.addNS('label', 'inkscape'): 'Hershey Text'}
|
||||
g = inkex.etree.SubElement(self.current_layer, 'g', g_attribs)
|
||||
|
||||
scale = self.unittouu('1px') # convert to document units
|
||||
font = eval('hersheydata.' + str(self.options.fontface))
|
||||
clearfont = hersheydata.futural
|
||||
# Baseline: modernized roman simplex from JHF distribution.
|
||||
scale = self.unittouu('1px') # convert to document units
|
||||
font = eval('hersheydata.' + str(self.options.fontface))
|
||||
clearfont = hersheydata.futural
|
||||
# Baseline: modernized roman simplex from JHF distribution.
|
||||
|
||||
w = 0 # Initial spacing offset
|
||||
v = 0 # Initial vertical offset
|
||||
spacing = 3 # spacing between letters
|
||||
w = 0 # Initial spacing offset
|
||||
v = 0 # Initial vertical offset
|
||||
spacing = 3 # spacing between letters
|
||||
|
||||
if self.options.action == "render":
|
||||
# evaluate text string
|
||||
letterVals = [ord(q) - 32 for q in self.options.text]
|
||||
for q in letterVals:
|
||||
if (q <= 0) or (q > 95):
|
||||
w += 2 * spacing
|
||||
else:
|
||||
w = draw_svg_text(q, font, w, 0, g)
|
||||
OutputGenerated = True
|
||||
elif self.options.action == 'sample':
|
||||
w, v = self.render_table_of_all_fonts('group_allfonts', g, spacing, clearfont)
|
||||
OutputGenerated = True
|
||||
scale *= 0.4 # Typically scales to about A4/US Letter size
|
||||
elif self.options.action == 'sampleHW':
|
||||
w, v = self.render_table_of_all_fonts('group_hwfonts', g, spacing, clearfont)
|
||||
OutputGenerated = True
|
||||
scale *= 0.5 # Typically scales to about A4/US Letter size
|
||||
else:
|
||||
# Generate glyph table
|
||||
wmax = 0
|
||||
for p in range(0, 10):
|
||||
w = 0
|
||||
v = spacing * (15 * p - 67)
|
||||
for q in range(0, 10):
|
||||
r = p * 10 + q
|
||||
if (r <= 0) or (r > 95):
|
||||
w += 5 * spacing
|
||||
else:
|
||||
w = draw_svg_text(r, clearfont, w, v, g)
|
||||
w = draw_svg_text(r, font, w, v, g)
|
||||
w += 5 * spacing
|
||||
if w > wmax:
|
||||
wmax = w
|
||||
w = wmax
|
||||
OutputGenerated = True
|
||||
# Translate group to center of view, approximately
|
||||
t = 'translate(' + str(self.view_center[0] - scale * w / 2) + ',' + str(self.view_center[1] - scale * v / 2) + ')'
|
||||
if scale != 1:
|
||||
t += ' scale(' + str(scale) + ')'
|
||||
g.set('transform', t)
|
||||
if self.options.action == "render":
|
||||
# evaluate text string
|
||||
letterVals = [ord(q) - 32 for q in self.options.text]
|
||||
for q in letterVals:
|
||||
if (q <= 0) or (q > 95):
|
||||
w += 2 * spacing
|
||||
else:
|
||||
w = draw_svg_text(q, font, w, 0, g)
|
||||
OutputGenerated = True
|
||||
elif self.options.action == 'sample':
|
||||
w, v = self.render_table_of_all_fonts('group_allfonts', g, spacing, clearfont)
|
||||
OutputGenerated = True
|
||||
scale *= 0.4 # Typically scales to about A4/US Letter size
|
||||
elif self.options.action == 'sampleHW':
|
||||
w, v = self.render_table_of_all_fonts('group_hwfonts', g, spacing, clearfont)
|
||||
OutputGenerated = True
|
||||
scale *= 0.5 # Typically scales to about A4/US Letter size
|
||||
else:
|
||||
# Generate glyph table
|
||||
wmax = 0
|
||||
for p in range(0, 10):
|
||||
w = 0
|
||||
v = spacing * (15 * p - 67)
|
||||
for q in range(0, 10):
|
||||
r = p * 10 + q
|
||||
if (r <= 0) or (r > 95):
|
||||
w += 5 * spacing
|
||||
else:
|
||||
w = draw_svg_text(r, clearfont, w, v, g)
|
||||
w = draw_svg_text(r, font, w, v, g)
|
||||
w += 5 * spacing
|
||||
if w > wmax:
|
||||
wmax = w
|
||||
w = wmax
|
||||
OutputGenerated = True
|
||||
# Translate group to center of view, approximately
|
||||
t = 'translate(' + str(self.view_center[0] - scale * w / 2) + ',' + str(self.view_center[1] - scale * v / 2) + ')'
|
||||
if scale != 1:
|
||||
t += ' scale(' + str(scale) + ')'
|
||||
g.set('transform', t)
|
||||
|
||||
if not OutputGenerated:
|
||||
self.current_layer.remove(g) # remove empty group, if no SVG was generated.
|
||||
if not OutputGenerated:
|
||||
self.current_layer.remove(g) # remove empty group, if no SVG was generated.
|
||||
|
||||
def render_table_of_all_fonts(self, fontgroupname, parent, spacing, clearfont):
|
||||
v = 0
|
||||
wmax = 0
|
||||
wmin = 0
|
||||
fontgroup = eval('hersheydata.' + fontgroupname)
|
||||
def render_table_of_all_fonts(self, fontgroupname, parent, spacing, clearfont):
|
||||
v = 0
|
||||
wmax = 0
|
||||
wmin = 0
|
||||
fontgroup = eval('hersheydata.' + fontgroupname)
|
||||
|
||||
# Render list of font names in a vertical column:
|
||||
nFontIndex = 0
|
||||
for f in fontgroup:
|
||||
w = 0
|
||||
letterVals = [ord(q) - 32 for q in (f[1] + ' -> ')]
|
||||
# we want to right-justify the clear text, so need to know its width
|
||||
for q in letterVals:
|
||||
w = svg_text_width(q, clearfont, w)
|
||||
# Render list of font names in a vertical column:
|
||||
nFontIndex = 0
|
||||
for f in fontgroup:
|
||||
w = 0
|
||||
letterVals = [ord(q) - 32 for q in (f[1] + ' -> ')]
|
||||
# we want to right-justify the clear text, so need to know its width
|
||||
for q in letterVals:
|
||||
w = svg_text_width(q, clearfont, w)
|
||||
|
||||
w = -w # move the name text left by its width
|
||||
if w < wmin:
|
||||
wmin = w
|
||||
# print the font name
|
||||
for q in letterVals:
|
||||
w = draw_svg_text(q, clearfont, w, v, parent)
|
||||
v += FONT_GROUP_V_SPACING
|
||||
if w > wmax:
|
||||
wmax = w
|
||||
w = -w # move the name text left by its width
|
||||
if w < wmin:
|
||||
wmin = w
|
||||
# print the font name
|
||||
for q in letterVals:
|
||||
w = draw_svg_text(q, clearfont, w, v, parent)
|
||||
v += FONT_GROUP_V_SPACING
|
||||
if w > wmax:
|
||||
wmax = w
|
||||
|
||||
# Next, we render a second column. The user's text, in each of the different fonts:
|
||||
v = 0 # back to top line
|
||||
wmaxname = wmax + 8 # single space width
|
||||
for f in fontgroup:
|
||||
w = wmaxname
|
||||
font = eval('hersheydata.' + f[0])
|
||||
# evaluate text string
|
||||
letterVals = [ord(q) - 32 for q in self.options.text]
|
||||
for q in letterVals:
|
||||
if (q <= 0) or (q > 95):
|
||||
w += 2 * spacing
|
||||
else:
|
||||
w = draw_svg_text(q, font, w, v, parent)
|
||||
v += FONT_GROUP_V_SPACING
|
||||
if w > wmax:
|
||||
wmax = w
|
||||
return wmax + wmin, v
|
||||
# Next, we render a second column. The user's text, in each of the different fonts:
|
||||
v = 0 # back to top line
|
||||
wmaxname = wmax + 8 # single space width
|
||||
for f in fontgroup:
|
||||
w = wmaxname
|
||||
font = eval('hersheydata.' + f[0])
|
||||
# evaluate text string
|
||||
letterVals = [ord(q) - 32 for q in self.options.text]
|
||||
for q in letterVals:
|
||||
if (q <= 0) or (q > 95):
|
||||
w += 2 * spacing
|
||||
else:
|
||||
w = draw_svg_text(q, font, w, v, parent)
|
||||
v += FONT_GROUP_V_SPACING
|
||||
if w > wmax:
|
||||
wmax = w
|
||||
return wmax + wmin, v
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
e = Hershey()
|
||||
e.affect()
|
||||
e = Hershey()
|
||||
e.affect()
|
||||
|
||||
# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -10,157 +10,157 @@ N_EGG_AXIS_STEPS = 3200 # steps for the egg motor move in one revolution
|
|||
|
||||
class EggbotPlotEAGLE:
|
||||
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self.serialPort = parent.serialPort
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self.serialPort = parent.serialPort
|
||||
|
||||
def reset(self):
|
||||
self.fX = None
|
||||
self.fY = None
|
||||
self.fPrevX = None
|
||||
self.fPrevY = None
|
||||
self.bPenIsUp = True
|
||||
self.ptFirst = None
|
||||
self.bStartingFirstLayer = True
|
||||
def reset(self):
|
||||
self.fX = None
|
||||
self.fY = None
|
||||
self.fPrevX = None
|
||||
self.fPrevY = None
|
||||
self.bPenIsUp = True
|
||||
self.ptFirst = None
|
||||
self.bStartingFirstLayer = True
|
||||
|
||||
def plot(self):
|
||||
def plot(self):
|
||||
|
||||
if self.serialPort is None or not self.serialPort.isConnected():
|
||||
return
|
||||
if self.serialPort is None or not self.serialPort.isConnected():
|
||||
return
|
||||
|
||||
self.reset()
|
||||
self.initializeServo()
|
||||
self.reset()
|
||||
self.initializeServo()
|
||||
|
||||
""" Start the plot function on a new thread so the gui still runs """
|
||||
args = ('filename',)
|
||||
thread.start_new_thread(self.start, args)
|
||||
""" Start the plot function on a new thread so the gui still runs """
|
||||
args = ('filename',)
|
||||
thread.start_new_thread(self.start, args)
|
||||
|
||||
"""
|
||||
Send the EBB the up, down, and speed settings for the servo.
|
||||
"""
|
||||
"""
|
||||
Send the EBB the up, down, and speed settings for the servo.
|
||||
"""
|
||||
|
||||
def initializeServo(self):
|
||||
self.serialPort.sendServoUpSetting(self.parent.nPenUpPos)
|
||||
self.serialPort.sendServoDownSetting(self.parent.nPenDownPos)
|
||||
self.serialPort.sendServoSpeedSetting(self.parent.nPenRaiseSpeed)
|
||||
def initializeServo(self):
|
||||
self.serialPort.sendServoUpSetting(self.parent.nPenUpPos)
|
||||
self.serialPort.sendServoDownSetting(self.parent.nPenDownPos)
|
||||
self.serialPort.sendServoSpeedSetting(self.parent.nPenRaiseSpeed)
|
||||
|
||||
""" start the recursive calls to plot the svg file, and update the gui when plot is finished """
|
||||
""" start the recursive calls to plot the svg file, and update the gui when plot is finished """
|
||||
|
||||
def start(self, filen):
|
||||
try:
|
||||
self.penUp(True) # force a pen up at the start
|
||||
""" store home """
|
||||
self.fPrevX = N_EGG_AXIS_STEPS
|
||||
self.fX = 0
|
||||
self.fPrevY = N_PEN_AXIS_STEPS / 2
|
||||
self.ptFirst = (self.fPrevX, self.fPrevY)
|
||||
def start(self, filen):
|
||||
try:
|
||||
self.penUp(True) # force a pen up at the start
|
||||
""" store home """
|
||||
self.fPrevX = N_EGG_AXIS_STEPS
|
||||
self.fX = 0
|
||||
self.fPrevY = N_PEN_AXIS_STEPS / 2
|
||||
self.ptFirst = (self.fPrevX, self.fPrevY)
|
||||
|
||||
self.plotfile(filen)
|
||||
self.serialPort.sendPenUp()
|
||||
self.penUp(True) # force a pen up at the end
|
||||
self.plotfile(filen)
|
||||
self.serialPort.sendPenUp()
|
||||
self.penUp(True) # force a pen up at the end
|
||||
|
||||
if self.parent.bReturnToHome and self.ptFirst is not None:
|
||||
(self.fX, self.fY) = self.ptFirst
|
||||
self.plotLineAndTime() # return to home line
|
||||
if self.parent.bReturnToHome and self.ptFirst is not None:
|
||||
(self.fX, self.fY) = self.ptFirst
|
||||
self.plotLineAndTime() # return to home line
|
||||
|
||||
self.parent.plotHasFinished()
|
||||
self.parent.plotHasFinished()
|
||||
|
||||
except Exception as ex:
|
||||
self.parent.reportError('Exception during plot: ' + str(ex))
|
||||
except Exception as ex:
|
||||
self.parent.reportError('Exception during plot: ' + str(ex))
|
||||
|
||||
"""
|
||||
Plot a simple pen plotter style file containing commands of the form
|
||||
C<pennumber> ;; select pen
|
||||
M<x>,<y> ;; Move to x,y with pen up.
|
||||
D<x>,<y> ;; Move to x,y with pen down.
|
||||
x and y are in steps. EAGLE (and probably other programs) can be
|
||||
coerced into generating this format of file.
|
||||
The main purpose of this program is to convert absolute coordinates
|
||||
to the relative step counts and timing needed by eggbot.
|
||||
"""
|
||||
"""
|
||||
Plot a simple pen plotter style file containing commands of the form
|
||||
C<pennumber> ;; select pen
|
||||
M<x>,<y> ;; Move to x,y with pen up.
|
||||
D<x>,<y> ;; Move to x,y with pen down.
|
||||
x and y are in steps. EAGLE (and probably other programs) can be
|
||||
coerced into generating this format of file.
|
||||
The main purpose of this program is to convert absolute coordinates
|
||||
to the relative step counts and timing needed by eggbot.
|
||||
"""
|
||||
|
||||
def plotfile(self, filen):
|
||||
self.infile = open('/tmp/plot.out', 'r')
|
||||
for line in self.infile:
|
||||
""" check if the user has hit the "stop" button """
|
||||
if self.parent.isStopped():
|
||||
return
|
||||
if self.parent.isPaused():
|
||||
while self.parent.isPaused():
|
||||
time.sleep(.5) # wait half a second before checking again
|
||||
def plotfile(self, filen):
|
||||
self.infile = open('/tmp/plot.out', 'r')
|
||||
for line in self.infile:
|
||||
""" check if the user has hit the "stop" button """
|
||||
if self.parent.isStopped():
|
||||
return
|
||||
if self.parent.isPaused():
|
||||
while self.parent.isPaused():
|
||||
time.sleep(.5) # wait half a second before checking again
|
||||
|
||||
""" make sure the pen is still in the right start before we start """
|
||||
if self.bPenIsUp:
|
||||
self.penUp(True)
|
||||
else:
|
||||
self.penDown(True)
|
||||
""" make sure the pen is still in the right start before we start """
|
||||
if self.bPenIsUp:
|
||||
self.penUp(True)
|
||||
else:
|
||||
self.penDown(True)
|
||||
|
||||
# print line[:-1]
|
||||
if line[0] == 'C':
|
||||
""" pause between layers, but don't pause before the first layer """
|
||||
if self.bStartingFirstLayer:
|
||||
self.bStartingFirstLayer = False
|
||||
else:
|
||||
self.parent.pauseForPenChange(line)
|
||||
# print line[:-1]
|
||||
if line[0] == 'C':
|
||||
""" pause between layers, but don't pause before the first layer """
|
||||
if self.bStartingFirstLayer:
|
||||
self.bStartingFirstLayer = False
|
||||
else:
|
||||
self.parent.pauseForPenChange(line)
|
||||
|
||||
elif line[0] == 'M':
|
||||
self.fX = N_EGG_AXIS_STEPS - int(line.split(',')[0][1:])
|
||||
self.fY = int(line.split(',')[1])
|
||||
# print " Move ", self.fX, self.fY
|
||||
self.penUp()
|
||||
self.plotLineAndTime()
|
||||
self.fPrevX = self.fX
|
||||
self.fPrevY = self.fY
|
||||
elif line[0] == 'D':
|
||||
self.fX = N_EGG_AXIS_STEPS - int(line.split(',')[0][1:])
|
||||
self.fY = int(line.split(',')[1])
|
||||
# print " Draw ", self.fX, self.fY
|
||||
self.penDown()
|
||||
self.plotLineAndTime()
|
||||
self.fPrevX = self.fX
|
||||
self.fPrevY = self.fY
|
||||
else:
|
||||
# Nothing yet
|
||||
pass
|
||||
elif line[0] == 'M':
|
||||
self.fX = N_EGG_AXIS_STEPS - int(line.split(',')[0][1:])
|
||||
self.fY = int(line.split(',')[1])
|
||||
# print " Move ", self.fX, self.fY
|
||||
self.penUp()
|
||||
self.plotLineAndTime()
|
||||
self.fPrevX = self.fX
|
||||
self.fPrevY = self.fY
|
||||
elif line[0] == 'D':
|
||||
self.fX = N_EGG_AXIS_STEPS - int(line.split(',')[0][1:])
|
||||
self.fY = int(line.split(',')[1])
|
||||
# print " Draw ", self.fX, self.fY
|
||||
self.penDown()
|
||||
self.plotLineAndTime()
|
||||
self.fPrevX = self.fX
|
||||
self.fPrevY = self.fY
|
||||
else:
|
||||
# Nothing yet
|
||||
pass
|
||||
|
||||
def penUp(self, bForcePenUp=False):
|
||||
if (not self.bPenIsUp) or bForcePenUp:
|
||||
self.serialPort.sendPenUp()
|
||||
self.bPenIsUp = True
|
||||
self.serialPort.sendPause(self.parent.nPenUpDelay)
|
||||
def penUp(self, bForcePenUp=False):
|
||||
if (not self.bPenIsUp) or bForcePenUp:
|
||||
self.serialPort.sendPenUp()
|
||||
self.bPenIsUp = True
|
||||
self.serialPort.sendPause(self.parent.nPenUpDelay)
|
||||
|
||||
def penDown(self, bForcePenDown=False):
|
||||
if self.bPenIsUp or bForcePenDown:
|
||||
self.serialPort.sendPenDown()
|
||||
self.bPenIsUp = False
|
||||
self.serialPort.sendPause(self.parent.nPenDownDelay)
|
||||
def penDown(self, bForcePenDown=False):
|
||||
if self.bPenIsUp or bForcePenDown:
|
||||
self.serialPort.sendPenDown()
|
||||
self.bPenIsUp = False
|
||||
self.serialPort.sendPause(self.parent.nPenDownDelay)
|
||||
|
||||
"""
|
||||
Send commands out the com port as a line segment (dx, dy) and a time (ms) the segment
|
||||
should take to implement
|
||||
"""
|
||||
"""
|
||||
Send commands out the com port as a line segment (dx, dy) and a time (ms) the segment
|
||||
should take to implement
|
||||
"""
|
||||
|
||||
def plotLineAndTime(self):
|
||||
def plotLineAndTime(self):
|
||||
|
||||
if self.fPrevX is None:
|
||||
return
|
||||
if self.fPrevX is None:
|
||||
return
|
||||
|
||||
if self.bPenIsUp:
|
||||
nSpeed = self.parent.nPenUpSpeed
|
||||
else:
|
||||
nSpeed = self.parent.nPenDownSpeed
|
||||
if self.bPenIsUp:
|
||||
nSpeed = self.parent.nPenUpSpeed
|
||||
else:
|
||||
nSpeed = self.parent.nPenDownSpeed
|
||||
|
||||
nDeltaX = int(self.fX) - int(self.fPrevX)
|
||||
nDeltaY = int(self.fY) - int(self.fPrevY)
|
||||
nDeltaX = int(self.fX) - int(self.fPrevX)
|
||||
nDeltaY = int(self.fY) - int(self.fPrevY)
|
||||
|
||||
nTime = int(round(1000.0 / nSpeed * distance(nDeltaX, nDeltaY)))
|
||||
nTime = int(round(1000.0 / nSpeed * distance(nDeltaX, nDeltaY)))
|
||||
|
||||
self.serialPort.sendMove(nDeltaX, nDeltaY, nTime)
|
||||
self.serialPort.sendMove(nDeltaX, nDeltaY, nTime)
|
||||
|
||||
""" overload this function since we don't need to access the svg document """
|
||||
""" overload this function since we don't need to access the svg document """
|
||||
|
||||
def uniqueId(self, prefix):
|
||||
return "1"
|
||||
def uniqueId(self, prefix):
|
||||
return "1"
|
||||
|
||||
|
||||
"""
|
||||
|
@ -169,4 +169,4 @@ Pythagorean theorem
|
|||
|
||||
|
||||
def distance(x, y):
|
||||
return sqrt(x * x + y * y)
|
||||
return sqrt(x * x + y * y)
|
||||
|
|
|
@ -19,230 +19,230 @@ RUNNING, PAUSED, STOPPED = range(3)
|
|||
|
||||
|
||||
class EggbotGui(wx.Frame):
|
||||
ID_TIMER = 30 # ms
|
||||
ID_TIMER = 30 # ms
|
||||
|
||||
def __init__(self, docRoot):
|
||||
def __init__(self, docRoot):
|
||||
|
||||
wx.Frame.__init__(self, None, wx.ID_ANY, "Eggbot Plotter")
|
||||
wx.Frame.__init__(self, None, wx.ID_ANY, "Eggbot Plotter")
|
||||
|
||||
self.initializeVariables()
|
||||
self.CreateStatusBar()
|
||||
self.menu = EggbotGuiMenu.EggbotGuiMenu(self)
|
||||
self.serialPort = EggbotSerial.EggbotSerial()
|
||||
self.EAGLEPlotter = EggbotPlotEAGLE.EggbotPlotEAGLE(self)
|
||||
self.docRoot = docRoot
|
||||
self.createButtons()
|
||||
self.initializeVariables()
|
||||
self.CreateStatusBar()
|
||||
self.menu = EggbotGuiMenu.EggbotGuiMenu(self)
|
||||
self.serialPort = EggbotSerial.EggbotSerial()
|
||||
self.EAGLEPlotter = EggbotPlotEAGLE.EggbotPlotEAGLE(self)
|
||||
self.docRoot = docRoot
|
||||
self.createButtons()
|
||||
|
||||
guiRect = self.GetScreenRect()
|
||||
guiRect = self.GetScreenRect()
|
||||
|
||||
self.config = EggbotGuiConfiguration.EggbotGuiConfiguration(self)
|
||||
self.config.Move(guiRect.GetTopRight())
|
||||
self.config.Show(True)
|
||||
self.config = EggbotGuiConfiguration.EggbotGuiConfiguration(self)
|
||||
self.config.Move(guiRect.GetTopRight())
|
||||
self.config.Show(True)
|
||||
|
||||
configRect = self.config.GetScreenRect()
|
||||
configRect = self.config.GetScreenRect()
|
||||
|
||||
self.manualControl = EggbotGuiManualControl.EggbotGuiManualControl(self)
|
||||
self.manualControl.Show(True)
|
||||
self.manualControl.Move(configRect.GetBottomLeft())
|
||||
self.manualControl = EggbotGuiManualControl.EggbotGuiManualControl(self)
|
||||
self.manualControl.Show(True)
|
||||
self.manualControl.Move(configRect.GetBottomLeft())
|
||||
|
||||
def createButtons(self):
|
||||
def createButtons(self):
|
||||
|
||||
self.SetAutoLayout(True)
|
||||
self.SetAutoLayout(True)
|
||||
|
||||
panel = wx.Panel(self)
|
||||
vbox = wx.BoxSizer(wx.VERTICAL)
|
||||
panel = wx.Panel(self)
|
||||
vbox = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
if self.serialPort.getCurrentPort() is None:
|
||||
strStatus = "\nNo eggbot connected\n\n"
|
||||
else:
|
||||
strStatus = "\nConnected to " + self.serialPort.getCurrentPort() + "\n"
|
||||
if self.serialPort.getCurrentPort() is None:
|
||||
strStatus = "\nNo eggbot connected\n\n"
|
||||
else:
|
||||
strStatus = "\nConnected to " + self.serialPort.getCurrentPort() + "\n"
|
||||
|
||||
strEbbVersion = self.serialPort.getEggbotVersion()
|
||||
strEbbVersion = self.serialPort.getEggbotVersion()
|
||||
|
||||
if strEbbVersion is None:
|
||||
strEbbVersion = "Eggbot firmware not found"
|
||||
if strEbbVersion is None:
|
||||
strEbbVersion = "Eggbot firmware not found"
|
||||
|
||||
strStatus += strEbbVersion + "\n"
|
||||
strStatus += strEbbVersion + "\n"
|
||||
|
||||
self.messageText = wx.StaticText(panel, -1, strStatus, style=wx.ALIGN_CENTER)
|
||||
vbox.Add(self.messageText, flag=wx.ALIGN_CENTER)
|
||||
self.messageText = wx.StaticText(panel, -1, strStatus, style=wx.ALIGN_CENTER)
|
||||
vbox.Add(self.messageText, flag=wx.ALIGN_CENTER)
|
||||
|
||||
hbox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
hbox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.startPauseButton = wx.Button(panel, ID_STARTPAUSE_BUTTON, 'Start')
|
||||
self.Bind(wx.EVT_BUTTON, self.onStartOrPause, self.startPauseButton)
|
||||
hbox.Add(self.startPauseButton)
|
||||
self.startPauseButton = wx.Button(panel, ID_STARTPAUSE_BUTTON, 'Start')
|
||||
self.Bind(wx.EVT_BUTTON, self.onStartOrPause, self.startPauseButton)
|
||||
hbox.Add(self.startPauseButton)
|
||||
|
||||
self.stopButton = wx.Button(panel, ID_STOP_BUTTON, 'Stop')
|
||||
self.Bind(wx.EVT_BUTTON, self.onStop, self.stopButton)
|
||||
hbox.Add(self.stopButton)
|
||||
self.stopButton = wx.Button(panel, ID_STOP_BUTTON, 'Stop')
|
||||
self.Bind(wx.EVT_BUTTON, self.onStop, self.stopButton)
|
||||
hbox.Add(self.stopButton)
|
||||
|
||||
vbox.Add(hbox, flag=wx.ALIGN_CENTER)
|
||||
vbox.Add(hbox, flag=wx.ALIGN_CENTER)
|
||||
|
||||
panel.SetSizerAndFit(vbox)
|
||||
self.SetClientSize(panel.GetSize())
|
||||
self.Show(True)
|
||||
panel.SetSizerAndFit(vbox)
|
||||
self.SetClientSize(panel.GetSize())
|
||||
self.Show(True)
|
||||
|
||||
def __del__(self):
|
||||
if self.serialPort is not None:
|
||||
self.serialPort.__del__()
|
||||
def __del__(self):
|
||||
if self.serialPort is not None:
|
||||
self.serialPort.__del__()
|
||||
|
||||
def onStartOrPause(self, event):
|
||||
if self.eCurrentState == STOPPED:
|
||||
""" start plot """
|
||||
self.eCurrentState = RUNNING
|
||||
self.startPauseButton.SetLabel('Pause')
|
||||
self.EAGLEPlotter.plot()
|
||||
def onStartOrPause(self, event):
|
||||
if self.eCurrentState == STOPPED:
|
||||
""" start plot """
|
||||
self.eCurrentState = RUNNING
|
||||
self.startPauseButton.SetLabel('Pause')
|
||||
self.EAGLEPlotter.plot()
|
||||
|
||||
elif self.eCurrentState == PAUSED:
|
||||
""" unpause plot """
|
||||
self.eCurrentState = RUNNING
|
||||
self.startPauseButton.SetLabel('Pause')
|
||||
elif self.eCurrentState == PAUSED:
|
||||
""" unpause plot """
|
||||
self.eCurrentState = RUNNING
|
||||
self.startPauseButton.SetLabel('Pause')
|
||||
|
||||
else:
|
||||
""" pause plot """
|
||||
self.eCurrentState = PAUSED
|
||||
self.startPauseButton.SetLabel('Resume')
|
||||
else:
|
||||
""" pause plot """
|
||||
self.eCurrentState = PAUSED
|
||||
self.startPauseButton.SetLabel('Resume')
|
||||
|
||||
def onStop(self, event):
|
||||
if self.eCurrentState == STOPPED:
|
||||
pass
|
||||
else:
|
||||
self.eCurrentState = STOPPED
|
||||
self.startPauseButton.SetLabel('Start')
|
||||
def onStop(self, event):
|
||||
if self.eCurrentState == STOPPED:
|
||||
pass
|
||||
else:
|
||||
self.eCurrentState = STOPPED
|
||||
self.startPauseButton.SetLabel('Start')
|
||||
|
||||
""" reset variables? """
|
||||
""" reset variables? """
|
||||
|
||||
def isStopped(self):
|
||||
return self.eCurrentState == STOPPED
|
||||
def isStopped(self):
|
||||
return self.eCurrentState == STOPPED
|
||||
|
||||
def isPaused(self):
|
||||
return self.eCurrentState == PAUSED
|
||||
def isPaused(self):
|
||||
return self.eCurrentState == PAUSED
|
||||
|
||||
def onTimer(self, event):
|
||||
if event.GetId() == EggbotGui.ID_TIMER:
|
||||
pass
|
||||
else:
|
||||
event.Skip()
|
||||
def onTimer(self, event):
|
||||
if event.GetId() == EggbotGui.ID_TIMER:
|
||||
pass
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
def sendServoInitialization(self):
|
||||
if self.eCurrentState != RUNNING and self.serialPort.isConnected():
|
||||
self.EAGLEPlotter.initializeServo()
|
||||
pass
|
||||
def sendServoInitialization(self):
|
||||
if self.eCurrentState != RUNNING and self.serialPort.isConnected():
|
||||
self.EAGLEPlotter.initializeServo()
|
||||
pass
|
||||
|
||||
def sendManualPenUp(self):
|
||||
if self.eCurrentState != RUNNING and self.serialPort.isConnected():
|
||||
self.serialPort.sendPenUp()
|
||||
def sendManualPenUp(self):
|
||||
if self.eCurrentState != RUNNING and self.serialPort.isConnected():
|
||||
self.serialPort.sendPenUp()
|
||||
|
||||
def sendManualPenDown(self):
|
||||
if self.eCurrentState != RUNNING and self.serialPort.isConnected():
|
||||
self.serialPort.sendPenDown()
|
||||
def sendManualPenDown(self):
|
||||
if self.eCurrentState != RUNNING and self.serialPort.isConnected():
|
||||
self.serialPort.sendPenDown()
|
||||
|
||||
def sendManualMove(self, nXSteps, nYSteps):
|
||||
if self.eCurrentState != RUNNING and self.serialPort.isConnected():
|
||||
self.serialPort.sendMove(nXSteps, nYSteps, 5 * (abs(nXSteps) + abs(nYSteps)))
|
||||
def sendManualMove(self, nXSteps, nYSteps):
|
||||
if self.eCurrentState != RUNNING and self.serialPort.isConnected():
|
||||
self.serialPort.sendMove(nXSteps, nYSteps, 5 * (abs(nXSteps) + abs(nYSteps)))
|
||||
|
||||
def reconnectToEggbot(self):
|
||||
if self.serialPort is not None:
|
||||
self.serialPort.reconnectToEggbot()
|
||||
else:
|
||||
self.serialPort = EggbotSerial.EggbotSerial()
|
||||
def reconnectToEggbot(self):
|
||||
if self.serialPort is not None:
|
||||
self.serialPort.reconnectToEggbot()
|
||||
else:
|
||||
self.serialPort = EggbotSerial.EggbotSerial()
|
||||
|
||||
def initializeVariables(self):
|
||||
self.eCurrentState = STOPPED
|
||||
self.bMotorsEnabled = True
|
||||
def initializeVariables(self):
|
||||
self.eCurrentState = STOPPED
|
||||
self.bMotorsEnabled = True
|
||||
|
||||
""" try to load parameters from the file """
|
||||
if os.path.exists('eggbot.cfg'):
|
||||
try:
|
||||
config = ConfigObj(CONFIG_FILE_NAME)
|
||||
self.nPenDownSpeed = int(config['penDownSpeed'])
|
||||
self.nPenUpSpeed = int(config['penUpSpeed'])
|
||||
self.nPenDownDelay = int(config['penDownDelay'])
|
||||
self.nPenUpDelay = int(config['penUpDelay'])
|
||||
self.nSmoothness = int(config['smoothness'])
|
||||
self.bPauseBetweenLayers = (config['pauseBetweenLayers'] == 'True') # simple boolean cast
|
||||
self.bReturnToHome = (config['returnToHome'] == 'True')
|
||||
self.bCenterPlot = (config['centerPlot'] == 'True')
|
||||
self.nPenUpPos = int(config['penUpPos'])
|
||||
self.nPenDownPos = int(config['penDownPos'])
|
||||
self.nPenRaiseSpeed = int(config['penRaiseSpeed'])
|
||||
return
|
||||
""" try to load parameters from the file """
|
||||
if os.path.exists('eggbot.cfg'):
|
||||
try:
|
||||
config = ConfigObj(CONFIG_FILE_NAME)
|
||||
self.nPenDownSpeed = int(config['penDownSpeed'])
|
||||
self.nPenUpSpeed = int(config['penUpSpeed'])
|
||||
self.nPenDownDelay = int(config['penDownDelay'])
|
||||
self.nPenUpDelay = int(config['penUpDelay'])
|
||||
self.nSmoothness = int(config['smoothness'])
|
||||
self.bPauseBetweenLayers = (config['pauseBetweenLayers'] == 'True') # simple boolean cast
|
||||
self.bReturnToHome = (config['returnToHome'] == 'True')
|
||||
self.bCenterPlot = (config['centerPlot'] == 'True')
|
||||
self.nPenUpPos = int(config['penUpPos'])
|
||||
self.nPenDownPos = int(config['penDownPos'])
|
||||
self.nPenRaiseSpeed = int(config['penRaiseSpeed'])
|
||||
return
|
||||
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
""" if no file exists or it has an error, just use the defaults """
|
||||
self.nPenDownSpeed = 200
|
||||
self.nPenUpSpeed = 400
|
||||
self.nPenDownDelay = 4
|
||||
self.nPenUpDelay = 4
|
||||
self.nSmoothness = 100 # %
|
||||
self.bReturnToHome = True
|
||||
self.bPauseBetweenLayers = True
|
||||
self.bCenterPlot = False
|
||||
self.nPenUpPos = 13500
|
||||
self.nPenDownPos = 11000
|
||||
self.nPenRaiseSpeed = 150
|
||||
""" if no file exists or it has an error, just use the defaults """
|
||||
self.nPenDownSpeed = 200
|
||||
self.nPenUpSpeed = 400
|
||||
self.nPenDownDelay = 4
|
||||
self.nPenUpDelay = 4
|
||||
self.nSmoothness = 100 # %
|
||||
self.bReturnToHome = True
|
||||
self.bPauseBetweenLayers = True
|
||||
self.bCenterPlot = False
|
||||
self.nPenUpPos = 13500
|
||||
self.nPenDownPos = 11000
|
||||
self.nPenRaiseSpeed = 150
|
||||
|
||||
""" store the current settings """
|
||||
""" store the current settings """
|
||||
|
||||
def storeConfiguration(self):
|
||||
config = ConfigObj(CONFIG_FILE_NAME)
|
||||
config['penDownSpeed'] = self.nPenDownSpeed
|
||||
config['penUpSpeed'] = self.nPenUpSpeed
|
||||
config['penDownDelay'] = self.nPenDownDelay
|
||||
config['penUpDelay'] = self.nPenUpDelay
|
||||
config['smoothness'] = self.nSmoothness
|
||||
config['pauseBetweenLayers'] = self.bPauseBetweenLayers
|
||||
config['returnToHome'] = self.bReturnToHome
|
||||
config['centerPlot'] = self.bCenterPlot
|
||||
config['penUpPos'] = self.nPenUpPos
|
||||
config['penDownPos'] = self.nPenDownPos
|
||||
config['penRaiseSpeed'] = self.nPenRaiseSpeed
|
||||
config.write()
|
||||
def storeConfiguration(self):
|
||||
config = ConfigObj(CONFIG_FILE_NAME)
|
||||
config['penDownSpeed'] = self.nPenDownSpeed
|
||||
config['penUpSpeed'] = self.nPenUpSpeed
|
||||
config['penDownDelay'] = self.nPenDownDelay
|
||||
config['penUpDelay'] = self.nPenUpDelay
|
||||
config['smoothness'] = self.nSmoothness
|
||||
config['pauseBetweenLayers'] = self.bPauseBetweenLayers
|
||||
config['returnToHome'] = self.bReturnToHome
|
||||
config['centerPlot'] = self.bCenterPlot
|
||||
config['penUpPos'] = self.nPenUpPos
|
||||
config['penDownPos'] = self.nPenDownPos
|
||||
config['penRaiseSpeed'] = self.nPenRaiseSpeed
|
||||
config.write()
|
||||
|
||||
def pauseForPenChange(self, strLayerName):
|
||||
if strLayerName is None:
|
||||
strLayerName = ''
|
||||
def pauseForPenChange(self, strLayerName):
|
||||
if strLayerName is None:
|
||||
strLayerName = ''
|
||||
|
||||
okDialog = wx.MessageDialog(self, 'About to plot new pen "' + strLayerName + '", would you like ' +
|
||||
'to change pens? Press okay to continue.', '', wx.OK)
|
||||
okDialog.ShowModal()
|
||||
okDialog.Destroy()
|
||||
okDialog = wx.MessageDialog(self, 'About to plot new pen "' + strLayerName + '", would you like ' +
|
||||
'to change pens? Press okay to continue.', '', wx.OK)
|
||||
okDialog.ShowModal()
|
||||
okDialog.Destroy()
|
||||
|
||||
""" end of plot, same as user hitting stop """
|
||||
""" end of plot, same as user hitting stop """
|
||||
|
||||
def plotHasFinished(self):
|
||||
self.onStop(None)
|
||||
def plotHasFinished(self):
|
||||
self.onStop(None)
|
||||
|
||||
def toggleDisableMotors(self):
|
||||
if self.eCurrentState != RUNNING:
|
||||
if self.bMotorsEnabled:
|
||||
self.bMotorsEnabled = False
|
||||
self.serialPort.sendDisableMotors()
|
||||
else:
|
||||
self.bMotorsEnabled = True
|
||||
self.serialPort.sendEnableMotors()
|
||||
def toggleDisableMotors(self):
|
||||
if self.eCurrentState != RUNNING:
|
||||
if self.bMotorsEnabled:
|
||||
self.bMotorsEnabled = False
|
||||
self.serialPort.sendDisableMotors()
|
||||
else:
|
||||
self.bMotorsEnabled = True
|
||||
self.serialPort.sendEnableMotors()
|
||||
|
||||
def reportError(self, strError):
|
||||
errDialog = wx.MessageDialog(self, strError + '\n\nContinue?', 'Error', wx.YES_NO)
|
||||
def reportError(self, strError):
|
||||
errDialog = wx.MessageDialog(self, strError + '\n\nContinue?', 'Error', wx.YES_NO)
|
||||
|
||||
if errDialog.ShowModal() == wx.ID_NO:
|
||||
self.onStop(None)
|
||||
if errDialog.ShowModal() == wx.ID_NO:
|
||||
self.onStop(None)
|
||||
|
||||
errDialog.Destroy()
|
||||
errDialog.Destroy()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
class fakeDoc:
|
||||
def getRoot(self):
|
||||
return []
|
||||
class fakeDoc:
|
||||
def getRoot(self):
|
||||
return []
|
||||
|
||||
|
||||
fakeCaller = fakeDoc()
|
||||
fakeCaller.document = fakeDoc()
|
||||
fakeCaller = fakeDoc()
|
||||
fakeCaller.document = fakeDoc()
|
||||
|
||||
app = wx.PySimpleApp()
|
||||
frame = EggbotGui(fakeCaller)
|
||||
app.MainLoop()
|
||||
app = wx.PySimpleApp()
|
||||
frame = EggbotGui(fakeCaller)
|
||||
app.MainLoop()
|
||||
|
|
Ładowanie…
Reference in New Issue