#!/usr/bin/env python 

'''
Copyright (C) 2007 hugomatic...
based on dots.py (C) 2005 Aaron Spike, aaron@ekips.org
modified by Fabrizio Zellini: units to mm and simple transformation matrix

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
'''
import sys
sys.path.append('/usr/share/inkscape/extensions') # or another path, as necessary

import inkex, simplestyle, simplepath
import os,re
import xml.xpath


# mathematically a 3-by-3 matrix:
#
#  a  b  0
#  c  d  0
#  tx ty 1
#
#
# we represent this as a 6-element list:
#   0  1  2  3  4  5
# [ a  b  c  d tx ty ]
#
# translation by (tx, ty) is described by
#
#   1  0  0
#   0  1  0
#  tx ty  1
#
#
# scaling by sx, sy is described by
#
#  sx  0  0
#   0 sy  0
#   0  0  1
#
#
# rotations counterclockwise about the origin by an angle A
# is described by:
#
#  cos(A) sin(A) 0
# -sin(A) cos(A) 0
#    0     0     1
#



class Gcode(inkex.Effect):
    def __init__(self):
        inkex.Effect.__init__(self)
        self.OptionParser.add_option("-d", "--directory",
                        action="store", type="string", 
                        dest="directory", default="/tmp/",
                        help="Directory for gcode file")

        self.OptionParser.add_option("-f", "--file",
                        action="store", type="string", 
                        dest="file", default="out.ngc",
                        help="File name")           

        self.OptionParser.add_option("-u", "--scaleX",
                        action="store", type="float", 
                        dest="scaleX", default="100.0",
                        help="Scale factor X")  

        self.OptionParser.add_option("-v", "--scaleY",
                        action="store", type="float", 
                        dest="scaleY", default="100.0",
                        help="Scale factor Y")  

        self.OptionParser.add_option("-x", "--offsetX",
                        action="store", type="float", 
                        dest="offsetX", default="0.0",
                        help="Offset along X")  

        self.OptionParser.add_option("-y", "--offsetY",
                        action="store", type="float", 
                        dest="offsetY", default="0.0",
                        help="Offset along Y")

        self.OptionParser.add_option("-s", "--safeZ",
                        action="store", type="float", 
                        dest="safeZ", default="5",
                        help="Z above all obstacles")

        self.OptionParser.add_option("-z", "--surfaceZ",
                        action="store", type="float", 
                        dest="surfaceZ", default="0.0",
                        help="Z above all obstacles")

        self.OptionParser.add_option("-c", "--cutZ",
                        action="store", type="float", 
                        dest="cutZ", default="-4",
                        help="Z depth of cut")

        self.OptionParser.add_option("-p", "--feed",
                        action="store", type="float", 
                        dest="feed", default="100.0",
                        help="Feed rate in mm/min")

    def effect(self):
        xScale =  self.options.scaleX*2.54/900.0 
        yScale =  self.options.scaleY*2.54/900.0
        xOffset = self.options.offsetX
        yOffset = self.options.offsetY
        safeZ = self.options.safeZ
        surfaceZ = self.options.surfaceZ
        cutZ = self.options.cutZ
        feed = self.options.feed
        # default for transformation table
        #1, 0, 0, 1, 0, 0
        a=1
        b=0
        c=0
        d=1
        tx=0
        ty=0
        
        ySize=0
        ctx = xml.xpath.Context.Context(self.document,processorNss=inkex.NSS)
        yattr = xml.xpath.Evaluate('//svg/@height',self.document,context=ctx)
        if yattr:
            ySize = yattr[0].value
            ySize = inkex.unittouu(ySize)
            ySize = ySize
        
        
        matrixre = re.compile ("matrix\(([^,]+),([^,]+),([^,]+),([^,]+),([^,]+),([^,]+)\)")
        translatere = re.compile ("translate\(([^,]+),([^,]+)\)")

        fileName = os.path.join(self.options.directory, self.options.file)
        ngcfile = open(fileName, 'wb')
        ngcfile.write("G21\nF%.4f\n" % feed)
        ngcfile.write("G0 Z%.4f\n (move out of the way)\n" % safeZ)
        
        for id, node in self.selected.iteritems():
            ngcfile.write("\n(ID " + str(id) + ")\n")
            
            if node.tagName == 'path':
                self.group = self.document.createElement('svg:g')
                node.parentNode.appendChild(self.group)
                new = self.document.createElement('svg:path')
                
                try:
                    t = node.attributes.getNamedItem('transform').value
                    self.group.setAttribute('transform', t)
                    # parse matrix to fixup dimensions
                    # matrix(3.1187192,0,0,1.6602321,-174.43497,-1.6029062)
                    # parse matrix
                    match = matrixre.match (t) 
                    if match:
                      a = float(match.group(1))
                      b = float(match.group(2))
                      c = float(match.group(3))
                      d = float(match.group(4))
                      tx = float(match.group(5))
                      ty = float(match.group(6))
                      
                    match = translatere.match(t)
                    if match:
                      tx = float(match.group(1))
                      ty = float(match.group(2))
                      
                      #print a,b,c,d,e,f


                except AttributeError:
                    pass

                p = simplepath.parsePath(node.attributes.getNamedItem('d').value)

                for cmd,params in p:
                    if cmd == 'M':
                        xPath = float(params[-2])
                        yPath = float(params[-1])
                        # apply transformation matrix
                        xPath = a*xPath + c*yPath + tx
                        yPath = b*xPath + d*yPath + ty
                        yPath = ySize-yPath
                        
                        x = xPath * xScale + xOffset
                        y = yPath * yScale + yOffset
                        
                        ngcfile.write("g00 Z%.4f\n (approaching)\n" % surfaceZ)
                        ngcfile.write("g00 X%.4f Y%.4f\n (fly above next cut)\n" % (x,y))
                        
                        ngcfile.write("g01 Z%.4f\n (plunging)\n" % cutZ)
                        ngcfile.write("g01 X%.4f Y%.4f\n" % (x,y))
                        continue
                                        
                    if cmd == 'C':
                        xa = float(params[0])
                        ya = float(params[1])
                        xb = float(params[2])
                        yb = float(params[3])
                        xc = float(params[4])
                        yc = float(params[5])
                        self.interpolate(ngcfile, xa,ya, xb,yb, xc,yc, 10)

                        
                    if cmd != 'Z':
                        xPath = float(params[-2])
                        yPath = float(params[-1])
                        # apply transformation matrix
                        xPath = a*xPath + c*yPath + tx
                        yPath = b*xPath + d*yPath + ty
                        yPath = ySize-yPath
                        
                        x = xPath * xScale + xOffset
                        y = yPath * yScale + yOffset
                        ngcfile.write("g01 X%.4f Y%.4f\n" % (x,y))
                    
                ngcfile.write("G0 Z%.4f\n (move out of the way)" % safeZ)
        ngcfile.write("G00 Z%.4f\n" % safeZ)
        ngcfile.write("M2\n")
        ngcfile.close()
                

    def interpolate(self, ngcfile, xa,ya, xb,yb, xc,yc, nbOfPoints):
        
        ngcfile.write("(C [%.4f, %.4f] [%.4f, %.4f] [%.4f, %.4f])\n" %(xa,ya, xb, yb, xc,yc))
        
    
e = Gcode()
e.affect()

