2010-09-12 17:54:10 +00:00
|
|
|
# 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
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# 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
|
|
|
|
|
2010-09-14 13:50:23 +00:00
|
|
|
# TODO: Add and honor advisory locking around device open/close for non Win32
|
2010-09-12 17:54:10 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
from bezmisc import *
|
2010-09-12 17:54:10 +00:00
|
|
|
from math import sqrt
|
|
|
|
from simpletransform import *
|
2010-08-09 17:34:06 +00:00
|
|
|
import gettext
|
2010-09-12 17:54:10 +00:00
|
|
|
import simplepath
|
|
|
|
import cspsubdiv
|
2010-09-12 19:46:52 +00:00
|
|
|
import os
|
2010-08-09 17:34:06 +00:00
|
|
|
import serial
|
|
|
|
import string
|
2010-09-12 17:54:10 +00:00
|
|
|
import sys
|
|
|
|
import time
|
2010-09-13 01:33:40 +00:00
|
|
|
import eggbot_scan
|
2010-08-09 17:34:06 +00:00
|
|
|
|
|
|
|
F_DEFAULT_SPEED = 1
|
|
|
|
N_PEN_DOWN_DELAY = 400 # delay (ms) for the pen to go down before the next move
|
|
|
|
N_PEN_UP_DELAY = 400 # delay (ms) for the pen to up down before the next move
|
2010-10-18 18:35:45 +00:00
|
|
|
N_PAGE_HEIGHT = 800 # Default page height (each unit equiv. to one step)
|
|
|
|
N_PAGE_WIDTH = 3200 # Default page width (each unit equiv. to one step)
|
2010-08-09 17:34:06 +00:00
|
|
|
|
|
|
|
N_PEN_UP_POS = 50 # Default pen-up position
|
|
|
|
N_PEN_DOWN_POS = 40 # Default pen-down position
|
|
|
|
N_SERVOSPEED = 50 # Default pen-lift speed
|
2010-09-09 22:24:17 +00:00
|
|
|
N_WALK_DEFAULT = 10 # Default steps for walking stepper motors
|
2010-08-09 17:34:06 +00:00
|
|
|
N_DEFAULT_LAYER = 1 # Default inkscape layer
|
2010-09-12 17:54:10 +00:00
|
|
|
|
|
|
|
# if bDebug = True, create an HPGL file to show what is being plotted.
|
|
|
|
# Pen up moves are shown in a different color if bDrawPenUpLines = True.
|
|
|
|
# Try viewing the .hpgl file in a shareware program or create a simple viewer.
|
|
|
|
|
2010-09-09 22:24:17 +00:00
|
|
|
bDebug = False
|
2010-08-09 17:34:06 +00:00
|
|
|
miscDebug = False
|
|
|
|
bDrawPenUpLines = False
|
|
|
|
bDryRun = False # write the commands to a text file instead of the serial port
|
|
|
|
|
|
|
|
platform = sys.platform.lower()
|
2010-09-12 19:46:52 +00:00
|
|
|
|
|
|
|
HOME = os.getenv( 'HOME' )
|
2010-08-09 17:34:06 +00:00
|
|
|
if platform == 'win32':
|
2010-09-12 19:46:52 +00:00
|
|
|
HOME = os.path.realpath( "C:/" ) # Arguably, this should be %APPDATA% or %TEMP%
|
|
|
|
|
|
|
|
DEBUG_OUTPUT_FILE = os.path.join( HOME, 'test.hpgl' )
|
|
|
|
DRY_RUN_OUTPUT_FILE = os.path.join( HOME, 'dry_run.txt' )
|
|
|
|
MISC_OUTPUT_FILE = os.path.join( HOME, 'misc.txt' )
|
2010-09-12 17:54:10 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
## if platform == 'darwin':
|
2010-09-12 11:05:42 +00:00
|
|
|
## ''' There's no good value for OS X '''
|
|
|
|
## STR_DEFAULT_COM_PORT = '/dev/cu.usbmodem1a21'
|
2010-08-09 17:34:06 +00:00
|
|
|
## elif platform == 'sunos':
|
2010-09-12 11:05:42 +00:00
|
|
|
## ''' Untested: YMMV '''
|
|
|
|
## STR_DEFAULT_COM_PORT = '/dev/term/0'
|
2010-08-09 17:34:06 +00:00
|
|
|
## else:
|
2010-09-12 11:05:42 +00:00
|
|
|
## ''' Works fine on Ubuntu; YMMV '''
|
|
|
|
## STR_DEFAULT_COM_PORT = '/dev/ttyACM0'
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-10-18 18:35:45 +00:00
|
|
|
def parseLengthWithUnits( str ):
|
|
|
|
'''
|
|
|
|
Parse an SVG value which may or may not have units attached
|
|
|
|
This version is greatly simplified in that it only allows: no units,
|
|
|
|
units of px, and units of %. Everything else, it returns None for.
|
|
|
|
There is a more general routine to consider in scour.py if more
|
|
|
|
generality is ever needed.
|
|
|
|
'''
|
|
|
|
u = 'px'
|
|
|
|
s = str.strip()
|
|
|
|
if s[-2:] == 'px':
|
|
|
|
s = s[:-2]
|
|
|
|
elif s[-1:] == '%':
|
|
|
|
u = '%'
|
|
|
|
s = s[:-1]
|
|
|
|
|
|
|
|
try:
|
|
|
|
v = float( s )
|
|
|
|
except:
|
|
|
|
return None, None
|
|
|
|
|
|
|
|
return v, u
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def subdivideCubicPath( sp, flat, i=1 ):
|
2010-09-12 17:54:10 +00:00
|
|
|
"""
|
|
|
|
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]).
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
This is a modified version of cspsubdiv.cspsubdiv(). I rewrote the recursive
|
|
|
|
call because it caused recursion-depth errors on complicated line segments.
|
|
|
|
"""
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
while True:
|
2010-08-09 17:34:06 +00:00
|
|
|
while True:
|
2010-09-12 11:05:42 +00:00
|
|
|
if i >= len( sp ):
|
2010-08-09 17:34:06 +00:00
|
|
|
return
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
p0 = sp[i - 1][1]
|
|
|
|
p1 = sp[i - 1][2]
|
2010-08-09 17:34:06 +00:00
|
|
|
p2 = sp[i][0]
|
|
|
|
p3 = sp[i][1]
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
b = ( p0, p1, p2, p3 )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if cspsubdiv.maxdist( b ) > flat:
|
2010-08-09 17:34:06 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
i += 1
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
one, two = beziersplitatt( b, 0.5 )
|
|
|
|
sp[i - 1][2] = one[1]
|
2010-08-09 17:34:06 +00:00
|
|
|
sp[i][0] = two[2]
|
2010-09-12 11:05:42 +00:00
|
|
|
p = [one[2], one[3], two[1]]
|
2010-08-09 17:34:06 +00:00
|
|
|
sp[i:1] = [p]
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
class EggBot( inkex.Effect ):
|
2010-09-12 17:54:10 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def __init__( self ):
|
|
|
|
inkex.Effect.__init__( self )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
self.OptionParser.add_option( "--smoothness",
|
|
|
|
action="store", type="float",
|
|
|
|
dest="smoothness", default=.2,
|
|
|
|
help="Smoothness of curves" )
|
2010-08-09 17:34:06 +00:00
|
|
|
## self.OptionParser.add_option( "--comPort",
|
2010-09-12 11:05:42 +00:00
|
|
|
## action="store", type="string",
|
|
|
|
## dest="comport", default=STR_DEFAULT_COM_PORT,
|
|
|
|
## help="USB COM port to connect eggbot.")
|
|
|
|
self.OptionParser.add_option( "--startCentered",
|
|
|
|
action="store", type="inkbool",
|
|
|
|
dest="startCentered", default=True,
|
|
|
|
help="Start plot with pen centered in the y-axis." )
|
|
|
|
self.OptionParser.add_option( "--returnToHome",
|
|
|
|
action="store", type="inkbool",
|
|
|
|
dest="returnToHome", default=True,
|
|
|
|
help="Return to home at end of plot." )
|
|
|
|
self.OptionParser.add_option( "--wraparound",
|
|
|
|
action="store", type="inkbool",
|
|
|
|
dest="wraparound", default=True,
|
|
|
|
help="Egg (x) axis wraps around-- take shortcuts!" )
|
|
|
|
self.OptionParser.add_option( "--penUpSpeed",
|
|
|
|
action="store", type="int",
|
|
|
|
dest="penUpSpeed", default=F_DEFAULT_SPEED,
|
|
|
|
help="Speed (step/sec) while pen is up." )
|
|
|
|
self.OptionParser.add_option( "--penDownSpeed",
|
|
|
|
action="store", type="int",
|
|
|
|
dest="penDownSpeed", default=F_DEFAULT_SPEED,
|
|
|
|
help="Speed (step/sec) while pen is down." )
|
|
|
|
self.OptionParser.add_option( "--penDownDelay",
|
|
|
|
action="store", type="int",
|
|
|
|
dest="penDownDelay", default=N_PEN_DOWN_DELAY,
|
|
|
|
help="Delay after pen down (msec)." )
|
|
|
|
self.OptionParser.add_option( "--penUpDelay",
|
|
|
|
action="store", type="int",
|
|
|
|
dest="penUpDelay", default=N_PEN_UP_DELAY,
|
|
|
|
help="Delay after pen up (msec)." )
|
2010-09-17 18:23:21 +00:00
|
|
|
self.OptionParser.add_option( "--engraving",
|
2010-09-12 11:05:42 +00:00
|
|
|
action="store", type="inkbool",
|
2010-09-17 18:23:21 +00:00
|
|
|
dest="engraving", default=False,
|
|
|
|
help="Enable optional engraving tool." )
|
2010-09-12 11:05:42 +00:00
|
|
|
self.OptionParser.add_option( "--tab",
|
|
|
|
action="store", type="string",
|
|
|
|
dest="tab", default="controls",
|
|
|
|
help="The active tab when Apply was pressed" )
|
|
|
|
self.OptionParser.add_option( "--penUpPosition",
|
|
|
|
action="store", type="int",
|
|
|
|
dest="penUpPosition", default=N_PEN_UP_POS,
|
|
|
|
help="Position of pen when lifted" )
|
2010-09-17 18:23:21 +00:00
|
|
|
self.OptionParser.add_option( "--ServoDownSpeed",
|
2010-09-12 11:05:42 +00:00
|
|
|
action="store", type="int",
|
2010-09-17 18:23:21 +00:00
|
|
|
dest="ServoDownSpeed", default=N_SERVOSPEED,
|
|
|
|
help="Rate of lowering pen " )
|
|
|
|
self.OptionParser.add_option( "--ServoUpSpeed",
|
|
|
|
action="store", type="int",
|
|
|
|
dest="ServoUpSpeed", default=N_SERVOSPEED,
|
|
|
|
help="Rate of lifting pen " )
|
2010-09-12 11:05:42 +00:00
|
|
|
self.OptionParser.add_option( "--penDownPosition",
|
|
|
|
action="store", type="int",
|
|
|
|
dest="penDownPosition", default=N_PEN_DOWN_POS,
|
|
|
|
help="Position of pen when lowered" )
|
|
|
|
self.OptionParser.add_option( "--layernumber",
|
|
|
|
action="store", type="int",
|
|
|
|
dest="layernumber", default=N_DEFAULT_LAYER,
|
|
|
|
help="Selected layer for multilayer plotting" )
|
2010-09-17 18:23:21 +00:00
|
|
|
self.OptionParser.add_option( "--setupType",
|
|
|
|
action="store", type="string",
|
|
|
|
dest="setupType", default="controls",
|
|
|
|
help="The active option when Apply was pressed" )
|
2010-09-12 11:05:42 +00:00
|
|
|
self.OptionParser.add_option( "--manualType",
|
|
|
|
action="store", type="string",
|
|
|
|
dest="manualType", default="controls",
|
2010-09-17 18:23:21 +00:00
|
|
|
help="The active option when Apply was pressed" )
|
2010-09-12 11:05:42 +00:00
|
|
|
self.OptionParser.add_option( "--WalkDistance",
|
|
|
|
action="store", type="int",
|
|
|
|
dest="WalkDistance", default=N_WALK_DEFAULT,
|
|
|
|
help="Selected layer for multilayer plotting" )
|
|
|
|
self.OptionParser.add_option( "--cancelOnly",
|
|
|
|
action="store", type="inkbool",
|
|
|
|
dest="cancelOnly", default=False,
|
|
|
|
help="Cancel plot and return home only." )
|
|
|
|
self.OptionParser.add_option( "--revPenMotor",
|
|
|
|
action="store", type="inkbool",
|
|
|
|
dest="revPenMotor", default=False,
|
|
|
|
help="Reverse motion of pen motor." )
|
|
|
|
self.OptionParser.add_option( "--revEggMotor",
|
|
|
|
action="store", type="inkbool",
|
|
|
|
dest="revEggMotor", default=False,
|
|
|
|
help="Reverse motion of egg motor." )
|
2010-09-09 15:52:39 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
self.bPenIsUp = True
|
|
|
|
self.virtualPenIsUp = False #Keeps track of pen postion when stepping through plot before resuming
|
2010-10-21 23:17:27 +00:00
|
|
|
self.engraverIsOn = False
|
|
|
|
self.penDownActivatesEngraver = False
|
2010-08-09 17:34:06 +00:00
|
|
|
self.fX = None
|
|
|
|
self.fY = None
|
|
|
|
self.fPrevX = None
|
|
|
|
self.fPrevY = None
|
|
|
|
self.ptFirst = None
|
|
|
|
self.bStopped = False
|
2010-09-09 22:24:17 +00:00
|
|
|
self.fSpeed = 1
|
2010-08-09 17:34:06 +00:00
|
|
|
self.resumeMode = False
|
2010-09-12 11:05:42 +00:00
|
|
|
self.nodeCount = int( 0 ) #NOTE: python uses 32-bit ints.
|
|
|
|
self.nodeTarget = int( 0 )
|
|
|
|
self.pathcount = int( 0 )
|
2010-08-09 17:34:06 +00:00
|
|
|
self.LayersPlotted = 0
|
|
|
|
self.svgSerialPort = ''
|
2010-09-12 11:05:42 +00:00
|
|
|
self.svgLayer = int( 0 )
|
|
|
|
self.svgNodeCount = int( 0 )
|
2010-08-09 17:34:06 +00:00
|
|
|
self.svgDataRead = False
|
2010-09-12 11:05:42 +00:00
|
|
|
self.svgLastPath = int( 0 )
|
|
|
|
self.svgLastPathNC = int( 0 )
|
|
|
|
self.svgTotalDeltaX = int( 0 )
|
|
|
|
self.svgTotalDeltaY = int( 0 )
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
self.nDeltaX = 0
|
|
|
|
self.nDeltaY = 0
|
2010-09-09 20:08:58 +00:00
|
|
|
|
2010-10-18 18:35:45 +00:00
|
|
|
self.svgWidth = float( N_PAGE_WIDTH )
|
|
|
|
self.svgHeight = float( N_PAGE_HEIGHT )
|
2010-11-28 23:54:40 +00:00
|
|
|
self.svgTransform = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
|
2010-10-18 18:35:45 +00:00
|
|
|
|
2011-05-03 22:40:45 +00:00
|
|
|
# So that we only generate a warning once for each
|
|
|
|
# unsupported SVG element, we use a dictionary to track
|
|
|
|
# which elements have received a warning
|
|
|
|
self.warnings = {}
|
|
|
|
|
2010-09-17 18:23:21 +00:00
|
|
|
# Hack for mismatched EBB/motors,
|
|
|
|
# which have half resolution
|
2010-08-09 17:34:06 +00:00
|
|
|
try:
|
|
|
|
import motor1600
|
2010-09-14 11:47:11 +00:00
|
|
|
self.step_scaling_factor = 2
|
2010-08-09 17:34:06 +00:00
|
|
|
except ImportError:
|
2010-09-14 11:47:11 +00:00
|
|
|
self.step_scaling_factor = 1
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
def effect( self ):
|
|
|
|
'''Main entry point: check to see which tab is selected, and act accordingly.'''
|
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
self.svg = self.document.getroot()
|
|
|
|
self.CheckSVGforEggbotData()
|
|
|
|
|
2010-09-09 22:24:17 +00:00
|
|
|
if self.options.tab == '"splash"':
|
2010-08-09 17:34:06 +00:00
|
|
|
self.allLayers = True
|
|
|
|
self.plotCurrentLayer = True
|
|
|
|
self.EggbotOpenSerial()
|
|
|
|
self.svgNodeCount = 0
|
|
|
|
self.svgLastPath = 0
|
2010-09-12 19:46:52 +00:00
|
|
|
unused_button = self.doRequest( 'QB\r' ) #Query if button pressed
|
2010-08-09 17:34:06 +00:00
|
|
|
self.svgLayer = 12345; # indicate that we are plotting all layers.
|
|
|
|
self.plotToEggBot()
|
|
|
|
|
|
|
|
|
2010-09-09 22:24:17 +00:00
|
|
|
elif self.options.tab == '"resume"':
|
|
|
|
self.EggbotOpenSerial()
|
2010-09-12 19:46:52 +00:00
|
|
|
unused_button = self.doRequest( 'QB\r' ) #Query if button pressed
|
2010-08-09 17:34:06 +00:00
|
|
|
self.resumePlotSetup()
|
|
|
|
if self.resumeMode:
|
|
|
|
self.plotToEggBot()
|
2010-09-14 12:01:03 +00:00
|
|
|
elif ( self.options.cancelOnly ):
|
2010-08-09 17:34:06 +00:00
|
|
|
pass
|
|
|
|
else:
|
2010-09-12 11:05:42 +00:00
|
|
|
inkex.errormsg( gettext.gettext( "Truly sorry, there does not seem to be any in-progress plot to resume." ) )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
|
|
|
elif self.options.tab == '"layers"':
|
2010-09-09 22:24:17 +00:00
|
|
|
self.allLayers = False
|
2010-08-09 17:34:06 +00:00
|
|
|
self.plotCurrentLayer = False
|
|
|
|
self.LayersPlotted = 0
|
|
|
|
self.svgLastPath = 0
|
|
|
|
self.EggbotOpenSerial()
|
2010-09-12 19:46:52 +00:00
|
|
|
unused_button = self.doRequest( 'QB\r' ) #Query if button pressed
|
2010-08-09 17:34:06 +00:00
|
|
|
self.svgNodeCount = 0;
|
|
|
|
self.svgLayer = self.options.layernumber
|
|
|
|
self.plotToEggBot()
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( self.LayersPlotted == 0 ):
|
2010-09-17 18:23:21 +00:00
|
|
|
inkex.errormsg( gettext.gettext( "Truly sorry, but I did not find any numbered layers to plot." ) )
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-17 18:23:21 +00:00
|
|
|
elif self.options.tab == '"setup"':
|
2010-08-09 17:34:06 +00:00
|
|
|
self.EggbotOpenSerial()
|
2010-09-17 18:23:21 +00:00
|
|
|
self.setupCommand()
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-17 18:23:21 +00:00
|
|
|
elif self.options.tab == '"manual"':
|
2010-08-09 17:34:06 +00:00
|
|
|
self.EggbotOpenSerial()
|
2010-09-17 18:23:21 +00:00
|
|
|
self.manualCommand()
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-17 18:23:21 +00:00
|
|
|
## elif self.options.tab == '"timing"':
|
|
|
|
## self.EggbotOpenSerial()
|
|
|
|
## if self.serialPort is not None:
|
|
|
|
## self.ServoSetupWrapper()
|
|
|
|
##
|
|
|
|
## elif self.options.tab == '"options"':
|
|
|
|
## self.EggbotOpenSerial()
|
|
|
|
## if self.serialPort is not None:
|
|
|
|
##
|
2010-08-09 17:34:06 +00:00
|
|
|
self.svgDataRead = False
|
2010-09-12 11:05:42 +00:00
|
|
|
self.UpdateSVGEggbotData( self.svg )
|
2010-09-09 22:24:17 +00:00
|
|
|
self.EggbotCloseSerial()
|
2010-08-09 17:34:06 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def CheckSVGforEggbotData( self ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.svgDataRead = False
|
2010-09-12 11:05:42 +00:00
|
|
|
self.recursiveEggbotDataScan( self.svg )
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( not self.svgDataRead ): #if there is no eggbot data, add some:
|
2010-09-12 11:05:42 +00:00
|
|
|
eggbotlayer = inkex.etree.SubElement( self.svg, 'eggbot' )
|
|
|
|
eggbotlayer.set( 'serialport', '' )
|
|
|
|
eggbotlayer.set( 'layer', str( 0 ) )
|
|
|
|
eggbotlayer.set( 'node', str( 0 ) )
|
|
|
|
eggbotlayer.set( 'lastpath', str( 0 ) )
|
|
|
|
eggbotlayer.set( 'lastpathnc', str( 0 ) )
|
|
|
|
eggbotlayer.set( 'totaldeltax', str( 0 ) )
|
|
|
|
eggbotlayer.set( 'totaldeltay', str( 0 ) )
|
|
|
|
|
|
|
|
def recursiveEggbotDataScan( self, aNodeList ):
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( not self.svgDataRead ):
|
2010-08-09 17:34:06 +00:00
|
|
|
for node in aNodeList:
|
|
|
|
if node.tag == 'svg':
|
2010-09-12 11:05:42 +00:00
|
|
|
self.recursiveEggbotDataScan( node )
|
|
|
|
elif node.tag == inkex.addNS( 'eggbot', 'svg' ) or node.tag == 'eggbot':
|
|
|
|
self.svgSerialPort = node.get( 'serialport' )
|
|
|
|
self.svgLayer = int( node.get( 'layer' ) )
|
|
|
|
self.svgNodeCount = int( node.get( 'node' ) )
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
try:
|
2010-09-12 11:05:42 +00:00
|
|
|
self.svgLastPath = int( node.get( 'lastpath' ) )
|
|
|
|
self.svgLastPathNC = int( node.get( 'lastpathnc' ) )
|
|
|
|
self.svgTotalDeltaX = int( node.get( 'totaldeltax' ) )
|
|
|
|
self.svgTotalDeltaY = int( node.get( 'totaldeltay' ) )
|
2010-08-09 17:34:06 +00:00
|
|
|
self.svgDataRead = True
|
|
|
|
except:
|
2010-09-12 11:05:42 +00:00
|
|
|
node.set( 'lastpath', str( 0 ) )
|
|
|
|
node.set( 'lastpathnc', str( 0 ) )
|
|
|
|
node.set( 'totaldeltax', str( 0 ) )
|
|
|
|
node.set( 'totaldeltay', str( 0 ) )
|
2010-08-09 17:34:06 +00:00
|
|
|
self.svgDataRead = True
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def UpdateSVGEggbotData( self, aNodeList ):
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( not self.svgDataRead ):
|
2010-08-09 17:34:06 +00:00
|
|
|
for node in aNodeList:
|
|
|
|
if node.tag == 'svg':
|
2010-09-12 11:05:42 +00:00
|
|
|
self.UpdateSVGEggbotData( node )
|
|
|
|
elif node.tag == inkex.addNS( 'eggbot', 'svg' ) or node.tag == 'eggbot':
|
|
|
|
node.set( 'serialport', self.svgSerialPort )
|
|
|
|
node.set( 'layer', str( self.svgLayer ) )
|
|
|
|
node.set( 'node', str( self.svgNodeCount ) )
|
|
|
|
node.set( 'lastpath', str( self.svgLastPath ) )
|
|
|
|
node.set( 'lastpathnc', str( self.svgLastPathNC ) )
|
|
|
|
node.set( 'totaldeltax', str( self.svgTotalDeltaX ) )
|
|
|
|
node.set( 'totaldeltay', str( self.svgTotalDeltaY ) )
|
2010-08-09 17:34:06 +00:00
|
|
|
self.svgDataRead = True
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def resumePlotSetup( self ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.LayerFound = False
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( self.svgLayer < 101 ) and ( self.svgLayer >= 0 ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.options.layernumber = self.svgLayer
|
2010-09-09 22:24:17 +00:00
|
|
|
self.allLayers = False
|
2010-08-09 17:34:06 +00:00
|
|
|
self.plotCurrentLayer = False
|
|
|
|
self.LayerFound = True
|
2010-09-12 11:05:42 +00:00
|
|
|
elif ( self.svgLayer == 12345 ): # Plot all layers
|
2010-08-09 17:34:06 +00:00
|
|
|
self.allLayers = True
|
|
|
|
self.plotCurrentLayer = True
|
|
|
|
self.LayerFound = True
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( self.LayerFound ):
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( self.svgNodeCount > 0 ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.nodeTarget = self.svgNodeCount
|
|
|
|
self.resumeMode = True
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( self.options.cancelOnly ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.resumeMode = False
|
|
|
|
self.fPrevX = self.svgTotalDeltaX
|
|
|
|
self.fPrevY = self.svgTotalDeltaY
|
|
|
|
self.fX = 0
|
|
|
|
self.fY = 0
|
|
|
|
self.plotLineAndTime()
|
|
|
|
self.penUp() #Always end with pen-up
|
|
|
|
self.svgLayer = 0
|
|
|
|
self.svgNodeCount = 0
|
|
|
|
self.svgLastPath = 0
|
|
|
|
self.svgLastPathNC = 0
|
|
|
|
self.svgTotalDeltaX = 0
|
|
|
|
self.svgTotalDeltaY = 0
|
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
def manualCommand( self ):
|
|
|
|
"""Execute commands from the "manual" tab"""
|
2010-08-09 17:34:06 +00:00
|
|
|
|
|
|
|
if self.options.manualType == "none":
|
|
|
|
return
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
if self.serialPort is None:
|
|
|
|
return
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-17 18:23:21 +00:00
|
|
|
## self.ServoSetup()
|
2010-10-17 22:15:42 +00:00
|
|
|
#walks are done at pen-down speed.
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
if self.options.manualType == "raise-pen":
|
|
|
|
self.ServoSetupWrapper()
|
|
|
|
self.penUp()
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-13 03:51:26 +00:00
|
|
|
elif self.options.manualType == "align-mode":
|
|
|
|
self.ServoSetupWrapper()
|
|
|
|
self.penUp()
|
|
|
|
self.sendDisableMotors()
|
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
elif self.options.manualType == "lower-pen":
|
|
|
|
self.ServoSetupWrapper()
|
|
|
|
self.penDown()
|
|
|
|
|
|
|
|
elif self.options.manualType == "enable-motors":
|
|
|
|
self.sendEnableMotors()
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
elif self.options.manualType == "disable-motors":
|
|
|
|
self.sendDisableMotors()
|
|
|
|
|
|
|
|
elif self.options.manualType == "version-check":
|
2010-09-12 11:05:42 +00:00
|
|
|
strVersion = self.doRequest( 'v\r' )
|
|
|
|
inkex.errormsg( 'I asked the EBB for its version info, and it replied:\n ' + strVersion )
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-10-21 23:17:27 +00:00
|
|
|
elif self.options.manualType == "enable-engraver":
|
|
|
|
if ( not self.options.engraving ):
|
|
|
|
inkex.errormsg( gettext.gettext( "The engraver option is disabled. " + \
|
|
|
|
" Please enable it first from the \"Options\" tab." ) )
|
|
|
|
else:
|
|
|
|
self.engraverOn()
|
|
|
|
|
|
|
|
elif self.options.manualType == 'disable-engraver':
|
|
|
|
if ( not self.options.engraving ):
|
|
|
|
inkex.errormsg( gettext.gettext( "The engraver option is disabled. " + \
|
|
|
|
" Please enable it first from the \"Options\" tab." ) )
|
|
|
|
else:
|
|
|
|
self.engraverOff()
|
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
else: # self.options.manualType is "walk-egg-motor" or "walk-pen-motor":
|
|
|
|
if self.options.manualType == "walk-egg-motor":
|
|
|
|
self.nDeltaX = self.options.WalkDistance
|
|
|
|
self.nDeltaY = 0
|
|
|
|
elif self.options.manualType == "walk-pen-motor":
|
|
|
|
self.nDeltaY = self.options.WalkDistance
|
2010-09-09 22:24:17 +00:00
|
|
|
self.nDeltaX = 0
|
2010-08-09 17:34:06 +00:00
|
|
|
else:
|
|
|
|
return
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
#Query pen position: 1 up, 0 down (followed by OK)
|
|
|
|
strVersion = self.doRequest( 'QP\r' )
|
2010-08-09 17:34:06 +00:00
|
|
|
if strVersion[0] == '0':
|
|
|
|
#inkex.errormsg('Pen is down' )
|
|
|
|
self.fSpeed = self.options.penDownSpeed
|
|
|
|
if strVersion[0] == '1':
|
|
|
|
#inkex.errormsg('Pen is up' )
|
|
|
|
self.fSpeed = self.options.penUpSpeed
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( self.options.revPenMotor ):
|
2010-09-08 02:03:47 +00:00
|
|
|
self.nDeltaY = -1 * self.nDeltaY
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( self.options.revEggMotor ):
|
2010-09-08 02:03:47 +00:00
|
|
|
self.nDeltaX = -1 * self.nDeltaX
|
2010-09-12 11:05:42 +00:00
|
|
|
self.nTime = int( round( 1000.0 / self.fSpeed * distance( self.nDeltaX, self.nDeltaY ) ) )
|
|
|
|
strOutput = ','.join( ['SM', str( self.nTime ), str( self.nDeltaY ), str( self.nDeltaX )] ) + '\r'
|
|
|
|
self.doCommand( strOutput )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
|
|
|
|
2010-09-17 18:23:21 +00:00
|
|
|
|
|
|
|
def setupCommand( self ):
|
|
|
|
"""Execute commands from the "setup" tab"""
|
|
|
|
|
|
|
|
if self.serialPort is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
self.ServoSetupWrapper()
|
|
|
|
|
|
|
|
if self.options.setupType == "align-mode":
|
|
|
|
self.penUp()
|
|
|
|
self.sendDisableMotors()
|
|
|
|
|
|
|
|
elif self.options.setupType == "toggle-pen":
|
|
|
|
self.doCommand( 'TP\r' ) #Toggle pen
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
def plotToEggBot( self ):
|
|
|
|
'''Perform the actual plotting, if selected in the interface:'''
|
|
|
|
#parse the svg data as a series of line segments and send each segment to be plotted
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
if self.serialPort is None:
|
|
|
|
return
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-10-18 18:35:45 +00:00
|
|
|
if self.options.startCentered and ( not self.getDocProps() ):
|
|
|
|
# Cannot handle the document's dimensions!!!
|
|
|
|
inkex.errormsg( gettext.gettext(
|
|
|
|
'The document to be plotted has invalid dimensions. ' +
|
|
|
|
'The dimensions must be unitless or have units of pixels (px) or ' +
|
|
|
|
'percentages (%). Document dimensions may be set in Inkscape with ' +
|
|
|
|
'File > Document Properties' ) )
|
|
|
|
return
|
|
|
|
|
2010-11-28 23:54:40 +00:00
|
|
|
# Viewbox handling
|
|
|
|
# Also ignores the preserveAspectRatio attribute
|
|
|
|
viewbox = self.svg.get( 'viewBox' )
|
|
|
|
if viewbox:
|
|
|
|
vinfo = viewbox.strip().replace( ',', ' ' ).split( ' ' )
|
|
|
|
if ( vinfo[2] != 0 ) and ( vinfo[3] != 0 ):
|
|
|
|
sx = self.svgWidth / float( vinfo[2] )
|
|
|
|
sy = self.svgHeight / float( vinfo[3] )
|
2011-02-05 16:29:00 +00:00
|
|
|
self.svgTransform = parseTransform( 'scale(%f,%f) translate(%f,%f)' % (sx, sy, -float( vinfo[0] ), -float( vinfo[1] ) ) )
|
2010-11-28 23:54:40 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
self.ServoSetup()
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-10-21 23:17:27 +00:00
|
|
|
# Ensure that the engraver is turned off for the time being
|
|
|
|
# It will be turned back on when the first non-virtual pen-down occurs
|
|
|
|
if self.options.engraving:
|
|
|
|
self.engraverOff()
|
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
if bDebug:
|
2010-09-12 11:05:42 +00:00
|
|
|
self.debugOut = open( DEBUG_OUTPUT_FILE, 'w' )
|
2010-08-09 17:34:06 +00:00
|
|
|
if bDrawPenUpLines:
|
2010-09-12 11:05:42 +00:00
|
|
|
self.debugOut.write( 'IN;SP1;' )
|
2010-08-09 17:34:06 +00:00
|
|
|
else:
|
2010-09-12 11:05:42 +00:00
|
|
|
self.debugOut.write( 'IN;PD;' )
|
2010-09-12 17:54:10 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
# wrap everything in a try so we can for sure close the serial port
|
2010-08-09 17:34:06 +00:00
|
|
|
#self.recursivelyTraverseSvg(self.document.getroot())
|
2010-10-21 23:17:27 +00:00
|
|
|
self.penDownActivatesEngraver = True
|
2010-11-28 23:54:40 +00:00
|
|
|
self.recursivelyTraverseSvg( self.svg, self.svgTransform )
|
2010-08-09 17:34:06 +00:00
|
|
|
self.penUp() #Always end with pen-up
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-10-21 23:17:27 +00:00
|
|
|
# Logically, we want to turn the engraver off here as well,
|
|
|
|
# but we put that in our finally clause instead
|
|
|
|
# self.engraverOff()
|
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
# return to home, if returnToHome = True
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( ( not self.bStopped ) and self.options.returnToHome and ( self.ptFirst ) ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.fX = self.ptFirst[0]
|
|
|
|
self.fY = self.ptFirst[1]
|
|
|
|
#self.penUp()
|
|
|
|
self.nodeCount = self.nodeTarget # enablesfpx return-to-home only option
|
|
|
|
self.plotLineAndTime()
|
|
|
|
#inkex.errormsg('Final node count: ' + str(self.svgNodeCount)) #Node Count - Debug option
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( not self.bStopped ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.svgLayer = 0
|
|
|
|
self.svgNodeCount = 0
|
|
|
|
self.svgLastPath = 0
|
|
|
|
self.svgLastPathNC = 0
|
|
|
|
self.svgTotalDeltaX = 0
|
|
|
|
self.svgTotalDeltaY = 0
|
|
|
|
|
|
|
|
finally:
|
2010-10-21 23:17:27 +00:00
|
|
|
# We may have had an exception and lost the serial port...
|
|
|
|
self.penDownActivatesEngraver = False
|
|
|
|
if ( not ( self.serialPort is None ) ) and ( self.options.engraving ):
|
|
|
|
self.engraverOff()
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
def recursivelyTraverseSvg( self, aNodeList,
|
|
|
|
matCurrent=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
|
|
|
|
parent_visibility='visible' ):
|
|
|
|
"""
|
|
|
|
Recursively traverse the svg file to plot out all of the
|
|
|
|
paths. The function keeps track of the composite transformation
|
|
|
|
that should be applied to each path.
|
|
|
|
|
|
|
|
This function handles path, group, line, rect, polyline, polygon,
|
2010-09-13 19:31:41 +00:00
|
|
|
circle, ellipse and use (clone) elements. Notable elements not
|
|
|
|
handled include text. Unhandled elements should be converted to
|
|
|
|
paths in Inkscape.
|
2010-09-12 17:54:10 +00:00
|
|
|
"""
|
2010-08-09 17:34:06 +00:00
|
|
|
for node in aNodeList:
|
2010-09-12 17:54:10 +00:00
|
|
|
# Ignore invisible nodes
|
2010-09-12 11:05:42 +00:00
|
|
|
v = node.get( 'visibility', parent_visibility )
|
2010-09-08 02:03:47 +00:00
|
|
|
if v == 'inherit':
|
|
|
|
v = parent_visibility
|
|
|
|
if v == 'hidden' or v == 'collapse':
|
|
|
|
pass
|
2010-08-29 18:56:40 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
# first apply the current matrix transform to this node's tranform
|
2010-09-12 11:05:42 +00:00
|
|
|
matNew = composeTransform( matCurrent, parseTransform( node.get( "transform" ) ) )
|
2010-09-13 19:31:41 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if node.tag == inkex.addNS( 'g', 'svg' ) or node.tag == 'g':
|
2010-09-13 19:31:41 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
self.penUp()
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( node.get( inkex.addNS( 'groupmode', 'inkscape' ) ) == 'layer' ):
|
2010-09-14 12:01:03 +00:00
|
|
|
if not self.allLayers:
|
2010-08-09 17:34:06 +00:00
|
|
|
#inkex.errormsg('Plotting layer named: ' + node.get(inkex.addNS('label', 'inkscape')))
|
2010-09-12 11:05:42 +00:00
|
|
|
self.DoWePlotLayer( node.get( inkex.addNS( 'label', 'inkscape' ) ) )
|
|
|
|
self.recursivelyTraverseSvg( node, matNew, parent_visibility=v )
|
2010-09-13 19:31:41 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
elif node.tag == inkex.addNS( 'use', 'svg' ) or node.tag == 'use':
|
2010-09-13 19:31:41 +00:00
|
|
|
|
|
|
|
# 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".
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
refid = node.get( inkex.addNS( 'href', 'xlink' ) )
|
2010-09-08 02:03:47 +00:00
|
|
|
if refid:
|
|
|
|
# [1:] to ignore leading '#' in reference
|
|
|
|
path = '//*[@id="%s"]' % refid[1:]
|
2010-09-12 11:05:42 +00:00
|
|
|
refnode = node.xpath( path )
|
2010-09-08 02:03:47 +00:00
|
|
|
if refnode:
|
2010-09-12 11:05:42 +00:00
|
|
|
x = float( node.get( 'x', '0' ) )
|
|
|
|
y = float( node.get( 'y', '0' ) )
|
2010-11-27 00:36:52 +00:00
|
|
|
# Note: the transform has already been applied
|
|
|
|
if ( x != 0 ) or (y != 0 ):
|
|
|
|
matNew2 = composeTransform( matNew, parseTransform( 'translate(%f,%f)' % (x,y) ) )
|
2010-09-08 02:03:47 +00:00
|
|
|
else:
|
2010-11-27 00:36:52 +00:00
|
|
|
matNew2 = matNew
|
2010-09-12 11:05:42 +00:00
|
|
|
v = node.get( 'visibility', v )
|
|
|
|
self.recursivelyTraverseSvg( refnode, matNew2, parent_visibility=v )
|
2010-09-08 02:03:47 +00:00
|
|
|
else:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
pass
|
2010-09-13 19:31:41 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
elif node.tag == inkex.addNS( 'path', 'svg' ):
|
2010-09-13 19:31:41 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
self.pathcount += 1
|
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
# if we're in resume mode AND self.pathcount < self.svgLastPath,
|
|
|
|
# then skip over this path.
|
|
|
|
# if we're in resume mode and self.pathcount = self.svgLastPath,
|
|
|
|
# then start here, and set
|
|
|
|
# self.nodeCount equal to self.svgLastPathNC
|
2010-09-12 11:05:42 +00:00
|
|
|
if self.resumeMode and ( self.pathcount == self.svgLastPath ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.nodeCount = self.svgLastPathNC
|
2010-09-12 11:05:42 +00:00
|
|
|
if self.resumeMode and ( self.pathcount < self.svgLastPath ):
|
2010-08-09 17:34:06 +00:00
|
|
|
pass
|
2010-09-09 22:24:17 +00:00
|
|
|
else:
|
2010-09-12 11:05:42 +00:00
|
|
|
self.plotPath( node, matNew )
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( not self.bStopped ): #an "index" for resuming plots quickly-- record last complete path
|
2010-08-09 17:34:06 +00:00
|
|
|
self.svgLastPath += 1
|
|
|
|
self.svgLastPathNC = self.nodeCount
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
elif node.tag == inkex.addNS( 'rect', 'svg' ) or node.tag == 'rect':
|
2010-08-09 22:50:13 +00:00
|
|
|
|
2010-09-13 19:31:41 +00:00
|
|
|
# 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
|
|
|
|
|
2010-08-09 22:50:13 +00:00
|
|
|
self.pathcount += 1
|
2010-09-12 17:54:10 +00:00
|
|
|
# if we're in resume mode AND self.pathcount < self.svgLastPath,
|
|
|
|
# then skip over this path.
|
|
|
|
# if we're in resume mode and self.pathcount = self.svgLastPath,
|
|
|
|
# then start here, and set
|
2010-08-09 22:50:13 +00:00
|
|
|
# self.nodeCount equal to self.svgLastPathNC
|
2010-09-12 11:05:42 +00:00
|
|
|
if self.resumeMode and ( self.pathcount == self.svgLastPath ):
|
2010-08-09 22:50:13 +00:00
|
|
|
self.nodeCount = self.svgLastPathNC
|
2010-09-12 11:05:42 +00:00
|
|
|
if self.resumeMode and ( self.pathcount < self.svgLastPath ):
|
2010-08-09 22:50:13 +00:00
|
|
|
pass
|
2010-09-09 22:24:17 +00:00
|
|
|
else:
|
2010-09-08 02:03:47 +00:00
|
|
|
# Create a path with the outline of the rectangle
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath = inkex.etree.Element( inkex.addNS( 'path', 'svg' ) )
|
|
|
|
x = float( node.get( 'x' ) )
|
|
|
|
y = float( node.get( 'y' ) )
|
|
|
|
w = float( node.get( 'width' ) )
|
|
|
|
h = float( node.get( 'height' ) )
|
|
|
|
s = node.get( 'style' )
|
2010-09-08 02:03:47 +00:00
|
|
|
if s:
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath.set( 'style', s )
|
|
|
|
t = node.get( 'transform' )
|
2010-09-08 02:03:47 +00:00
|
|
|
if t:
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath.set( 'transform', t )
|
2010-09-08 02:03:47 +00:00
|
|
|
a = []
|
2010-09-12 11:05:42 +00:00
|
|
|
a.append( ['M ', [x, y]] )
|
|
|
|
a.append( [' l ', [w, 0]] )
|
|
|
|
a.append( [' l ', [0, h]] )
|
|
|
|
a.append( [' l ', [-w, 0]] )
|
|
|
|
a.append( [' Z', []] )
|
|
|
|
newpath.set( 'd', simplepath.formatPath( a ) )
|
|
|
|
self.plotPath( newpath, matNew )
|
2010-09-08 02:03:47 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
elif node.tag == inkex.addNS( 'line', 'svg' ) or node.tag == 'line':
|
2010-08-09 22:50:13 +00:00
|
|
|
|
2010-09-13 19:31:41 +00:00
|
|
|
# Convert
|
|
|
|
#
|
|
|
|
# <line x1="X1" y1="Y1" x2="X2" y2="Y2/>
|
|
|
|
#
|
|
|
|
# to
|
|
|
|
#
|
|
|
|
# <path d="MX1,Y1 LX2,Y2"/>
|
|
|
|
|
2010-08-09 22:50:13 +00:00
|
|
|
self.pathcount += 1
|
2010-09-12 17:54:10 +00:00
|
|
|
# if we're in resume mode AND self.pathcount < self.svgLastPath,
|
|
|
|
# then skip over this path.
|
|
|
|
# if we're in resume mode and self.pathcount = self.svgLastPath,
|
|
|
|
# then start here, and set
|
2010-08-09 22:50:13 +00:00
|
|
|
# self.nodeCount equal to self.svgLastPathNC
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if self.resumeMode and ( self.pathcount == self.svgLastPath ):
|
2010-08-09 22:50:13 +00:00
|
|
|
self.nodeCount = self.svgLastPathNC
|
2010-09-12 11:05:42 +00:00
|
|
|
if self.resumeMode and ( self.pathcount < self.svgLastPath ):
|
2010-08-09 22:50:13 +00:00
|
|
|
pass
|
|
|
|
else:
|
2010-09-08 02:03:47 +00:00
|
|
|
# Create a path to contain the line
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath = inkex.etree.Element( inkex.addNS( 'path', 'svg' ) )
|
|
|
|
x1 = float( node.get( 'x1' ) )
|
|
|
|
y1 = float( node.get( 'y1' ) )
|
|
|
|
x2 = float( node.get( 'x2' ) )
|
|
|
|
y2 = float( node.get( 'y2' ) )
|
|
|
|
s = node.get( 'style' )
|
2010-09-08 02:03:47 +00:00
|
|
|
if s:
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath.set( 'style', s )
|
|
|
|
t = node.get( 'transform' )
|
2010-09-08 02:03:47 +00:00
|
|
|
if t:
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath.set( 'transform', t )
|
2010-09-08 02:03:47 +00:00
|
|
|
a = []
|
2010-09-12 11:05:42 +00:00
|
|
|
a.append( ['M ', [x1, y1]] )
|
|
|
|
a.append( [' L ', [x2, y2]] )
|
|
|
|
newpath.set( 'd', simplepath.formatPath( a ) )
|
|
|
|
self.plotPath( newpath, matNew )
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( not self.bStopped ): #an "index" for resuming plots quickly-- record last complete path
|
2010-09-08 02:03:47 +00:00
|
|
|
self.svgLastPath += 1
|
|
|
|
self.svgLastPathNC = self.nodeCount
|
2010-08-09 22:50:13 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
elif node.tag == inkex.addNS( 'polyline', 'svg' ) or node.tag == 'polyline':
|
2010-09-13 19:31:41 +00:00
|
|
|
|
|
|
|
# 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
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
pl = node.get( 'points', '' ).strip()
|
2010-09-08 02:03:47 +00:00
|
|
|
if pl == '':
|
|
|
|
pass
|
2010-08-09 22:50:13 +00:00
|
|
|
|
2010-09-08 02:03:47 +00:00
|
|
|
self.pathcount += 1
|
|
|
|
#if we're in resume mode AND self.pathcount < self.svgLastPath, then skip over this path.
|
|
|
|
#if we're in resume mode and self.pathcount = self.svgLastPath, then start here, and set
|
|
|
|
# self.nodeCount equal to self.svgLastPathNC
|
2010-08-09 22:50:13 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if self.resumeMode and ( self.pathcount == self.svgLastPath ):
|
2010-09-08 02:03:47 +00:00
|
|
|
self.nodeCount = self.svgLastPathNC
|
2010-08-10 23:41:28 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if self.resumeMode and ( self.pathcount < self.svgLastPath ):
|
2010-09-08 02:03:47 +00:00
|
|
|
pass
|
2010-08-09 22:50:13 +00:00
|
|
|
|
2010-09-08 02:03:47 +00:00
|
|
|
else:
|
|
|
|
pa = pl.split()
|
2010-10-18 19:23:54 +00:00
|
|
|
if not len( pa ):
|
|
|
|
pass
|
|
|
|
# Issue 29: pre 2.5.? versions of Python do not have
|
|
|
|
# "statement-1 if expression-1 else statement-2"
|
|
|
|
# which came out of PEP 308, Conditional Expressions
|
|
|
|
#d = "".join( ["M " + pa[i] if i == 0 else " L " + pa[i] for i in range( 0, len( pa ) )] )
|
|
|
|
d = "M " + pa[0]
|
|
|
|
for i in range( 1, len( pa ) ):
|
|
|
|
d += " L " + pa[i]
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath = inkex.etree.Element( inkex.addNS( 'path', 'svg' ) )
|
|
|
|
newpath.set( 'd', d );
|
|
|
|
s = node.get( 'style' )
|
2010-09-08 02:03:47 +00:00
|
|
|
if s:
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath.set( 'style', s )
|
|
|
|
t = node.get( 'transform' )
|
2010-09-08 02:03:47 +00:00
|
|
|
if t:
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath.set( 'transform', t )
|
|
|
|
self.plotPath( newpath, matNew )
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( not self.bStopped ): #an "index" for resuming plots quickly-- record last complete path
|
2010-09-08 02:03:47 +00:00
|
|
|
self.svgLastPath += 1
|
|
|
|
self.svgLastPathNC = self.nodeCount
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
elif node.tag == inkex.addNS( 'polygon', 'svg' ) or node.tag == 'polygon':
|
2010-09-08 02:03:47 +00:00
|
|
|
|
2010-09-13 19:31:41 +00:00
|
|
|
# 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
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
pl = node.get( 'points', '' ).strip()
|
2010-09-08 02:03:47 +00:00
|
|
|
if pl == '':
|
|
|
|
pass
|
2010-08-10 23:41:28 +00:00
|
|
|
|
2010-08-10 23:22:41 +00:00
|
|
|
self.pathcount += 1
|
|
|
|
#if we're in resume mode AND self.pathcount < self.svgLastPath, then skip over this path.
|
|
|
|
#if we're in resume mode and self.pathcount = self.svgLastPath, then start here, and set
|
|
|
|
# self.nodeCount equal to self.svgLastPathNC
|
2010-09-08 02:03:47 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if self.resumeMode and ( self.pathcount == self.svgLastPath ):
|
2010-08-10 23:22:41 +00:00
|
|
|
self.nodeCount = self.svgLastPathNC
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if self.resumeMode and ( self.pathcount < self.svgLastPath ):
|
2010-08-10 23:22:41 +00:00
|
|
|
pass
|
|
|
|
|
2010-09-08 02:03:47 +00:00
|
|
|
else:
|
|
|
|
pa = pl.split()
|
2010-10-18 19:23:54 +00:00
|
|
|
if not len( pa ):
|
|
|
|
pass
|
|
|
|
# Issue 29: pre 2.5.? versions of Python do not have
|
|
|
|
# "statement-1 if expression-1 else statement-2"
|
|
|
|
# which came out of PEP 308, Conditional Expressions
|
|
|
|
#d = "".join( ["M " + pa[i] if i == 0 else " L " + pa[i] for i in range( 0, len( pa ) )] )
|
|
|
|
d = "M " + pa[0]
|
|
|
|
for i in range( 1, len( pa ) ):
|
|
|
|
d += " L " + pa[i]
|
2010-09-08 02:03:47 +00:00
|
|
|
d += " Z"
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath = inkex.etree.Element( inkex.addNS( 'path', 'svg' ) )
|
|
|
|
newpath.set( 'd', d );
|
|
|
|
s = node.get( 'style' )
|
2010-09-08 02:03:47 +00:00
|
|
|
if s:
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath.set( 'style', s )
|
|
|
|
t = node.get( 'transform' )
|
2010-09-08 02:03:47 +00:00
|
|
|
if t:
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath.set( 'transform', t )
|
|
|
|
self.plotPath( newpath, matNew )
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( not self.bStopped ): #an "index" for resuming plots quickly-- record last complete path
|
2010-09-08 02:03:47 +00:00
|
|
|
self.svgLastPath += 1
|
|
|
|
self.svgLastPathNC = self.nodeCount
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
elif node.tag == inkex.addNS( 'ellipse', 'svg' ) or \
|
|
|
|
node.tag == 'ellipse' or \
|
|
|
|
node.tag == inkex.addNS( 'circle', 'svg' ) or \
|
|
|
|
node.tag == 'circle':
|
2010-09-08 02:03:47 +00:00
|
|
|
|
2010-09-13 19:31:41 +00:00
|
|
|
# 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
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if node.tag == inkex.addNS( 'ellipse', 'svg' ) or node.tag == 'ellipse':
|
|
|
|
rx = float( node.get( 'rx', '0' ) )
|
|
|
|
ry = float( node.get( 'ry', '0' ) )
|
2010-09-08 02:03:47 +00:00
|
|
|
else:
|
2010-09-12 11:05:42 +00:00
|
|
|
rx = float( node.get( 'r', '0' ) )
|
2010-09-08 02:03:47 +00:00
|
|
|
ry = rx
|
|
|
|
if rx == 0 or ry == 0:
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.pathcount += 1
|
|
|
|
#if we're in resume mode AND self.pathcount < self.svgLastPath, then skip over this path.
|
|
|
|
#if we're in resume mode and self.pathcount = self.svgLastPath, then start here, and set
|
|
|
|
# self.nodeCount equal to self.svgLastPathNC
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if self.resumeMode and ( self.pathcount == self.svgLastPath ):
|
2010-09-08 02:03:47 +00:00
|
|
|
self.nodeCount = self.svgLastPathNC
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if self.resumeMode and ( self.pathcount < self.svgLastPath ):
|
2010-09-08 02:03:47 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
else:
|
2010-09-12 11:05:42 +00:00
|
|
|
cx = float( node.get( 'cx', '0' ) )
|
|
|
|
cy = float( node.get( 'cy', '0' ) )
|
2010-09-08 02:03:47 +00:00
|
|
|
x1 = cx - rx
|
|
|
|
x2 = cx + rx
|
2010-09-12 11:05:42 +00:00
|
|
|
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 )
|
|
|
|
newpath = inkex.etree.Element( inkex.addNS( 'path', 'svg' ) )
|
|
|
|
newpath.set( 'd', d );
|
|
|
|
s = node.get( 'style' )
|
2010-09-08 02:03:47 +00:00
|
|
|
if s:
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath.set( 'style', s )
|
|
|
|
t = node.get( 'transform' )
|
2010-09-08 02:03:47 +00:00
|
|
|
if t:
|
2010-09-12 11:05:42 +00:00
|
|
|
newpath.set( 'transform', t )
|
|
|
|
self.plotPath( newpath, matNew )
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( not self.bStopped ): #an "index" for resuming plots quickly-- record last complete path
|
2010-09-08 02:03:47 +00:00
|
|
|
self.svgLastPath += 1
|
|
|
|
self.svgLastPathNC = self.nodeCount
|
2010-09-12 11:05:42 +00:00
|
|
|
elif node.tag == inkex.addNS( 'metadata', 'svg' ) or node.tag == 'metadata':
|
2010-08-09 17:34:06 +00:00
|
|
|
pass
|
2010-09-12 11:05:42 +00:00
|
|
|
elif node.tag == inkex.addNS( 'defs', 'svg' ) or node.tag == 'defs':
|
2010-08-09 17:34:06 +00:00
|
|
|
pass
|
2010-09-12 11:05:42 +00:00
|
|
|
elif node.tag == inkex.addNS( 'namedview', 'sodipodi' ) or node.tag == 'namedview':
|
2010-08-09 17:34:06 +00:00
|
|
|
pass
|
2010-09-12 11:05:42 +00:00
|
|
|
elif node.tag == inkex.addNS( 'eggbot', 'svg' ) or node.tag == 'eggbot':
|
2010-08-09 17:34:06 +00:00
|
|
|
pass
|
2011-04-28 22:48:39 +00:00
|
|
|
elif node.tag == inkex.addNS( 'title', 'svg' ) or node.tag == 'title':
|
|
|
|
pass
|
2011-05-03 22:40:45 +00:00
|
|
|
elif node.tag == inkex.addNS( 'desc', 'svg' ) or node.tag == 'desc':
|
|
|
|
pass
|
2010-09-12 11:05:42 +00:00
|
|
|
elif node.tag == inkex.addNS( 'text', 'svg' ) or node.tag == 'text':
|
2011-05-03 22:40:45 +00:00
|
|
|
if not self.warnings.has_key( 'text' ):
|
|
|
|
inkex.errormsg( gettext.gettext( 'Warning: unable to draw text; ' +
|
|
|
|
'please convert it to a path first. Consider using the ' +
|
|
|
|
'Hershey Text extension which is located under the '+
|
|
|
|
'"Render" category of extensions.' ) )
|
|
|
|
self.warnings['text'] = 1
|
|
|
|
pass
|
|
|
|
elif node.tag == inkex.addNS( 'image', 'svg' ) or node.tag == 'image':
|
|
|
|
if not self.warnings.has_key( 'image' ):
|
|
|
|
inkex.errormsg( gettext.gettext( 'Warning: unable to draw bitmap images; ' +
|
|
|
|
'please convert them to line art first. Consider using the "Trace bitmap..." ' +
|
|
|
|
'tool of the "Path" menu. Mac users please note that some X11 settings may ' +
|
|
|
|
'cause cut-and-paste operations to paste in bitmap copies.' ) )
|
|
|
|
self.warnings['image'] = 1
|
|
|
|
pass
|
|
|
|
elif node.tag == inkex.addNS( 'pattern', 'svg' ) or node.tag == 'pattern':
|
|
|
|
pass
|
|
|
|
elif node.tag == inkex.addNS( 'radialGradient', 'svg' ) or node.tag == 'radialGradient':
|
|
|
|
# Similar to pattern
|
|
|
|
pass
|
|
|
|
elif node.tag == inkex.addNS( 'linearGradient', 'svg' ) or node.tag == 'linearGradient':
|
|
|
|
# Similar in pattern
|
|
|
|
pass
|
|
|
|
elif node.tag == inkex.addNS( 'style', 'svg' ) or node.tag == 'style':
|
|
|
|
# This is a reference to an external style sheet and not the value
|
|
|
|
# of a style attribute to be inherited by child elements
|
|
|
|
pass
|
|
|
|
elif node.tag == inkex.addNS( 'cursor', 'svg' ) or node.tag == 'cursor':
|
|
|
|
pass
|
|
|
|
elif node.tag == inkex.addNS( 'color-profile', 'svg' ) or node.tag == 'color-profile':
|
|
|
|
# Gamma curves, color temp, etc. are not relevant to single color output
|
2010-08-09 17:34:06 +00:00
|
|
|
pass
|
2010-10-17 22:15:42 +00:00
|
|
|
elif not isinstance( node.tag, basestring ):
|
2011-05-03 22:40:45 +00:00
|
|
|
# This is likely an XML processing instruction such as an XML
|
|
|
|
# comment. lxml uses a function reference for such node tags
|
|
|
|
# and as such the node tag is likely not a printable string.
|
|
|
|
# Further, converting it to a printable string likely won't
|
|
|
|
# be very useful.
|
2010-10-17 22:15:42 +00:00
|
|
|
pass
|
2010-08-09 17:34:06 +00:00
|
|
|
else:
|
2011-05-03 22:40:45 +00:00
|
|
|
if not self.warnings.has_key( str( node.tag ) ):
|
|
|
|
t = str( node.tag ).split( '}' )
|
|
|
|
inkex.errormsg( gettext.gettext( 'Warning: unable to draw <' + str( t[-1] ) +
|
|
|
|
'> object, please convert it to a path first.' ) )
|
|
|
|
self.warnings[str( node.tag )] = 1
|
2010-08-09 17:34:06 +00:00
|
|
|
pass
|
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
def DoWePlotLayer( self, strLayerName ):
|
|
|
|
"""
|
|
|
|
We are only plotting *some* layers. Check to see
|
|
|
|
whether or not we're going to plot this one.
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
First: scan first 4 chars of node id for first non-numeric character,
|
|
|
|
and scan the part before that (if any) into a number
|
|
|
|
|
|
|
|
Then, see if the number matches the layer.
|
|
|
|
"""
|
2010-08-09 17:34:06 +00:00
|
|
|
|
|
|
|
TempNumString = 'x'
|
|
|
|
stringPos = 1
|
2010-09-12 11:05:42 +00:00
|
|
|
CurrentLayerName = string.lstrip( strLayerName ) #remove leading whitespace
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
# Look at layer name. Sample first character, then first two, and
|
2010-08-09 17:34:06 +00:00
|
|
|
# so on, until the string ends or the string no longer consists of
|
|
|
|
# digit characters only.
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
MaxLength = len( CurrentLayerName )
|
2010-08-09 17:34:06 +00:00
|
|
|
if MaxLength > 0:
|
|
|
|
while stringPos <= MaxLength:
|
2010-09-12 11:05:42 +00:00
|
|
|
if str.isdigit( CurrentLayerName[:stringPos] ):
|
2010-08-09 17:34:06 +00:00
|
|
|
TempNumString = CurrentLayerName[:stringPos] # Store longest numeric string so far
|
|
|
|
stringPos = stringPos + 1
|
|
|
|
else:
|
|
|
|
break
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
self.plotCurrentLayer = False #Temporarily assume that we aren't plotting the layer
|
|
|
|
if ( str.isdigit( TempNumString ) ):
|
|
|
|
if ( self.svgLayer == int( float( TempNumString ) ) ):
|
|
|
|
self.plotCurrentLayer = True #We get to plot the layer!
|
2010-08-09 17:34:06 +00:00
|
|
|
self.LayersPlotted += 1
|
|
|
|
#Note: this function is only called if we are NOT plotting all layers.
|
|
|
|
|
2010-10-18 18:35:45 +00:00
|
|
|
def getLength( self, name, default ):
|
|
|
|
'''
|
|
|
|
Get the <svg> attribute with name "name" and default value "default"
|
|
|
|
Parse the attribute into a value and associated units. Then, accept
|
|
|
|
no units (''), units of pixels ('px'), and units of percentage ('%').
|
|
|
|
'''
|
|
|
|
str = self.svg.get( name )
|
|
|
|
if str:
|
|
|
|
v, u = parseLengthWithUnits( str )
|
|
|
|
if not v:
|
|
|
|
# Couldn't parse the value
|
|
|
|
return None
|
|
|
|
elif ( u == '' ) or ( u == 'px' ):
|
|
|
|
return v
|
|
|
|
elif u == '%':
|
|
|
|
return float( default ) * v / 100.0
|
|
|
|
else:
|
|
|
|
# Unsupported units
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
# No width specified; assume the default value
|
|
|
|
return float( default )
|
|
|
|
|
|
|
|
def getDocProps( self ):
|
|
|
|
'''
|
|
|
|
Get the document's height and width attributes from the <svg> tag.
|
|
|
|
Use a default value in case the property is not present or is
|
|
|
|
expressed in units of percentages.
|
|
|
|
'''
|
|
|
|
self.svgHeight = self.getLength( 'height', N_PAGE_HEIGHT )
|
|
|
|
self.svgWidth = self.getLength( 'width', N_PAGE_WIDTH )
|
|
|
|
if ( self.svgHeight == None ) or ( self.svgWidth == None ):
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def plotPath( self, path, matTransform ):
|
2010-09-12 17:54:10 +00:00
|
|
|
'''
|
|
|
|
Plot the path while applying the transformation defined
|
|
|
|
by the matrix [matTransform].
|
|
|
|
'''
|
|
|
|
# turn this path into a cubicsuperpath (list of beziers)...
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
d = path.get( 'd' )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if len( simplepath.parsePath( d ) ) == 0:
|
2010-08-09 17:34:06 +00:00
|
|
|
return
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
p = cubicsuperpath.parsePath( d )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
# ...and apply the transformation to each point
|
2010-09-12 11:05:42 +00:00
|
|
|
applyTransformToPath( matTransform, p )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
# p is now a list of lists of cubic beziers [control pt1, control pt2, endpoint]
|
|
|
|
# where the start-point is the last point in the previous segment.
|
2010-08-09 17:34:06 +00:00
|
|
|
for sp in p:
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
subdivideCubicPath( sp, self.options.smoothness )
|
2010-08-09 17:34:06 +00:00
|
|
|
nIndex = 0
|
|
|
|
|
|
|
|
for csp in sp:
|
|
|
|
|
|
|
|
if self.bStopped:
|
|
|
|
return
|
|
|
|
|
|
|
|
if self.plotCurrentLayer:
|
|
|
|
if nIndex == 0:
|
|
|
|
self.penUp()
|
|
|
|
self.virtualPenIsUp = True
|
|
|
|
elif nIndex == 1:
|
|
|
|
self.penDown()
|
|
|
|
self.virtualPenIsUp = False
|
|
|
|
|
|
|
|
nIndex += 1
|
|
|
|
|
2010-09-14 11:47:11 +00:00
|
|
|
self.fX = float( csp[1][0] ) / self.step_scaling_factor
|
|
|
|
self.fY = float( csp[1][1] ) / self.step_scaling_factor
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
# store home
|
2010-08-09 17:34:06 +00:00
|
|
|
if self.ptFirst is None:
|
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
# if we should start at center, then the first line segment should draw from there
|
2010-08-09 17:34:06 +00:00
|
|
|
if self.options.startCentered:
|
2010-10-18 18:35:45 +00:00
|
|
|
self.fPrevX = self.svgWidth / ( 2 * self.step_scaling_factor )
|
|
|
|
self.fPrevY = self.svgHeight / ( 2 * self.step_scaling_factor )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
self.ptFirst = ( self.fPrevX, self.fPrevY )
|
2010-08-09 17:34:06 +00:00
|
|
|
else:
|
2010-09-12 11:05:42 +00:00
|
|
|
self.ptFirst = ( self.fX, self.fY )
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
if self.plotCurrentLayer:
|
|
|
|
self.plotLineAndTime()
|
|
|
|
self.fPrevX = self.fX
|
|
|
|
self.fPrevY = self.fY
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def sendEnableMotors( self ):
|
|
|
|
self.doCommand( 'EM,1,1\r' )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def sendDisableMotors( self ):
|
2010-10-21 23:17:27 +00:00
|
|
|
# Insist on turning the engraver off. Otherwise, if it is on
|
|
|
|
# and the pen is down, then the engraver's vibration may cause
|
|
|
|
# the loose pen arm to start moving or the egg to start turning.
|
|
|
|
self.engraverOff() # Call will check if engraver option is enabled
|
2010-09-12 11:05:42 +00:00
|
|
|
self.doCommand( 'EM,0,0\r' )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-10-20 18:13:37 +00:00
|
|
|
def doTimedPause( self, nPause ):
|
|
|
|
while ( nPause > 0 ):
|
|
|
|
if ( nPause > 750 ):
|
|
|
|
td = int( 750 )
|
|
|
|
else:
|
|
|
|
td = nPause
|
|
|
|
if ( td < 1 ):
|
|
|
|
td = int( 1 ) # don't allow zero-time moves
|
|
|
|
if ( not self.resumeMode ):
|
|
|
|
self.doCommand( 'SM,' + str( td ) + ',0,0\r' )
|
|
|
|
nPause -= td
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def penUp( self ):
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( ( not self.resumeMode ) or ( not self.virtualPenIsUp ) ):
|
2010-09-12 11:05:42 +00:00
|
|
|
self.doCommand( 'SP,1\r' )
|
2010-10-20 18:13:37 +00:00
|
|
|
self.doTimedPause( self.options.penUpDelay ) # pause for pen to go up
|
2010-08-09 17:34:06 +00:00
|
|
|
self.bPenIsUp = True
|
|
|
|
self.virtualPenIsUp = True
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def penDown( self ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.virtualPenIsUp = False # Virtual pen keeps track of state for resuming plotting.
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( not self.resumeMode ):
|
2010-10-21 23:17:27 +00:00
|
|
|
if self.penDownActivatesEngraver:
|
|
|
|
self.engraverOn() # will check self.enableEngraver
|
2010-09-12 11:05:42 +00:00
|
|
|
self.doCommand( 'SP,0\r' )
|
2010-10-20 18:13:37 +00:00
|
|
|
self.doTimedPause( self.options.penDownDelay ) # pause for pen to go down
|
2010-08-09 17:34:06 +00:00
|
|
|
self.bPenIsUp = False
|
|
|
|
|
2010-10-21 23:17:27 +00:00
|
|
|
def engraverOff( self ):
|
|
|
|
# Note: we don't bother checking self.engraverIsOn -- turn it off regardless
|
|
|
|
# Reason being that we may not know the true hardware state
|
|
|
|
if self.options.engraving:
|
|
|
|
self.doCommand( 'PO,B,3,0\r' )
|
|
|
|
self.engraverIsOn = False
|
|
|
|
|
|
|
|
def engraverOn( self ):
|
|
|
|
if self.options.engraving and ( not self.engraverIsOn ):
|
|
|
|
self.engraverIsOn = True
|
2011-06-07 12:07:39 +00:00
|
|
|
self.doCommand( 'PD,B,3,0\r' ) #Added 6/6/2011, necessary.
|
2010-10-21 23:17:27 +00:00
|
|
|
self.doCommand( 'PO,B,3,1\r' )
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def ServoSetupWrapper( self ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.ServoSetup()
|
2010-09-12 11:05:42 +00:00
|
|
|
strVersion = self.doRequest( 'QP\r' ) #Query pen position: 1 up, 0 down (followed by OK)
|
2010-08-09 17:34:06 +00:00
|
|
|
if strVersion[0] == '0':
|
|
|
|
#inkex.errormsg('Pen is down' )
|
2010-09-12 11:05:42 +00:00
|
|
|
self.doCommand( 'SP,0\r' ) #Lower Pen
|
2010-08-09 17:34:06 +00:00
|
|
|
else:
|
2010-09-12 11:05:42 +00:00
|
|
|
self.doCommand( 'SP,1\r' ) #Raise pen
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def ServoSetup( self ):
|
2010-08-09 17:34:06 +00:00
|
|
|
# Pen position units range from 0% to 100%, which correspond to
|
|
|
|
# a timing range of 6000 - 30000 in units of 1/(12 MHz).
|
|
|
|
# 1% corresponds to 20 us, or 240 units of 1/(12 MHz).
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
intTemp = 240 * ( self.options.penUpPosition + 25 )
|
|
|
|
self.doCommand( 'SC,4,' + str( intTemp ) + '\r' )
|
|
|
|
intTemp = 240 * ( self.options.penDownPosition + 25 )
|
|
|
|
self.doCommand( 'SC,5,' + str( intTemp ) + '\r' )
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
# Servo speed units are in units of %/second, referring to the
|
|
|
|
# percentages above. The EBB takes speeds in units of 1/(12 MHz) steps
|
|
|
|
# per 21 ms. Scaling as above, 1% in 1 second corresponds to
|
|
|
|
# 240 steps/s, which corresponds to 0.240 steps/ms, which corresponds
|
|
|
|
# to 5.04 steps/21 ms. Rounding this to 5 steps/21 ms is correct
|
|
|
|
# to within 1 %.
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-17 18:23:21 +00:00
|
|
|
## intTemp = 5 * self.options.ServoSpeed
|
|
|
|
## self.doCommand( 'SC,10,' + str( intTemp ) + '\r' )
|
2010-08-09 17:34:06 +00:00
|
|
|
#inkex.errormsg('Setting up Servo Motors!')
|
2010-09-17 18:23:21 +00:00
|
|
|
intTemp = 5 * self.options.ServoUpSpeed
|
|
|
|
self.doCommand( 'SC,11,' + str( intTemp ) + '\r' )
|
|
|
|
intTemp = 5 * self.options.ServoDownSpeed
|
|
|
|
self.doCommand( 'SC,12,' + str( intTemp ) + '\r' )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def stop( self ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.bStopped = True
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def plotLineAndTime( self ):
|
2010-09-12 17:54:10 +00:00
|
|
|
'''
|
|
|
|
Send commands out the com port as a line segment (dx, dy) and a time (ms) the segment
|
|
|
|
should take to implement
|
|
|
|
'''
|
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
if self.bStopped:
|
|
|
|
return
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( self.fPrevX is None ):
|
2010-08-09 17:34:06 +00:00
|
|
|
return
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
self.nDeltaX = int( self.fX ) - int( self.fPrevX )
|
|
|
|
self.nDeltaY = int( self.fY ) - int( self.fPrevY )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
|
|
|
if self.bPenIsUp:
|
|
|
|
self.fSpeed = self.options.penUpSpeed
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-14 11:47:11 +00:00
|
|
|
if ( self.options.wraparound ):
|
|
|
|
if ( self.nDeltaX > 1600 / self.step_scaling_factor ):
|
2010-12-16 04:34:56 +00:00
|
|
|
while ( self.nDeltaX > 1600 / self.step_scaling_factor ):
|
|
|
|
self.nDeltaX -= 3200 / self.step_scaling_factor
|
2010-09-14 11:47:11 +00:00
|
|
|
elif ( self.nDeltaX < -1600 / self.step_scaling_factor ):
|
2010-12-16 04:34:56 +00:00
|
|
|
while ( self.nDeltaX < -1600 / self.step_scaling_factor ):
|
|
|
|
self.nDeltaX += 3200 / self.step_scaling_factor
|
2010-08-09 17:34:06 +00:00
|
|
|
|
|
|
|
else:
|
|
|
|
self.fSpeed = self.options.penDownSpeed
|
|
|
|
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( distance( self.nDeltaX, self.nDeltaY ) > 0 ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.nodeCount += 1
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
if self.resumeMode:
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( self.nodeCount > self.nodeTarget ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.resumeMode = False
|
|
|
|
#inkex.errormsg('First node plotted will be number: ' + str(self.nodeCount))
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( not self.virtualPenIsUp ):
|
2010-08-09 17:34:06 +00:00
|
|
|
self.penDown()
|
|
|
|
self.fSpeed = self.options.penDownSpeed
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
nTime = int( math.ceil( 1000 / self.fSpeed * distance( self.nDeltaX, self.nDeltaY ) ) )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
while ( ( abs( self.nDeltaX ) > 0 ) or ( abs( self.nDeltaY ) > 0 ) ):
|
|
|
|
if ( nTime > 750 ):
|
|
|
|
xd = int( round( ( 750.0 * self.nDeltaX ) / nTime ) )
|
|
|
|
yd = int( round( ( 750.0 * self.nDeltaY ) / nTime ) )
|
|
|
|
td = int( 750 )
|
2010-08-09 17:34:06 +00:00
|
|
|
else:
|
|
|
|
xd = self.nDeltaX
|
|
|
|
yd = self.nDeltaY
|
|
|
|
td = nTime
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( td < 1 ):
|
2010-08-09 17:34:06 +00:00
|
|
|
td = 1 # don't allow zero-time moves.
|
|
|
|
|
2010-09-14 12:01:03 +00:00
|
|
|
if ( not self.resumeMode ):
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( self.options.revPenMotor ):
|
2010-09-08 02:03:47 +00:00
|
|
|
yd2 = yd
|
|
|
|
else:
|
|
|
|
yd2 = -yd
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( self.options.revEggMotor ):
|
2010-09-08 02:03:47 +00:00
|
|
|
xd2 = -xd
|
|
|
|
else:
|
|
|
|
xd2 = xd
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
strOutput = ','.join( ['SM', str( td ), str( yd2 ), str( xd2 )] ) + '\r'
|
2010-08-09 17:34:06 +00:00
|
|
|
self.svgTotalDeltaX += xd
|
|
|
|
self.svgTotalDeltaY += yd
|
2010-09-12 11:05:42 +00:00
|
|
|
self.doCommand( strOutput )
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
self.nDeltaX -= xd
|
|
|
|
self.nDeltaY -= yd
|
|
|
|
nTime -= td
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
#self.doCommand('NI\r') #Increment node counter on EBB
|
2010-09-12 11:05:42 +00:00
|
|
|
strButton = self.doRequest( 'QB\r' ) #Query if button pressed
|
2010-08-09 17:34:06 +00:00
|
|
|
if strButton[0] == '0':
|
|
|
|
pass #button not pressed
|
|
|
|
else:
|
|
|
|
self.svgNodeCount = self.nodeCount;
|
2010-09-12 11:05:42 +00:00
|
|
|
inkex.errormsg( 'Plot paused by button press after segment number ' + str( self.nodeCount ) + '.' )
|
|
|
|
inkex.errormsg( 'Use the "resume" feature to continue.' )
|
2010-08-09 17:34:06 +00:00
|
|
|
#self.penUp() # Should be redundant...
|
2010-10-21 23:17:27 +00:00
|
|
|
self.engraverOff()
|
2010-08-09 17:34:06 +00:00
|
|
|
self.bStopped = True
|
|
|
|
return
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
# note: the pen-motor is first, and it corresponds to the y-axis on-screen
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def EggbotOpenSerial( self ):
|
2010-08-09 17:34:06 +00:00
|
|
|
if not bDryRun:
|
|
|
|
self.serialPort = self.getSerialPort()
|
|
|
|
else:
|
2010-09-12 11:05:42 +00:00
|
|
|
self.serialPort = open( DRY_RUN_OUTPUT_FILE, 'w' )
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
if self.serialPort is None:
|
2010-09-12 11:05:42 +00:00
|
|
|
inkex.errormsg( gettext.gettext( "Unable to find an Eggbot on any serial port. :(" ) )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def EggbotCloseSerial( self ):
|
2010-08-09 17:34:06 +00:00
|
|
|
try:
|
2010-09-14 12:01:03 +00:00
|
|
|
if self.serialPort:
|
2010-08-09 17:34:06 +00:00
|
|
|
self.serialPort.flush()
|
|
|
|
self.serialPort.close()
|
|
|
|
if bDebug:
|
|
|
|
self.debugOut.close()
|
|
|
|
finally:
|
|
|
|
self.serialPort = None
|
|
|
|
return
|
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
def testSerialPort( self, strComPort ):
|
|
|
|
'''
|
|
|
|
look at COM1 to COM20 and return a SerialPort object
|
|
|
|
for the first port with an EBB (eggbot board).
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 17:54:10 +00:00
|
|
|
YOU are responsible for closing this serial port!
|
|
|
|
'''
|
2010-08-09 17:34:06 +00:00
|
|
|
|
|
|
|
try:
|
2010-09-12 11:05:42 +00:00
|
|
|
serialPort = serial.Serial( strComPort, timeout=1 ) # 1 second timeout!
|
2010-08-09 17:34:06 +00:00
|
|
|
|
|
|
|
serialPort.setRTS() # ??? remove
|
|
|
|
serialPort.setDTR() # ??? remove
|
|
|
|
serialPort.flushInput()
|
|
|
|
serialPort.flushOutput()
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
time.sleep( 0.1 )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
serialPort.write( 'v\r' )
|
2010-08-09 17:34:06 +00:00
|
|
|
strVersion = serialPort.readline()
|
|
|
|
|
2010-09-14 12:01:03 +00:00
|
|
|
if strVersion and strVersion.startswith( 'EBB' ):
|
2010-08-09 17:34:06 +00:00
|
|
|
# do version control here to check the firmware...
|
|
|
|
return serialPort
|
|
|
|
serialPort.close()
|
|
|
|
except serial.SerialException:
|
|
|
|
pass
|
|
|
|
return None
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def getSerialPort( self ):
|
2010-09-09 22:24:17 +00:00
|
|
|
|
2010-09-13 01:33:40 +00:00
|
|
|
# Before searching, first check to see if the last known
|
|
|
|
# serial port is still good.
|
|
|
|
|
|
|
|
serialPort = self.testSerialPort( self.svgSerialPort )
|
2010-09-14 12:01:03 +00:00
|
|
|
if serialPort:
|
2010-09-13 01:33:40 +00:00
|
|
|
return serialPort
|
|
|
|
|
|
|
|
# Try any devices which seem to have EBB boards attached
|
|
|
|
for strComPort in eggbot_scan.findEiBotBoards():
|
|
|
|
serialPort = self.testSerialPort( strComPort )
|
2010-09-14 12:01:03 +00:00
|
|
|
if serialPort:
|
2010-09-13 02:03:17 +00:00
|
|
|
self.svgSerialPort = strComPort
|
2010-09-13 01:33:40 +00:00
|
|
|
return serialPort
|
|
|
|
|
|
|
|
# Try any likely ports
|
|
|
|
for strComPort in eggbot_scan.findPorts():
|
|
|
|
serialPort = self.testSerialPort( strComPort )
|
2010-09-14 12:01:03 +00:00
|
|
|
if serialPort:
|
2010-09-13 02:03:17 +00:00
|
|
|
self.svgSerialPort = strComPort
|
2010-09-09 22:24:17 +00:00
|
|
|
return serialPort
|
|
|
|
|
2010-08-09 17:34:06 +00:00
|
|
|
return None
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def doCommand( self, cmd ):
|
2010-08-09 17:34:06 +00:00
|
|
|
try:
|
2010-09-12 11:05:42 +00:00
|
|
|
self.serialPort.write( cmd )
|
2010-08-09 17:34:06 +00:00
|
|
|
response = self.serialPort.readline()
|
2010-09-12 11:05:42 +00:00
|
|
|
if ( response != 'OK\r\n' ):
|
|
|
|
if ( response != '' ):
|
|
|
|
inkex.errormsg( 'After command ' + cmd + ',' )
|
|
|
|
inkex.errormsg( 'Received bad response from EBB: ' + str( response ) + '.' )
|
2010-08-09 17:34:06 +00:00
|
|
|
#inkex.errormsg('BTW:: Node number is ' + str(self.nodeCount) + '.')
|
|
|
|
|
|
|
|
else:
|
2010-09-12 11:05:42 +00:00
|
|
|
inkex.errormsg( 'EBB Serial Timeout.' )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def doRequest( self, cmd ):
|
2010-08-09 17:34:06 +00:00
|
|
|
response = ''
|
|
|
|
try:
|
2010-09-12 11:05:42 +00:00
|
|
|
self.serialPort.write( cmd )
|
2010-08-09 17:34:06 +00:00
|
|
|
response = self.serialPort.readline()
|
2010-09-12 17:54:10 +00:00
|
|
|
unused_response = self.serialPort.readline() #read in extra blank/OK line
|
2010-08-09 17:34:06 +00:00
|
|
|
except:
|
2010-09-12 11:05:42 +00:00
|
|
|
inkex.errormsg( gettext.gettext( "Error reading serial data." ) )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
2010-09-09 22:24:17 +00:00
|
|
|
return response
|
2010-10-21 23:17:27 +00:00
|
|
|
|
2010-09-12 11:05:42 +00:00
|
|
|
def distance( x, y ):
|
2010-09-12 17:54:10 +00:00
|
|
|
'''
|
|
|
|
Pythagorean theorem!
|
|
|
|
'''
|
2010-09-12 11:05:42 +00:00
|
|
|
return sqrt( x * x + y * y )
|
2010-08-09 17:34:06 +00:00
|
|
|
|
|
|
|
e = EggBot()
|
|
|
|
#e.affect(output=False)
|
2010-08-09 22:50:13 +00:00
|
|
|
e.affect()
|