summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlvin Penner <penner@vaxxine.com>2013-02-13 22:43:28 +0000
committerapenner <penner@vaxxine.com>2013-02-13 22:43:28 +0000
commit438a33572d89375c19bd79aae20399577777501a (patch)
treebe042d154c993bf24963e975e0750d52d3058d88
parentfixing variable type in stream classes to what was intended (preparation for ... (diff)
downloadinkscape-438a33572d89375c19bd79aae20399577777501a.tar.gz
inkscape-438a33572d89375c19bd79aae20399577777501a.zip
extensions. hpgl output. patch by TimeWaster for Bug 1118663
Fixed bugs: - https://launchpad.net/bugs/1118663 (bzr r12124)
-rw-r--r--share/extensions/hpgl_output.inx49
-rwxr-xr-xshare/extensions/hpgl_output.py229
2 files changed, 226 insertions, 52 deletions
diff --git a/share/extensions/hpgl_output.inx b/share/extensions/hpgl_output.inx
index fc65e7070..281de27cc 100644
--- a/share/extensions/hpgl_output.inx
+++ b/share/extensions/hpgl_output.inx
@@ -5,13 +5,48 @@
<dependency type="extension">org.inkscape.output.svg.inkscape</dependency>
<dependency type="executable" location="extensions">hpgl_output.py</dependency>
<dependency type="executable" location="extensions">inkex.py</dependency>
- <param name="flat" type="float" min="0.001" max="1000" _gui-text="hpgl output flatness">0.2</param>
- <param name="mirror" type="boolean" _gui-text="Mirror Y-axis">FALSE</param>
- <param name="xOrigin" type="float" min="-100000" max="100000" _gui-text="X-origin (px)">0.0</param>
- <param name="yOrigin" type="float" min="-100000" max="100000" _gui-text="Y-origin (px)">0.0</param>
- <param name="resolution" type="int" min="90" max="2048" _gui-text="Resolution (dpi)">1016</param>
- <param name="pen" type="int" min="1" max="10" _gui-text="Pen number">1</param>
- <param name="plotInvisibleLayers" type="boolean" _gui-text="Plot invisible layers">FALSE</param>
+ <_param name="introduction" type="description">Please make sure that all objects you want to plot are converted to paths. The plot will automatically be aligned to the zero point.</_param>
+ <param name="horizontal_guide" type="description">—————————————————————————————</param>
+ <param name="resolution" type="int" min="90" max="2048" _gui-text="Resolution (dpi)" _gui-description="The amount of steps the cutter moves if it moves for 1 inch, either get this value from your plotter manual or learn it by trial and error (Standard: '1016')">1016</param>
+ <param name="pen" type="int" min="1" max="10" _gui-text="Pen number" _gui-description="The number of the pen (tool) to use, on most plotters 1 (Standard: '1')">1</param>
+ <param name="angle" type="optiongroup" appearance="minimal" _gui-text="Orientation" _gui-description="Orientation of the plot, change this if your plotter is plotting horizontal instead of vertical (Standard: '-90°')">
+ <option value="-90">-90°</option>
+ <option value="0">0°</option>
+ <option value="90">90°</option>
+ <option value="180">180°</option>
+ </param>
+ <param name="mirror" type="boolean" _gui-text="Mirror Y-axis" _gui-description="Whether to mirror the Y axis. Some plotters need this, some not. Look in your plotter manual or learn it by trial and error (Standard: 'False')">false</param>
+ <param name="flat" type="float" min="0.05" max="10.0" precision="2" _gui-text="Curve flatness (mm)" _gui-description="Curves get divided into lines, this is the approximate length of one line in mm (Standard: '0.50')">0.50</param>
+ <param name="horizontal_guide" type="description">—————————————————————————————</param>
+ <param name="useOvercut" type="boolean" _gui-text="Use Overcut" _gui-description="Whether the overcut will be used, if not the 'Overcut' parameter is unused (Standard: 'True')">true</param>
+ <param name="overcut" type="float" min="0.0" max="100.0" precision="2" _gui-text="Overcut (mm)" _gui-description="The distance in mm that will be cut over the starting point of the path to prevent open paths (Standard: '1.00')">1.00</param>
+ <param name="horizontal_guide" type="description">—————————————————————————————</param>
+ <param name="correctToolOffset" type="boolean" _gui-text="Correct tool offset" _gui-description="Whether the tool offset should be corrected, if not the 'Tool offset' and 'Return Factor' parameters are unused (Standard: 'True')">true</param>
+ <param name="toolOffset" type="float" min="0.0" max="100.0" precision="2" _gui-text="Tool offset (mm)" _gui-description="The offset from the tool tip to the tool axis in mm (Standard: '0.25')">0.25</param>
+ <param name="toolOffsetReturn" type="float" min="1.0" max="10.0" precision="2" _gui-text="Return Factor" _gui-description="The return factor multiplied by the tool offset is the length that is used to guide the tool back to the original path after an overcut is performed, you can only determine this value by experimentation (Standard: '2.50')">2.50</param>
+ <param name="horizontal_guide" type="description">—————————————————————————————</param>
+ <param name="xOffset" type="float" min="0.0" max="10000.0" precision="2" _gui-text="X offset (mm)" _gui-description="The offset to move your plot away from the zero point in mm (Standard: '0.00')">0.00</param>
+ <param name="yOffset" type="float" min="0.0" max="10000.0" precision="2" _gui-text="Y offset (mm)" _gui-description="The offset to move your plot away from the zero point in mm (Standard: '0.00')">0.00</param>
+ <param name="plotInvisibleLayers" type="boolean" _gui-text="Plot invisible layers" _gui-description="Plot invisible layers (Standard: 'False')">false</param>
+ <param name="horizontal_guide" type="description">—————————————————————————————</param>
+ <param name="sendToPlotter" type="boolean" _gui-text="Send to Plotter also" _gui-description="Sends the generated HPGL data also via serial connection to your plotter (Standard: 'False')">false</param>
+ <param name="port" type="string" _gui-text="Serial Port" _gui-description="The port of your serial connection, on Windows something like 'COM1', on Linux something like: '/dev/ttyUSB0' (Standard: 'COM1')">COM1</param>
+ <param name="baudRate" type="optiongroup" appearance="minimal" _gui-text="Baud Rate" _gui-description="The Baud rate of your serial connection (Standard: '9600')">
+ <option value="110">110</option>
+ <option value="300">300</option>
+ <option value="600">600</option>
+ <option value="1200">1200</option>
+ <option value="2400">2400</option>
+ <option value="4800">4800</option>
+ <option value="9600">9600</option>
+ <option value="14400">14400</option>
+ <option value="19200">19200</option>
+ <option value="28800">28800</option>
+ <option value="38400">38400</option>
+ <option value="56000">56000</option>
+ <option value="57600">57600</option>
+ <option value="115200">115200</option>
+ </param>
<output>
<extension>.hpgl</extension>
<mimetype>image/hpgl</mimetype>
diff --git a/share/extensions/hpgl_output.py b/share/extensions/hpgl_output.py
index bd28e3717..e8333ac22 100755
--- a/share/extensions/hpgl_output.py
+++ b/share/extensions/hpgl_output.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
+# coding=utf-8
'''
Copyright (C) 2008 Aaron Spike, aaron@ekips.org
+Overcut, Tool Offset, Rotation, Serial Com., Many Bugfixes and Improvements: Sebastian Wüst, sebi@timewaster.de, http://www.timewasters-place.com/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,27 +18,27 @@ You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
'''
-import inkex, simpletransform, cubicsuperpath, simplestyle, cspsubdiv
+import inkex, simpletransform, cubicsuperpath, simplestyle, cspsubdiv, sys, math
class MyEffect(inkex.Effect):
def __init__(self):
inkex.Effect.__init__(self)
self.OptionParser.add_option("-f", "--flatness",
action="store", type="float",
- dest="flat", default=0.2,
+ dest="flat", default=0.5,
help="Minimum flatness of the subdivided curves")
self.OptionParser.add_option("-m", "--mirror",
action="store", type="inkbool",
dest="mirror", default="FALSE",
help="Mirror Y-Axis")
- self.OptionParser.add_option("-x", "--xOrigin",
+ self.OptionParser.add_option("-x", "--xOffset",
action="store", type="float",
- dest="xOrigin", default=0.0,
- help="X Origin (pixels)")
- self.OptionParser.add_option("-y", "--yOrigin",
+ dest="xOffset", default=0.0,
+ help="X offset (mm)")
+ self.OptionParser.add_option("-y", "--yOffset",
action="store", type="float",
- dest="yOrigin", default=0.0,
- help="Y Origin (pixels)")
+ dest="yOffset", default=0.0,
+ help="Y offset (mm)")
self.OptionParser.add_option("-r", "--resolution",
action="store", type="int",
dest="resolution", default=1016,
@@ -49,29 +51,85 @@ class MyEffect(inkex.Effect):
action="store", type="inkbool",
dest="plotInvisibleLayers", default="FALSE",
help="Plot invisible layers")
-
- def output(self):
- print ''.join(self.hpgl)
-
- def process_path(self, node, mat):
- d = node.get('d')
- if d:
- p = cubicsuperpath.parsePath(d)
- trans = node.get('transform')
- if trans:
- mat = simpletransform.composeTransform(mat, simpletransform.parseTransform(trans))
- simpletransform.applyTransformToPath(mat, p)
- cspsubdiv.cspsubdiv(p, self.options.flat)
- for sp in p:
- first = True
- for csp in sp:
- cmd = 'PD'
- if first:
- cmd = 'PU'
- first = False
- self.hpgl.append('%s%d,%d;' % (cmd,csp[1][0],csp[1][1]))
+ self.OptionParser.add_option("-u", "--useOvercut",
+ action="store", type="inkbool",
+ dest="useOvercut", default="TRUE",
+ help="Use Overcut")
+ self.OptionParser.add_option("-o", "--overcut",
+ action="store", type="float",
+ dest="overcut", default=1.0,
+ help="Overcut (mm)")
+ self.OptionParser.add_option("-c", "--correctToolOffset",
+ action="store", type="inkbool",
+ dest="correctToolOffset", default="TRUE",
+ help="Correct tool offset")
+ self.OptionParser.add_option("-t", "--toolOffset",
+ action="store", type="float",
+ dest="toolOffset", default=0.25,
+ help="Tool offset (mm)")
+ self.OptionParser.add_option("-w", "--toolOffsetReturn",
+ action="store", type="float",
+ dest="toolOffsetReturn", default=2.5,
+ help="Return Factor")
+ self.OptionParser.add_option("-a", "--angle",
+ action="store", type="string",
+ dest="rotation", default="-90",
+ help="Orientation")
+ self.OptionParser.add_option("-s", "--sendToPlotter",
+ action="store", type="inkbool",
+ dest="sendToPlotter", default="FALSE",
+ help="Send to Plotter also")
+ self.OptionParser.add_option("-z", "--port",
+ action="store", type="string",
+ dest="port", default="COM1",
+ help="Serial Port")
+ self.OptionParser.add_option("-b", "--baudRate",
+ action="store", type="string",
+ dest="baudRate", default="9600",
+ help="Baud Rate")
+
+ def effect(self):
+ # initiate vars
+ self.vData = [['', -1.0, -1.0], ['', -1.0, -1.0], ['', -1.0, -1.0], ['', -1.0, -1.0]]
+ self.xDivergence = 999999999999.0
+ self.yDivergence = 999999999999.0
+ scale = float(self.options.resolution) / 90 # dots/inch to dots/pixels
+ x0 = self.options.xOffset * 3.5433070866 * scale # mm to dots
+ y0 = self.options.yOffset * 3.5433070866 * scale # mm to dots
+ self.options.flat *= 3.5433070866 # mm to pixels
+ self.options.overcut = self.options.overcut * 3.5433070866 * scale # mm to dots
+ self.options.toolOffset = self.options.toolOffset * 3.5433070866 * scale # mm to dots
+ doc = self.document.getroot()
+ mirror = 1.0
+ if self.options.mirror:
+ mirror = -1.0
+ # dryRun to find edges
+ self.dryRun = True
+ self.groupmat = [[[scale, 0.0, 0.0], [0.0, mirror*scale, 0.0]]]
+ self.process_group(doc)
+ # life run
+ self.dryRun = False
+ self.groupmat = [[[scale, 0.0, -self.xDivergence + x0], [0.0, mirror*scale, -self.yDivergence + y0]]]
+ self.hpgl = 'IN;SP%d;' % self.options.pen
+ if self.options.sendToPlotter:
+ try:
+ import serial
+ except ImportError, e:
+ inkex.errormsg('pySerial is not installed.\n\n1. Download pySerial here: http://pypi.python.org/pypi/pyserial\n2. Extract the "serial" folder from the zip to the following folder: "C:\Program Files (x86)\inkscape\python\Lib"\n3. Restart Inkscape.')
+ self.options.sendToPlotter = False
+ if self.options.sendToPlotter:
+ self.S = serial.Serial(port=self.options.port, baudrate=self.options.baudRate, timeout=0.01, writeTimeout=None)
+ self.S.write('IN;SP%d;' % self.options.pen)
+ self.process_group(doc)
+ self.calcOffset('PU', 0, 0)
+ self.hpgl += 'PU0,0;'
+ if self.options.sendToPlotter:
+ self.S.write('PU0,0;')
+ self.S.read(2)
+ self.S.close()
def process_group(self, group):
+ # process groups
style = group.get('style')
if style:
style = simplestyle.parseStyle(style)
@@ -90,23 +148,104 @@ class MyEffect(inkex.Effect):
if trans:
self.groupmat.pop()
- def effect(self):
- self.hpgl = ['IN;SP%d;' % self.options.pen]
- x0 = self.options.xOrigin
- y0 = self.options.yOrigin
- scale = float(self.options.resolution)/90
- self.options.flat *= scale
- mirror = 1.0
- if self.options.mirror:
- mirror = -1.0
- if inkex.unittouu(self.document.getroot().xpath('@height', namespaces=inkex.NSS)[0]):
- y0 -= float(inkex.unittouu(self.document.getroot().xpath('@height', namespaces=inkex.NSS)[0]))
- self.groupmat = [[[scale, 0.0, -x0*scale], [0.0, mirror*scale, -y0*scale]]]
- doc = self.document.getroot()
- self.process_group(doc)
- self.hpgl.append('PU;')
+ def process_path(self, node, mat):
+ # process oath
+ d = node.get('d')
+ if d:
+ # transform path
+ p = cubicsuperpath.parsePath(d)
+ trans = node.get('transform')
+ if trans:
+ mat = simpletransform.composeTransform(mat, simpletransform.parseTransform(trans))
+ mat = simpletransform.composeTransform(mat, simpletransform.parseTransform('rotate(' + self.options.rotation + ')'))
+ simpletransform.applyTransformToPath(mat, p)
+ cspsubdiv.cspsubdiv(p, self.options.flat)
+ # break path into HPGL commands
+ xPosOld = -1
+ yPosOld = -1
+ for sp in p:
+ cmd = 'PU'
+ for csp in sp:
+ xPos = csp[1][0]
+ yPos = csp[1][1]
+ if int(xPos) != int(xPosOld) or int(yPos) != int(yPosOld):
+ self.calcOffset(cmd, xPos, yPos)
+ cmd = 'PD'
+ xPosOld = xPos
+ yPosOld = yPos
+ # perform overcut
+ if self.options.useOvercut:
+ if int(xPos) == int(sp[0][1][0]) and int(yPos) == int(sp[0][1][1]):
+ for csp in sp:
+ xPos2 = csp[1][0]
+ yPos2 = csp[1][1]
+ if int(xPos) != int(xPos2) or int(yPos) != int(yPos2):
+ self.calcOffset(cmd, xPos2, yPos2)
+ if self.options.overcut - self.getLength(xPosOld, yPosOld, xPos2, yPos2) <= 0:
+ break
+ xPos = xPos2
+ yPos = yPos2
+
+ def getLength(self, x1, y1, x2, y2, abs = True):
+ # calc absoulute or relative length of two points
+ if abs: return math.fabs(math.sqrt((x2 - x1) ** 2.0 + (y2 - y1) ** 2.0))
+ else: return math.sqrt((x2 - x1) ** 2.0 + (y2 - y1) ** 2.0)
+
+ def calcOffset(self, cmd, xPos, yPos):
+ # calculate offset correction (or dont))
+ if not self.options.correctToolOffset:
+ self.storeData(cmd, xPos, yPos)
+ else:
+ self.vData.pop(0)
+ self.vData.insert(3, [cmd, xPos, yPos])
+ if self.vData[2][1] != -1.0:
+ if self.vData[1][1] != -1.0:
+ if self.vData[2][0] == 'PD':
+ self.storeData('PD',
+ self.changeLengthX(self.vData[1][1], self.vData[1][2], self.vData[2][1], self.vData[2][2], self.options.toolOffset),
+ self.changeLengthY(self.vData[1][1], self.vData[1][2], self.vData[2][1], self.vData[2][2], self.options.toolOffset))
+ elif self.vData[0][1] != -1.0:
+ xTemp = self.vData[1][1] - self.changeLengthX(self.vData[0][1], self.vData[0][2], self.vData[1][1], self.vData[1][2], self.options.toolOffset)
+ yTemp = self.vData[1][2] - self.changeLengthY(self.vData[0][1], self.vData[0][2], self.vData[1][1], self.vData[1][2], self.options.toolOffset)
+ self.storeData('PU', self.vData[2][1] - xTemp, self.vData[2][2] - yTemp)
+ else:
+ self.storeData('PU', self.vData[2][1], self.vData[2][2])
+ if self.vData[3][0] == 'PD':
+ self.storeData('PD',
+ self.changeLengthX(self.vData[3][1], self.vData[3][2], self.vData[2][1], self.vData[2][2], -(self.options.toolOffset * self.options.toolOffsetReturn)),
+ self.changeLengthY(self.vData[3][1], self.vData[3][2], self.vData[2][1], self.vData[2][2], -(self.options.toolOffset * self.options.toolOffsetReturn)))
+ else:
+ self.storeData(self.vData[2][0], self.vData[2][1], self.vData[2][2])
+
+ def storeData(self, command, x, y):
+ if self.dryRun:
+ if x < self.xDivergence: self.xDivergence = x
+ if y < self.yDivergence: self.yDivergence = y
+ else:
+ if x < 0: x = 0 # only positive values are allowed
+ if y < 0: y = 0
+ self.hpgl += '%s%d,%d;' % (command, x, y)
+ if self.options.sendToPlotter:
+ self.S.write('%s%d,%d;' % (command, x, y))
+
+ def changeLengthX(self, x1, y1, x2, y2, offset):
+ # change length between two points - x axis
+ if offset < 0: offset = max(-self.getLength(x1, y1, x2, y2), offset)
+ return x2 + (x2 - x1) / self.getLength(x1, y1, x2, y2, False) * offset;
+
+ def changeLengthY(self, x1, y1, x2, y2, offset):
+ # change length between two points - y axis
+ if offset < 0: offset = max(-self.getLength(x1, y1, x2, y2), offset)
+ return y2 + (y2 - y1) / self.getLength(x1, y1, x2, y2, False) * offset;
+
+ def output(self):
+ # print to file
+ print self.hpgl
-if __name__ == '__main__': #pragma: no cover
+if __name__ == '__main__':
+ # Raise recursion limit to avoid exceptions on big documents
+ sys.setrecursionlimit(20000);
+ # start calculations
e = MyEffect()
e.affect()