#!/usr/bin/env python 
"""
Comments starting "#LT" or "#CLT" are by Chris Lusby Taylor who rewrote the engraving function in 2011.
History of CLT changes to engraving and other functions it uses:
9 May 2011 Changed test of tool diameter to square it
10 May Note that there are many unused functions, including:
	  bound_to_bound_distance, csp_curvature_radius_at_t,
        csp_special_points, csplength, rebuild_csp, csp_slope,
        csp_simple_bound_to_point_distance, csp_bound_to_point_distance,
        bez_at_t, bez_to_point_distance, bez_normalized_slope, matrix_mul, transpose
       Fixed csp_point_inside_bound() to work if x outside bounds
20 May Now encoding the bisectors of angles.
23 May Using r/cos(a) instead of normalised normals for bisectors of angles.
23 May Note that Z values generated for engraving are in pixels, not mm.
       Removed the biarc curves - straight lines are better.
24 May Changed Bezier slope calculation to be less sensitive to tiny differences in points.
       Added use of self.options.engraving_newton_iterations to control accuracy
25 May Big restructure and new recursive function.
	   Changed the way I treat corners - I now find if the centre of a proposed circle is
                within the area bounded by the line being tested and the two angle bisectors at
		    its ends. See get_radius_to_line().
29 May Eliminating redundant points. If A,B,C colinear, drop B
30 May Eliminating redundant lines in divided Beziers. Changed subdivision of lines
  7Jun Try to show engraving in 3D
 8 Jun Displaying in stereo 3D.
       Fixed a bug in bisect - it could go wrong due to rounding errors if
			1+x1.x2+y1.y2<0 which should never happen. BTW, I spotted a non-normalised normal
			returned by csp_normalized_normal. Need to check for that.
 9 Jun Corrected spelling of 'definition' but still match previous 'defention' and 	  'defenition' if found in file
	 Changed get_tool to find 1.6.04 tools or new tools with corrected spelling
10 Jun Put 3D into a separate layer called 3D, created unless it already exists
       Changed csp_normalized_slope to reject lines shorter than 1e-9.
10 Jun Changed all dimensions seen by user to be mm/inch, not pixels. This includes
	  tool diameter, maximum engraving distance, tool shape and all Z values.
12 Jun ver 208 Now scales correctly if orientation points moved or stretched.
12 Jun ver 209. Now detect if engraving toolshape not a function of radius
                Graphics now indicate Gcode toolpath, limited by min(tool diameter/2,max-dist)
24 Jan 2017 Removed hard-coded scale values from orientation point calculation
TODO Change line division to be recursive, depending on what line is touched. See line_divide


engraving() functions (c) 2011 Chris Lusby Taylor, clusbytaylor@enterprise.net
Copyright (C) 2009 Nick Drobchenko, nick@cnc-club.ru
based on gcode.py (C) 2007 hugomatic... 
based on addnodes.py (C) 2005,2007 Aaron Spike, aaron@ekips.org
based on dots.py (C) 2005 Aaron Spike, aaron@ekips.org
based on interp.py (C) 2005 Aaron Spike, aaron@ekips.org
based on bezmisc.py (C) 2005 Aaron Spike, aaron@ekips.org
based on cubicsuperpath.py (C) 2005 Aaron Spike, aaron@ekips.org

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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
"""

###
###		Gcodetools v 1.7
###

gcodetools_current_version = "1.7"

# standard library
import os
import math
import bezmisc
import re
import copy
import sys
import time
import cmath
import codecs
import random
# local library
import inkex
import simplestyle
import simplepath
import cubicsuperpath
import simpletransform
import bezmisc
 
### Check if inkex has errormsg (0.46 version does not have one.) Could be removed later.
if "errormsg" not in dir(inkex):
	inkex.errormsg = lambda msg: sys.stderr.write((unicode(msg) + "\n").encode("UTF-8"))

try:
    import numpy
except:
    inkex.errormsg(_("Failed to import the numpy modules. These modules are required by this extension. Please install them and try again.  On a Debian-like system this can be done with the command, sudo apt-get install python-numpy."))
    exit()


def bezierslopeatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t):
	ax,ay,bx,by,cx,cy,x0,y0=bezmisc.bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
	dx=3*ax*(t**2)+2*bx*t+cx
	dy=3*ay*(t**2)+2*by*t+cy
	if dx==dy==0 : 
		dx = 6*ax*t+2*bx
		dy = 6*ay*t+2*by
		if dx==dy==0 : 
			dx = 6*ax
			dy = 6*ay
			if dx==dy==0 : 
				print_("Slope error x = %s*t^3+%s*t^2+%s*t+%s, y = %s*t^3+%s*t^2+%s*t+%s,  t = %s, dx==dy==0" % (ax,bx,cx,dx,ay,by,cy,dy,t))
				print_(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
				dx, dy = 1, 1
				
	return dx,dy
bezmisc.bezierslopeatt = bezierslopeatt


def ireplace(self,old,new,count=0):
	pattern = re.compile(re.escape(old),re.I)
	return re.sub(pattern,new,self,count) 

def isset(variable):
	# VARIABLE NAME SHOULD BE A STRING! Like isset("foobar")
	return variable in locals() or variable in globals()
	
	
################################################################################
###
###		Styles and additional parameters
###
################################################################################

math.pi2 = math.pi*2
straight_tolerance = 0.0001
straight_distance_tolerance = 0.0001
engraving_tolerance = 0.0001
loft_lengths_tolerance = 0.0000001

EMC_TOLERANCE_EQUAL = 0.00001

options = {}
defaults = {
'header': """%
(Header)
(Generated by gcodetools from Inkscape.)
(Using default header. To add your own header create file "header" in the output dir.)
M3
(Header end.)
""",
'footer': """
(Footer)
M5
G00 X0.0000 Y0.0000
M2
(Using default footer. To add your own footer create file "footer" in the output dir.)
(end)
%"""
}

intersection_recursion_depth = 10
intersection_tolerance = 0.00001

styles = {
		"in_out_path_style" : simplestyle.formatStyle({ 'stroke': '#0072a7', 'fill': 'none', 'stroke-width':'1', 'marker-mid':'url(#InOutPathMarker)' }),
		
		"loft_style" : {
				'main curve':	simplestyle.formatStyle({ 'stroke': '#88f', 'fill': 'none', 'stroke-width':'1', 'marker-end':'url(#Arrow2Mend)' }),
			},
		"biarc_style" : {
				'biarc0':	simplestyle.formatStyle({ 'stroke': '#88f', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
				'biarc1':	simplestyle.formatStyle({ 'stroke': '#8f8', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
				'line':		simplestyle.formatStyle({ 'stroke': '#f88', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
				'area':		simplestyle.formatStyle({ 'stroke': '#777', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.1' }),
			},
		"biarc_style_dark" : {
				'biarc0':	simplestyle.formatStyle({ 'stroke': '#33a', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
				'biarc1':	simplestyle.formatStyle({ 'stroke': '#3a3', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
				'line':		simplestyle.formatStyle({ 'stroke': '#a33', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
				'area':		simplestyle.formatStyle({ 'stroke': '#222', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
			},
		"biarc_style_dark_area" : {
				'biarc0':	simplestyle.formatStyle({ 'stroke': '#33a', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.1' }),
				'biarc1':	simplestyle.formatStyle({ 'stroke': '#3a3', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.1' }),
				'line':		simplestyle.formatStyle({ 'stroke': '#a33', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.1' }),
				'area':		simplestyle.formatStyle({ 'stroke': '#222', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
			},
		"biarc_style_i"  : {
				'biarc0':	simplestyle.formatStyle({ 'stroke': '#880', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
				'biarc1':	simplestyle.formatStyle({ 'stroke': '#808', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
				'line':		simplestyle.formatStyle({ 'stroke': '#088', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
				'area':		simplestyle.formatStyle({ 'stroke': '#999', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
			},
		"biarc_style_dark_i" : {
				'biarc0':	simplestyle.formatStyle({ 'stroke': '#dd5', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
				'biarc1':	simplestyle.formatStyle({ 'stroke': '#d5d', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
				'line':		simplestyle.formatStyle({ 'stroke': '#5dd', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
				'area':		simplestyle.formatStyle({ 'stroke': '#aaa', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
			},
		"biarc_style_lathe_feed" : {
				'biarc0':	simplestyle.formatStyle({ 'stroke': '#07f', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
				'biarc1':	simplestyle.formatStyle({ 'stroke': '#0f7', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
				'line':		simplestyle.formatStyle({ 'stroke': '#f44', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
				'area':		simplestyle.formatStyle({ 'stroke': '#aaa', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
			},
		"biarc_style_lathe_passing feed" : {
				'biarc0':	simplestyle.formatStyle({ 'stroke': '#07f', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
				'biarc1':	simplestyle.formatStyle({ 'stroke': '#0f7', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
				'line':		simplestyle.formatStyle({ 'stroke': '#f44', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
				'area':		simplestyle.formatStyle({ 'stroke': '#aaa', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
			},
		"biarc_style_lathe_fine feed" : {
				'biarc0':	simplestyle.formatStyle({ 'stroke': '#7f0', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
				'biarc1':	simplestyle.formatStyle({ 'stroke': '#f70', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
				'line':		simplestyle.formatStyle({ 'stroke': '#744', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
				'area':		simplestyle.formatStyle({ 'stroke': '#aaa', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
			},
		"area artefact": 		simplestyle.formatStyle({ 'stroke': '#ff0000', 'fill': '#ffff00', 'stroke-width':'1' }),
		"area artefact arrow":	simplestyle.formatStyle({ 'stroke': '#ff0000', 'fill': '#ffff00', 'stroke-width':'1' }),
		"dxf_points":		 	simplestyle.formatStyle({ "stroke": "#ff0000", "fill": "#ff0000"}),
		
	}



################################################################################
###		Gcode additional functions
################################################################################

def gcode_comment_str(s, replace_new_line = False):
	if replace_new_line :
		s = re.sub(r"[\n\r]+", ".", s)
	res = ""
	if s[-1] == "\n" : s = s[:-1]
	for a in s.split("\n") :
		if a != "" : 
			res +=  "(" + re.sub(r"[\(\)\\\n\r]", ".", a) + ")\n"
		else : 	
			res +=  "\n"
	return res	


################################################################################
###		Cubic Super Path additional functions
################################################################################


def csp_from_polyline(line) :
	return [ [ [point[:] for k in range(3) ] for point in subline ]  for subline in line ]
	
def csp_remove_zerro_segments(csp, tolerance = 1e-7):	
	res = []
	for subpath in csp:
		if len(subpath) > 0 :
			res.append([subpath[0]])
			for sp1,sp2 in zip(subpath,subpath[1:]) :
				if point_to_point_d2(sp1[1],sp2[1])<=tolerance and point_to_point_d2(sp1[2],sp2[1])<=tolerance and point_to_point_d2(sp1[1],sp2[0])<=tolerance :
					res[-1][-1][2] = sp2[2]
				else :
					res[-1].append(sp2)
	return res
			

	


def point_inside_csp(p,csp, on_the_path = True) :
	# we'll do the raytracing and see how many intersections are there on the ray's way. 
	# if number of intersections is even then point is outside.
	# ray will be x=p.x and y=>p.y
	# you can assing any value to on_the_path, by dfault if point is on the path 
	# function will return thai it's inside the path. 
	x,y = p
	ray_intersections_count = 0
	for subpath in csp :
		
		for i in range(1, len(subpath)) :
			sp1, sp2 = subpath[i-1], subpath[i]
			ax,ay,bx,by,cx,cy,dx,dy = csp_parameterize(sp1,sp2)
		 	if  ax==0 and bx==0 and cx==0 and dx==x : 
		 		#we've got a special case here
		 		b = csp_true_bounds( [[sp1,sp2]])
		 		if  b[1][1]<=y<=b[3][1] :
		 			# points is on the path 
			 		return on_the_path
			 	else :
			 		# we can skip this segment because it wont influence the answer.
			 		pass	
		 	else: 
	 			for t in csp_line_intersection([x,y],[x,y+5],sp1,sp2) :
	 				if t == 0 or t == 1 :
	 					#we've got another special case here
		 				x1,y1 = csp_at_t(sp1,sp2,t)
		 				if y1==y : 
		 					# the point is on the path 
		 					return on_the_path
	 					# if t == 0 we should have considered this case previously. 
	 					if t == 1 :
							# we have to check the next segmant if it is on the same side of the ray
							st_d = csp_normalized_slope(sp1,sp2,1)[0]
							if st_d == 0 : st_d = csp_normalized_slope(sp1,sp2,0.99)[0]
							
							for j in range(1, len(subpath)+1):
								if (i+j) % len(subpath) == 0  : continue # skip the closing segment 
								sp11,sp22 = subpath[(i-1+j) % len(subpath)], subpath[(i+j) % len(subpath)]
								ax1,ay1,bx1,by1,cx1,cy1,dx1,dy1 = csp_parameterize(sp1,sp2)
		 						if  ax1==0 and bx1==0 and cx1==0 and dx1==x : continue # this segment parallel to the ray, so skip it 
								en_d = csp_normalized_slope(sp11,sp22,0)[0]
								if en_d == 0 : en_d = csp_normalized_slope(sp11,sp22,0.01)[0]
								if st_d*en_d <=0 : 
			 						ray_intersections_count += 1
			 						break 
	 				else :	
		 				x1,y1 = csp_at_t(sp1,sp2,t)
		 				if y1==y : 
		 					# the point is on the path 
		 					return on_the_path
		 				else :
		 					if y1>y and 3*ax*t**2 + 2*bx*t + cx !=0 : # if it's 0 the path only touches the ray
		 						ray_intersections_count += 1 	
	return ray_intersections_count%2 == 1 				

def csp_close_all_subpaths(csp, tolerance = 0.000001):
	for i in range(len(csp)):
		if point_to_point_d2(csp[i][0][1] , csp[i][-1][1])> tolerance**2 :
			csp[i][-1][2] = csp[i][-1][1][:] 
			csp[i] += [ [csp[i][0][1][:] for j in range(3)] ]
		else: 
			if csp[i][0][1] != csp[i][-1][1] : 
				csp[i][-1][1] = csp[i][0][1][:]
	return csp

def csp_simple_bound(csp):
	minx,miny,maxx,maxy = None,None,None,None
	for subpath in csp:
		for sp in subpath : 
			for p in sp:
				minx = min(minx,p[0]) if minx!=None else p[0]
				miny = min(miny,p[1]) if miny!=None else p[1]
				maxx = max(maxx,p[0]) if maxx!=None else p[0]
				maxy = max(maxy,p[1]) if maxy!=None else p[1]
	return minx,miny,maxx,maxy		


def csp_segment_to_bez(sp1,sp2) :
	return sp1[1:]+sp2[:2]


def bound_to_bound_distance(sp1,sp2,sp3,sp4) :
	min_dist = 1e100
	max_dist = 0
	points1 = csp_segment_to_bez(sp1,sp2)
	points2 = csp_segment_to_bez(sp3,sp4)
	for i in range(4) :
		for j in range(4) :
			min_, max_ = line_to_line_min_max_distance_2(points1[i-1], points1[i], points2[j-1], points2[j])
			min_dist = min(min_dist,min_)
			max_dist = max(max_dist,max_)
			print_("bound_to_bound", min_dist, max_dist)
	return min_dist, max_dist
	
def csp_to_point_distance(csp, p, dist_bounds = [0,1e100], tolerance=.01) :
	min_dist = [1e100,0,0,0]
	for j in range(len(csp)) :
		for i in range(1,len(csp[j])) :
			d = csp_seg_to_point_distance(csp[j][i-1],csp[j][i],p,sample_points = 5, tolerance = .01)
			if d[0] < dist_bounds[0] : 
#				draw_pointer( list(csp_at_t(subpath[dist[2]-1],subpath[dist[2]],dist[3]))
#					+list(csp_at_t(csp[dist[4]][dist[5]-1],csp[dist[4]][dist[5]],dist[6])),"red","line", comment = math.sqrt(dist[0]))
				return [d[0],j,i,d[1]]
			else : 
				if d[0] < min_dist[0] : min_dist = [d[0],j,i,d[1]]
	return min_dist
			
def csp_seg_to_point_distance(sp1,sp2,p,sample_points = 5, tolerance = .01) :
	ax,ay,bx,by,cx,cy,dx,dy = csp_parameterize(sp1,sp2)
	dx, dy = dx-p[0], dy-p[1]
	if sample_points < 2 : sample_points = 2
	d = min( [(p[0]-sp1[1][0])**2 + (p[1]-sp1[1][1])**2,0.], [(p[0]-sp2[1][0])**2 + (p[1]-sp2[1][1])**2,1.]	)	
	for k in range(sample_points) :
		t = float(k)/(sample_points-1)
		i = 0
		while i==0 or abs(f)>0.000001 and i<20 : 
			t2,t3 = t**2,t**3
			f = (ax*t3+bx*t2+cx*t+dx)*(3*ax*t2+2*bx*t+cx) + (ay*t3+by*t2+cy*t+dy)*(3*ay*t2+2*by*t+cy)
			df = (6*ax*t+2*bx)*(ax*t3+bx*t2+cx*t+dx) + (3*ax*t2+2*bx*t+cx)**2 + (6*ay*t+2*by)*(ay*t3+by*t2+cy*t+dy) + (3*ay*t2+2*by*t+cy)**2
			if df!=0 :
				t = t - f/df
			else :	
				break
			i += 1	
		if 0<=t<=1 : 
			p1 = csp_at_t(sp1,sp2,t)
			d1 = (p1[0]-p[0])**2 + (p1[1]-p[1])**2
			if d1 < d[0] :
				d = [d1,t]
	return d	


def csp_seg_to_csp_seg_distance(sp1,sp2,sp3,sp4, dist_bounds = [0,1e100], sample_points = 5, tolerance=.01) : 
	# check the ending points first
	dist =	csp_seg_to_point_distance(sp1,sp2,sp3[1],sample_points, tolerance)
	dist += [0.]
	if dist[0] <= dist_bounds[0] : return dist
	d = csp_seg_to_point_distance(sp1,sp2,sp4[1],sample_points, tolerance)
	if d[0]<dist[0] :
		dist = d+[1.]
		if dist[0] <= dist_bounds[0] : return dist
	d =	csp_seg_to_point_distance(sp3,sp4,sp1[1],sample_points, tolerance)
	if d[0]<dist[0] :
		dist = [d[0],0.,d[1]]
		if dist[0] <= dist_bounds[0] : return dist
	d =	csp_seg_to_point_distance(sp3,sp4,sp2[1],sample_points, tolerance)
	if d[0]<dist[0] :
		dist = [d[0],1.,d[1]]
		if dist[0] <= dist_bounds[0] : return dist
	sample_points -= 2
	if sample_points < 1 : sample_points = 1
	ax1,ay1,bx1,by1,cx1,cy1,dx1,dy1 = csp_parameterize(sp1,sp2)
	ax2,ay2,bx2,by2,cx2,cy2,dx2,dy2 = csp_parameterize(sp3,sp4)
	#	try to find closes points using Newtons method
	for k in range(sample_points) :
		for j in range(sample_points) : 
			t1,t2 = float(k+1)/(sample_points+1), float(j)/(sample_points+1)
			t12, t13, t22, t23 = t1*t1, t1*t1*t1, t2*t2, t2*t2*t2
			i = 0
			F1, F2, F = [0,0], [[0,0],[0,0]], 1e100
			x,y   = ax1*t13+bx1*t12+cx1*t1+dx1 - (ax2*t23+bx2*t22+cx2*t2+dx2), ay1*t13+by1*t12+cy1*t1+dy1 - (ay2*t23+by2*t22+cy2*t2+dy2)			
			while i<2 or abs(F-Flast)>tolerance and i<30 :
				#draw_pointer(csp_at_t(sp1,sp2,t1))
				f1x = 3*ax1*t12+2*bx1*t1+cx1
				f1y = 3*ay1*t12+2*by1*t1+cy1
				f2x = 3*ax2*t22+2*bx2*t2+cx2
				f2y = 3*ay2*t22+2*by2*t2+cy2
				F1[0] = 2*f1x*x +  2*f1y*y
				F1[1] = -2*f2x*x -  2*f2y*y
				F2[0][0] =  2*(6*ax1*t1+2*bx1)*x + 2*f1x*f1x + 2*(6*ay1*t1+2*by1)*y +2*f1y*f1y
				F2[0][1] = -2*f1x*f2x - 2*f1y*f2y
				F2[1][0] = -2*f2x*f1x - 2*f2y*f1y 
				F2[1][1] = -2*(6*ax2*t2+2*bx2)*x + 2*f2x*f2x - 2*(6*ay2*t2+2*by2)*y + 2*f2y*f2y
				F2 = inv_2x2(F2)
				if F2!=None :
					t1 -= ( F2[0][0]*F1[0] + F2[0][1]*F1[1] )
					t2 -= ( F2[1][0]*F1[0] + F2[1][1]*F1[1] )
					t12, t13, t22, t23 = t1*t1, t1*t1*t1, t2*t2, t2*t2*t2
					x,y   = ax1*t13+bx1*t12+cx1*t1+dx1 - (ax2*t23+bx2*t22+cx2*t2+dx2), ay1*t13+by1*t12+cy1*t1+dy1 - (ay2*t23+by2*t22+cy2*t2+dy2)
					Flast = F
					F = x*x+y*y
				else : 
					break
				i += 1
			if F < dist[0] and 0<=t1<=1 and 0<=t2<=1:
				dist = [F,t1,t2]
				if dist[0] <= dist_bounds[0] : 
					return dist
	return dist			


def csp_to_csp_distance(csp1,csp2, dist_bounds = [0,1e100], tolerance=.01) : 
	dist = [1e100,0,0,0,0,0,0]
	for i1 in range(len(csp1)) : 
		for j1 in range(1,len(csp1[i1])) :
			for i2 in range(len(csp2)) :
				for j2 in range(1,len(csp2[i2])) :
					d = csp_seg_bound_to_csp_seg_bound_max_min_distance(csp1[i1][j1-1],csp1[i1][j1],csp2[i2][j2-1],csp2[i2][j2])
					if d[0] >= dist_bounds[1] : continue
					if  d[1] < dist_bounds[0] : return [d[1],i1,j1,1,i2,j2,1]
					d = csp_seg_to_csp_seg_distance(csp1[i1][j1-1],csp1[i1][j1],csp2[i2][j2-1],csp2[i2][j2], dist_bounds, tolerance=tolerance)
					if d[0] < dist[0] :
						dist = [d[0], i1,j1,d[1], i2,j2,d[2]]
					if dist[0] <= dist_bounds[0] :	
						return dist
			if dist[0] >= dist_bounds[1] :	
				return dist
	return dist
#	draw_pointer( list(csp_at_t(csp1[dist[1]][dist[2]-1],csp1[dist[1]][dist[2]],dist[3]))
#				+ list(csp_at_t(csp2[dist[4]][dist[5]-1],csp2[dist[4]][dist[5]],dist[6])), "#507","line")
					
					
def csp_split(sp1,sp2,t=.5) :
	[x1,y1],[x2,y2],[x3,y3],[x4,y4] = sp1[1], sp1[2], sp2[0], sp2[1] 
	x12 = x1+(x2-x1)*t
	y12 = y1+(y2-y1)*t
	x23 = x2+(x3-x2)*t
	y23 = y2+(y3-y2)*t
	x34 = x3+(x4-x3)*t
	y34 = y3+(y4-y3)*t
	x1223 = x12+(x23-x12)*t
	y1223 = y12+(y23-y12)*t
	x2334 = x23+(x34-x23)*t
	y2334 = y23+(y34-y23)*t
	x = x1223+(x2334-x1223)*t
	y = y1223+(y2334-y1223)*t
	return [sp1[0],sp1[1],[x12,y12]], [[x1223,y1223],[x,y],[x2334,y2334]], [[x34,y34],sp2[1],sp2[2]]
	
	
def csp_true_bounds(csp) :
	# Finds minx,miny,maxx,maxy of the csp and return their (x,y,i,j,t) 
	minx = [float("inf"), 0, 0, 0]																																																																																																								
	maxx = [float("-inf"), 0, 0, 0]
	miny = [float("inf"), 0, 0, 0]
	maxy = [float("-inf"), 0, 0, 0]
	for i in range(len(csp)):
		for j in range(1,len(csp[i])):
			ax,ay,bx,by,cx,cy,x0,y0 = bezmisc.bezierparameterize((csp[i][j-1][1],csp[i][j-1][2],csp[i][j][0],csp[i][j][1]))
			roots = cubic_solver(0, 3*ax, 2*bx, cx)	 + [0,1]
			for root in roots :
				if type(root) is complex and abs(root.imag)<1e-10:
					root = root.real
				if type(root) is not complex and 0<=root<=1:
					y = ay*(root**3)+by*(root**2)+cy*root+y0  
					x = ax*(root**3)+bx*(root**2)+cx*root+x0  
					maxx = max([x,y,i,j,root],maxx)
					minx = min([x,y,i,j,root],minx)

			roots = cubic_solver(0, 3*ay, 2*by, cy)	 + [0,1]
			for root in roots :
				if type(root) is complex and root.imag==0:
					root = root.real
				if type(root) is not complex and 0<=root<=1:
					y = ay*(root**3)+by*(root**2)+cy*root+y0  
					x = ax*(root**3)+bx*(root**2)+cx*root+x0  
					maxy = max([y,x,i,j,root],maxy)
					miny = min([y,x,i,j,root],miny)
	maxy[0],maxy[1] = maxy[1],maxy[0]
	miny[0],miny[1] = miny[1],miny[0]

	return minx,miny,maxx,maxy


############################################################################
### csp_segments_intersection(sp1,sp2,sp3,sp4)
###
### Returns array containig all intersections between two segmets of cubic 
### super path. Results are [ta,tb], or [ta0, ta1, tb0, tb1, "Overlap"] 
### where ta, tb are values of t for the intersection point.
############################################################################
def csp_segments_intersection(sp1,sp2,sp3,sp4) :
	a, b = csp_segment_to_bez(sp1,sp2), csp_segment_to_bez(sp3,sp4)

	def polish_intersection(a,b,ta,tb, tolerance = intersection_tolerance) :
		ax,ay,bx,by,cx,cy,dx,dy			= bezmisc.bezierparameterize(a)
		ax1,ay1,bx1,by1,cx1,cy1,dx1,dy1	= bezmisc.bezierparameterize(b)
		i = 0
		F, F1 =  [.0,.0], [[.0,.0],[.0,.0]]
		while i==0 or (abs(F[0])**2+abs(F[1])**2 > tolerance and i<10):
			ta3, ta2, tb3, tb2 = ta**3, ta**2, tb**3, tb**2
			F[0] = ax*ta3+bx*ta2+cx*ta+dx-ax1*tb3-bx1*tb2-cx1*tb-dx1
			F[1] = ay*ta3+by*ta2+cy*ta+dy-ay1*tb3-by1*tb2-cy1*tb-dy1
			F1[0][0] =  3*ax *ta2 + 2*bx *ta + cx
			F1[0][1] = -3*ax1*tb2 - 2*bx1*tb - cx1
			F1[1][0] =  3*ay *ta2 + 2*by *ta + cy
			F1[1][1] = -3*ay1*tb2 - 2*by1*tb - cy1	
			det = F1[0][0]*F1[1][1] - F1[0][1]*F1[1][0]
			if det!=0 :
				F1 = [	[ F1[1][1]/det, -F1[0][1]/det],	[-F1[1][0]/det,  F1[0][0]/det] ]
				ta = ta - ( F1[0][0]*F[0] + F1[0][1]*F[1] )
				tb = tb - ( F1[1][0]*F[0] + F1[1][1]*F[1] )
			else: break	
			i += 1

		return ta, tb 			


	def recursion(a,b, ta0,ta1,tb0,tb1, depth_a,depth_b) :
		global bezier_intersection_recursive_result
		if a==b : 
			bezier_intersection_recursive_result += [[ta0,tb0,ta1,tb1,"Overlap"]]
			return 
		tam, tbm = (ta0+ta1)/2, (tb0+tb1)/2
		if depth_a>0 and depth_b>0 : 
			a1,a2 = bez_split(a,0.5)
			b1,b2 = bez_split(b,0.5)
			if bez_bounds_intersect(a1,b1) : recursion(a1,b1, ta0,tam,tb0,tbm, depth_a-1,depth_b-1) 
			if bez_bounds_intersect(a2,b1) : recursion(a2,b1, tam,ta1,tb0,tbm, depth_a-1,depth_b-1) 
			if bez_bounds_intersect(a1,b2) : recursion(a1,b2, ta0,tam,tbm,tb1, depth_a-1,depth_b-1) 
			if bez_bounds_intersect(a2,b2) : recursion(a2,b2, tam,ta1,tbm,tb1, depth_a-1,depth_b-1) 
		elif depth_a>0  : 
			a1,a2 = bez_split(a,0.5)
			if bez_bounds_intersect(a1,b) : recursion(a1,b, ta0,tam,tb0,tb1, depth_a-1,depth_b) 
			if bez_bounds_intersect(a2,b) : recursion(a2,b, tam,ta1,tb0,tb1, depth_a-1,depth_b) 
		elif depth_b>0  : 
			b1,b2 = bez_split(b,0.5)
			if bez_bounds_intersect(a,b1) : recursion(a,b1, ta0,ta1,tb0,tbm, depth_a,depth_b-1) 
			if bez_bounds_intersect(a,b2) : recursion(a,b2, ta0,ta1,tbm,tb1, depth_a,depth_b-1) 
		else : # Both segments have been subdevided enougth. Let's get some intersections :).
			intersection, t1, t2 =  straight_segments_intersection([a[0]]+[a[3]],[b[0]]+[b[3]])
			if intersection :
				if intersection == "Overlap" :
					t1 = ( max(0,min(1,t1[0]))+max(0,min(1,t1[1])) )/2
					t2 = ( max(0,min(1,t2[0]))+max(0,min(1,t2[1])) )/2
				bezier_intersection_recursive_result += [[ta0+t1*(ta1-ta0),tb0+t2*(tb1-tb0)]]

	global bezier_intersection_recursive_result
	bezier_intersection_recursive_result = []
	recursion(a,b,0.,1.,0.,1.,intersection_recursion_depth,intersection_recursion_depth)
	intersections = bezier_intersection_recursive_result
	for i in range(len(intersections)) :			
		if len(intersections[i])<5 or intersections[i][4] != "Overlap" :
			intersections[i] = polish_intersection(a,b,intersections[i][0],intersections[i][1])
	return intersections


def csp_segments_true_intersection(sp1,sp2,sp3,sp4) :
	intersections = csp_segments_intersection(sp1,sp2,sp3,sp4)
	res = []
	for intersection in intersections :
		if  (
				(len(intersection)==5 and intersection[4] == "Overlap" and (0<=intersection[0]<=1 or 0<=intersection[1]<=1) and (0<=intersection[2]<=1 or 0<=intersection[3]<=1) ) 
			 or ( 0<=intersection[0]<=1 and 0<=intersection[1]<=1 )
			) :
			res += [intersection]
	return res


def csp_get_t_at_curvature(sp1,sp2,c, sample_points = 16):
	# returns a list containning [t1,t2,t3,...,tn],  0<=ti<=1...
	if sample_points < 2 : sample_points = 2
	tolerance = .0000000001
	res = []
	ax,ay,bx,by,cx,cy,dx,dy = csp_parameterize(sp1,sp2)
	for k in range(sample_points) :
		t = float(k)/(sample_points-1)
		i, F = 0, 1e100
		while i<2 or abs(F)>tolerance and i<17 :
			try : # some numerical calculation could exceed the limits 
				t2 = t*t
				#slopes...
				f1x = 3*ax*t2+2*bx*t+cx
				f1y = 3*ay*t2+2*by*t+cy
				f2x = 6*ax*t+2*bx
				f2y = 6*ay*t+2*by
				f3x = 6*ax
				f3y = 6*ay
				d = (f1x**2+f1y**2)**1.5
				F1 = (
						 (	(f1x*f3y-f3x*f1y)*d - (f1x*f2y-f2x*f1y)*3.*(f2x*f1x+f2y*f1y)*((f1x**2+f1y**2)**.5) )	/ 
				 				((f1x**2+f1y**2)**3)
					 )
				F = (f1x*f2y-f1y*f2x)/d - c
				t -= F/F1
			except:
				break
			i += 1
		if 0<=t<=1 and F<=tolerance:
			if len(res) == 0 :
				res.append(t)
			for i in res : 
				if abs(t-i)<=0.001 :
					break
			if not abs(t-i)<=0.001 :
				res.append(t)
	return res	
		
	
def csp_max_curvature(sp1,sp2):
	ax,ay,bx,by,cx,cy,dx,dy = csp_parameterize(sp1,sp2)
	tolerance = .0001
	F = 0.
	i = 0
	while i<2 or F-Flast<tolerance and i<10 :
		t = .5
		f1x = 3*ax*t**2 + 2*bx*t + cx
		f1y = 3*ay*t**2 + 2*by*t + cy
		f2x = 6*ax*t + 2*bx
		f2y = 6*ay*t + 2*by
		f3x = 6*ax
		f3y = 6*ay
		d = pow(f1x**2+f1y**2,1.5)
		if d != 0 :
			Flast = F
			F = (f1x*f2y-f1y*f2x)/d
			F1 = 	(
						 (	d*(f1x*f3y-f3x*f1y) - (f1x*f2y-f2x*f1y)*3.*(f2x*f1x+f2y*f1y)*pow(f1x**2+f1y**2,.5) )	/ 
				 				(f1x**2+f1y**2)**3
					)
			i+=1	
			if F1!=0:
				t -= F/F1
			else:
				break
		else: break
	return t			
	

def csp_curvature_at_t(sp1,sp2,t, depth = 3) :
	ax,ay,bx,by,cx,cy,dx,dy = bezmisc.bezierparameterize(csp_segment_to_bez(sp1,sp2))
	
	#curvature = (x'y''-y'x'') / (x'^2+y'^2)^1.5 
	
	f1x = 3*ax*t**2 + 2*bx*t + cx
	f1y = 3*ay*t**2 + 2*by*t + cy
	f2x = 6*ax*t + 2*bx
	f2y = 6*ay*t + 2*by
	d = (f1x**2+f1y**2)**1.5
	if d != 0 :
		return (f1x*f2y-f1y*f2x)/d
	else :
		t1 = f1x*f2y-f1y*f2x
		if t1 > 0 : return 1e100
		if t1 < 0 : return -1e100
		# Use the Lapitals rule to solve 0/0 problem for 2 times...
		t1 = 2*(bx*ay-ax*by)*t+(ay*cx-ax*cy)
		if t1 > 0 : return 1e100
		if t1 < 0 : return -1e100
		t1 = bx*ay-ax*by
		if t1 > 0 : return 1e100
		if t1 < 0 : return -1e100
		if depth>0 :
			# little hack ;^) hope it wont influence anything...
			return csp_curvature_at_t(sp1,sp2,t*1.004, depth-1)
		return 1e100

	 	
def csp_curvature_radius_at_t(sp1,sp2,t) :
	c = csp_curvature_at_t(sp1,sp2,t)
	if c == 0 : return 1e100 
	else: return 1/c


def csp_special_points(sp1,sp2) :
	# special points = curvature == 0
	ax,ay,bx,by,cx,cy,dx,dy = bezmisc.bezierparameterize((sp1[1],sp1[2],sp2[0],sp2[1]))	
	a = 3*ax*by-3*ay*bx
	b = 3*ax*cy-3*cx*ay
	c = bx*cy-cx*by
	roots = cubic_solver(0, a, b, c)	
	res = []
	for i in roots :
		if type(i) is complex and i.imag==0:
			i = i.real
		if type(i) is not complex and 0<=i<=1:
			res.append(i)
	return res

	
def csp_subpath_ccw(subpath):
	# Remove all zerro length segments
	s = 0
	#subpath = subpath[:]
	if (P(subpath[-1][1])-P(subpath[0][1])).l2() > 1e-10 :
		subpath[-1][2] = subpath[-1][1]
		subpath[0][0] = subpath[0][1]
		subpath += [ [subpath[0][1],subpath[0][1],subpath[0][1]]  ]
	pl = subpath[-1][2]
	for sp1 in subpath:
		for p in sp1 :
			s += (p[0]-pl[0])*(p[1]+pl[1])
			pl = p
	return s<0


def csp_at_t(sp1,sp2,t):
	ax,bx,cx,dx = sp1[1][0], sp1[2][0], sp2[0][0], sp2[1][0]
	ay,by,cy,dy = sp1[1][1], sp1[2][1], sp2[0][1], sp2[1][1]

	x1, y1 = ax+(bx-ax)*t, ay+(by-ay)*t	
	x2, y2 = bx+(cx-bx)*t, by+(cy-by)*t	
	x3, y3 = cx+(dx-cx)*t, cy+(dy-cy)*t	
	
	x4,y4 = x1+(x2-x1)*t, y1+(y2-y1)*t 
	x5,y5 = x2+(x3-x2)*t, y2+(y3-y2)*t 
	
	x,y = x4+(x5-x4)*t, y4+(y5-y4)*t 
	return [x,y]

def csp_at_length(sp1,sp2,l=0.5, tolerance = 0.01):
	bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
	t = bezmisc.beziertatlength(bez, l, tolerance)
	return csp_at_t(sp1,sp2,t)
	

def csp_splitatlength(sp1, sp2, l = 0.5, tolerance = 0.01):
	bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
	t = bezmisc.beziertatlength(bez, l, tolerance)
	return csp_split(sp1, sp2, t)	

	
def cspseglength(sp1,sp2, tolerance = 0.01):
	bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
	return bezmisc.bezierlength(bez, tolerance)	


def csplength(csp):
	total = 0
	lengths = []
	for sp in csp:
		for i in xrange(1,len(sp)):
			l = cspseglength(sp[i-1],sp[i])
			lengths.append(l)
			total += l			
	return lengths, total


def csp_segments(csp):
	l, seg = 0, [0]
	for sp in csp:
		for i in xrange(1,len(sp)):
			l += cspseglength(sp[i-1],sp[i])
			seg += [ l ] 

	if l>0 :
		seg = [seg[i]/l for i in xrange(len(seg))]
	return seg,l


def rebuild_csp (csp, segs, s=None):
	# rebuild_csp() adds to csp control points making it's segments looks like segs
	if s==None : s, l = csp_segments(csp)
	
	if len(s)>len(segs) : return None
	segs = segs[:]
	segs.sort()
	for i in xrange(len(s)):
		d = None
		for j in xrange(len(segs)):
			d = min( [abs(s[i]-segs[j]),j], d) if d!=None else [abs(s[i]-segs[j]),j]
		del segs[d[1]]
	for i in xrange(len(segs)):
		for j in xrange(0,len(s)):
			if segs[i]<s[j] : break
		if s[j]-s[j-1] != 0 :
			t = (segs[i] - s[j-1])/(s[j]-s[j-1])
			sp1,sp2,sp3 = csp_split(csp[j-1],csp[j], t)
			csp = csp[:j-1] + [sp1,sp2,sp3] + csp[j+1:]
			s = s[:j] + [ s[j-1]*(1-t)+s[j]*t   ] + s[j:]
	return csp, s


def csp_slope(sp1,sp2,t):
	bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
	return bezmisc.bezierslopeatt(bez,t)


def csp_line_intersection(l1,l2,sp1,sp2):
	dd=l1[0]
	cc=l2[0]-l1[0]
	bb=l1[1]
	aa=l2[1]-l1[1]
	if aa==cc==0 : return []
	if aa:
		coef1=cc/aa
		coef2=1
	else:
		coef1=1
		coef2=aa/cc
	bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
	ax,ay,bx,by,cx,cy,x0,y0=bezmisc.bezierparameterize(bez)
	a=coef1*ay-coef2*ax
	b=coef1*by-coef2*bx
	c=coef1*cy-coef2*cx
	d=coef1*(y0-bb)-coef2*(x0-dd)
	roots = cubic_solver(a,b,c,d)
	retval = []
	for i in roots :
		if type(i) is complex and abs(i.imag)<1e-7:
			i = i.real
		if type(i) is not complex and -1e-10<=i<=1.+1e-10:
			retval.append(i)
	return retval


def csp_split_by_two_points(sp1,sp2,t1,t2) :
	if t1>t2 : t1, t2 = t2, t1
	if t1 == t2 : 
		sp1,sp2,sp3 =  csp_split(sp1,sp2,t)
		return [sp1,sp2,sp2,sp3]
	elif t1 <= 1e-10 and t2 >= 1.-1e-10 :
		return [sp1,sp1,sp2,sp2]
	elif t1 <= 1e-10:	
		sp1,sp2,sp3 = csp_split(sp1,sp2,t2)
		return [sp1,sp1,sp2,sp3]
	elif t2 >= 1.-1e-10 : 
		sp1,sp2,sp3 = csp_split(sp1,sp2,t1)
		return [sp1,sp2,sp3,sp3]
	else:
		sp1,sp2,sp3 = csp_split(sp1,sp2,t1)
		sp2,sp3,sp4 = csp_split(sp2,sp3,(t2-t1)/(1-t1) )
		return [sp1,sp2,sp3,sp4]

def csp_seg_split(sp1,sp2, points):
	# points is float=t or list [t1, t2, ..., tn]
	if type(points) is float :
		points = [points]
	points.sort()
	res = [sp1,sp2]
	last_t = 0
	for t in points: 
		if 1e-10<t<1.-1e-10 :
			sp3,sp4,sp5 = csp_split(res[-2],res[-1], (t-last_t)/(1-last_t))
			last_t = t
			res[-2:] = [sp3,sp4,sp5]
	return res		


def csp_subpath_split_by_points(subpath, points) :
	# points are [[i,t]...] where i-segment's number
	points.sort()
	points = [[1,0.]] + points + [[len(subpath)-1,1.]]
	parts = []
	for int1,int2 in zip(points,points[1:]) : 
		if int1==int2 :
			continue
		if int1[1] == 1. :
			int1[0] += 1
			int1[1] = 0.
		if int1==int2 :
			continue
		if int2[1] == 0. :
			int2[0] -= 1
			int2[1] = 1.
		if int1[0] == 0 and int2[0]==len(subpath)-1:# and small(int1[1]) and small(int2[1]-1) :
			continue
		if int1[0]==int2[0] :	# same segment
			sp = csp_split_by_two_points(subpath[int1[0]-1],subpath[int1[0]],int1[1], int2[1])
			if sp[1]!=sp[2] :
				parts += [   [sp[1],sp[2]]     ]
		else :
			sp5,sp1,sp2 = csp_split(subpath[int1[0]-1],subpath[int1[0]],int1[1])
			sp3,sp4,sp5 = csp_split(subpath[int2[0]-1],subpath[int2[0]],int2[1])
			if int1[0]==int2[0]-1 :
				parts += [	[sp1, [sp2[0],sp2[1],sp3[2]], sp4]  ]
			else :
				parts += [  [sp1,sp2]+subpath[int1[0]+1:int2[0]-1]+[sp3,sp4]  ]
	return parts


def arc_from_s_r_n_l(s,r,n,l) :
	if abs(n[0]**2+n[1]**2 - 1) > 1e-10 : n = normalize(n)
	return arc_from_c_s_l([s[0]+n[0]*r, s[1]+n[1]*r],s,l)


def arc_from_c_s_l(c,s,l) :
	r = point_to_point_d(c,s)
	if r == 0 : return []
	alpha = l/r 
	cos_, sin_ = math.cos(alpha), math.sin(alpha)
	e = [ c[0] + (s[0]-c[0])*cos_ - (s[1]-c[1])*sin_, c[1] + (s[0]-c[0])*sin_ + (s[1]-c[1])*cos_]
	n = [c[0]-s[0],c[1]-s[1]]
	slope = rotate_cw(n) if l>0 else rotate_ccw(n)
	return csp_from_arc(s, e, c, r, slope)


def csp_from_arc(start, end, center, r, slope_st) : 
	# Creates csp that approximise specified arc
	r = abs(r)
	alpha = (atan2(end[0]-center[0],end[1]-center[1]) - atan2(start[0]-center[0],start[1]-center[1])) % math.pi2

	sectors = int(abs(alpha)*2/math.pi)+1
	alpha_start = atan2(start[0]-center[0],start[1]-center[1])
	cos_,sin_ = math.cos(alpha_start), math.sin(alpha_start)
	k = (4.*math.tan(alpha/sectors/4.)/3.)
	if dot(slope_st , [- sin_*k*r, cos_*k*r]) < 0 :
		if alpha>0 : alpha -= math.pi2
		else: alpha += math.pi2
	if abs(alpha*r)<0.001 : 
		return []

	sectors = int(abs(alpha)*2/math.pi)+1
	k = (4.*math.tan(alpha/sectors/4.)/3.)
	result = []
	for i in range(sectors+1) :
		cos_,sin_ = math.cos(alpha_start + alpha*i/sectors), math.sin(alpha_start + alpha*i/sectors)
		sp = [ [], [center[0] + cos_*r, center[1] + sin_*r], [] ]
		sp[0] = [sp[1][0] + sin_*k*r, sp[1][1] - cos_*k*r ]
		sp[2] = [sp[1][0] - sin_*k*r, sp[1][1] + cos_*k*r ]
		result += [sp]
	result[0][0] = result[0][1][:]
	result[-1][2] = result[-1][1]
		
	return result


def point_to_arc_distance(p, arc):
	###		Distance calculattion from point to arc
	P0,P2,c,a = arc
	dist = None
	p = P(p)
	r = (P0-c).mag()
	if r>0 :
		i = c + (p-c).unit()*r
		alpha = ((i-c).angle() - (P0-c).angle())
		if a*alpha<0: 
			if alpha>0:	alpha = alpha-math.pi2
			else: alpha = math.pi2+alpha
		if between(alpha,0,a) or min(abs(alpha),abs(alpha-a))<straight_tolerance : 
			return (p-i).mag(), [i.x, i.y]
		else : 
			d1, d2 = (p-P0).mag(), (p-P2).mag()
			if d1<d2 : 
				return (d1, [P0.x,P0.y])
			else :
				return (d2, [P2.x,P2.y])


def csp_to_arc_distance(sp1,sp2, arc1, arc2, tolerance = 0.01 ): # arc = [start,end,center,alpha]
	n, i = 10, 0
	d, d1, dl = (0,(0,0)), (0,(0,0)), 0
	while i<1 or (abs(d1[0]-dl[0])>tolerance and i<4):
		i += 1
		dl = d1*1	
		for j in range(n+1):
			t = float(j)/n
			p = csp_at_t(sp1,sp2,t) 
			d = min(point_to_arc_distance(p,arc1), point_to_arc_distance(p,arc2))
			d1 = max(d1,d)
		n=n*2
	return d1[0]


def csp_simple_bound_to_point_distance(p, csp):
	minx,miny,maxx,maxy = None,None,None,None
	for subpath in csp:
		for sp in subpath:
			for p_ in sp:
				minx = min(minx,p_[0]) if minx!=None else p_[0]
				miny = min(miny,p_[1]) if miny!=None else p_[1]
				maxx = max(maxx,p_[0]) if maxx!=None else p_[0]
				maxy = max(maxy,p_[1]) if maxy!=None else p_[1]
	return math.sqrt(max(minx-p[0],p[0]-maxx,0)**2+max(miny-p[1],p[1]-maxy,0)**2)


def csp_point_inside_bound(sp1, sp2, p):
	bez = [sp1[1],sp1[2],sp2[0],sp2[1]]
	x,y = p
	c = 0
	#CLT added test of x in range
	xmin=1e100
	xmax=-1e100
	for i in range(4):
		[x0,y0], [x1,y1] = bez[i-1], bez[i]
		xmin=min(xmin,x0)
		xmax=max(xmax,x0)
		if x0-x1!=0 and (y-y0)*(x1-x0)>=(x-x0)*(y1-y0) and x>min(x0,x1) and x<=max(x0,x1) :
			c +=1
	return xmin<=x<=xmax and c%2==0	


def csp_bound_to_point_distance(sp1, sp2, p):
	if csp_point_inside_bound(sp1, sp2, p) :
		return 0.
	bez = csp_segment_to_bez(sp1,sp2)
	min_dist = 1e100
	for i in range(0,4):
		d = point_to_line_segment_distance_2(p, bez[i-1],bez[i])
		if d <= min_dist : min_dist = d
	return min_dist 


def line_line_intersect(p1,p2,p3,p4) : # Return only true intersection. 
	if (p1[0]==p2[0] and p1[1]==p2[1]) or (p3[0]==p4[0] and p3[1]==p4[1]) : return False
	x = (p2[0]-p1[0])*(p4[1]-p3[1]) - (p2[1]-p1[1])*(p4[0]-p3[0])
	if x==0 : # Lines are parallel
		if (p3[0]-p1[0])*(p2[1]-p1[1]) == (p3[1]-p1[1])*(p2[0]-p1[0]) :
			if p3[0]!=p4[0] :
				t11 = (p1[0]-p3[0])/(p4[0]-p3[0]) 
				t12 = (p2[0]-p3[0])/(p4[0]-p3[0]) 
				t21 = (p3[0]-p1[0])/(p2[0]-p1[0])
				t22 = (p4[0]-p1[0])/(p2[0]-p1[0])
			else:
				t11 = (p1[1]-p3[1])/(p4[1]-p3[1]) 
				t12 = (p2[1]-p3[1])/(p4[1]-p3[1]) 
				t21 = (p3[1]-p1[1])/(p2[1]-p1[1])
				t22 = (p4[1]-p1[1])/(p2[1]-p1[1])
			return ("Overlap" if (0<=t11<=1 or 0<=t12<=1) and (0<=t21<=1 or  0<=t22<=1) else False)
		else: return False	
	else :
		return (
					0<=((p4[0]-p3[0])*(p1[1]-p3[1]) - (p4[1]-p3[1])*(p1[0]-p3[0]))/x<=1 and
					0<=((p2[0]-p1[0])*(p1[1]-p3[1]) - (p2[1]-p1[1])*(p1[0]-p3[0]))/x<=1 )
					
					
def line_line_intersection_points(p1,p2,p3,p4) : # Return only points [ (x,y) ] 
	if (p1[0]==p2[0] and p1[1]==p2[1]) or (p3[0]==p4[0] and p3[1]==p4[1]) : return []
	x = (p2[0]-p1[0])*(p4[1]-p3[1]) - (p2[1]-p1[1])*(p4[0]-p3[0])
	if x==0 : # Lines are parallel
		if (p3[0]-p1[0])*(p2[1]-p1[1]) == (p3[1]-p1[1])*(p2[0]-p1[0]) :
			if p3[0]!=p4[0] :
				t11 = (p1[0]-p3[0])/(p4[0]-p3[0]) 
				t12 = (p2[0]-p3[0])/(p4[0]-p3[0]) 
				t21 = (p3[0]-p1[0])/(p2[0]-p1[0])
				t22 = (p4[0]-p1[0])/(p2[0]-p1[0])
			else:
				t11 = (p1[1]-p3[1])/(p4[1]-p3[1]) 
				t12 = (p2[1]-p3[1])/(p4[1]-p3[1]) 
				t21 = (p3[1]-p1[1])/(p2[1]-p1[1])
				t22 = (p4[1]-p1[1])/(p2[1]-p1[1])
			res = [] 
			if (0<=t11<=1 or 0<=t12<=1) and (0<=t21<=1 or  0<=t22<=1) :
				if 0<=t11<=1 : res += [p1]	
				if 0<=t12<=1 : res += [p2]	
				if 0<=t21<=1 : res += [p3]	
				if 0<=t22<=1 : res += [p4]	
			return res
		else: return []
	else :
		t1 = ((p4[0]-p3[0])*(p1[1]-p3[1]) - (p4[1]-p3[1])*(p1[0]-p3[0]))/x
		t2 = ((p2[0]-p1[0])*(p1[1]-p3[1]) - (p2[1]-p1[1])*(p1[0]-p3[0]))/x
		if 0<=t1<=1 and 0<=t2<=1 : return [ [p1[0]*(1-t1)+p2[0]*t1, p1[1]*(1-t1)+p2[1]*t1] ]
		else : return []					


def point_to_point_d2(a,b):
	return (a[0]-b[0])**2 + (a[1]-b[1])**2


def point_to_point_d(a,b):
	return math.sqrt((a[0]-b[0])**2 + (a[1]-b[1])**2)


def point_to_line_segment_distance_2(p1, p2,p3) :
	# p1 - point, p2,p3 - line segment
	#draw_pointer(p1)
	w0 = [p1[0]-p2[0], p1[1]-p2[1]]
	v = [p3[0]-p2[0], p3[1]-p2[1]]
	c1 = w0[0]*v[0] + w0[1]*v[1]
	if c1 <= 0 :
		return w0[0]*w0[0]+w0[1]*w0[1]
	c2 = v[0]*v[0] + v[1]*v[1]
	if c2 <= c1 :
		return  (p1[0]-p3[0])**2 + (p1[1]-p3[1])**2
	return (p1[0]- p2[0]-v[0]*c1/c2)**2 + (p1[1]- p2[1]-v[1]*c1/c2)


def line_to_line_distance_2(p1,p2,p3,p4):
	if line_line_intersect(p1,p2,p3,p4) : return 0
	return min(
			point_to_line_segment_distance_2(p1,p3,p4),
			point_to_line_segment_distance_2(p2,p3,p4),
			point_to_line_segment_distance_2(p3,p1,p2),
			point_to_line_segment_distance_2(p4,p1,p2))


def csp_seg_bound_to_csp_seg_bound_max_min_distance(sp1,sp2,sp3,sp4) :
	bez1 = csp_segment_to_bez(sp1,sp2)
	bez2 = csp_segment_to_bez(sp3,sp4) 
	min_dist = 1e100
	max_dist = 0.
	for i in range(4) :
		if csp_point_inside_bound(sp1, sp2, bez2[i]) or csp_point_inside_bound(sp3, sp4, bez1[i]) :
			min_dist = 0.
			break	
	for i in range(4) : 
		for j in range(4) : 
			d = line_to_line_distance_2(bez1[i-1],bez1[i],bez2[j-1],bez2[j])
			if d < min_dist : min_dist = d
			d = (bez2[j][0]-bez1[i][0])**2 + (bez2[j][1]-bez1[i][1])**2 
			if max_dist < d  : max_dist = d
	return min_dist, max_dist


def csp_reverse(csp) :
	for i in range(len(csp)) :
	 	n = []
	 	for j in csp[i] :
			n = [  [j[2][:],j[1][:],j[0][:]]  ] + n
	 	csp[i] = n[:]
	return csp			


def csp_normalized_slope(sp1,sp2,t) :
	ax,ay,bx,by,cx,cy,dx,dy=bezmisc.bezierparameterize((sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:]))
	if sp1[1]==sp2[1]==sp1[2]==sp2[0] : return [1.,0.]
	f1x = 3*ax*t*t+2*bx*t+cx
	f1y = 3*ay*t*t+2*by*t+cy
	if abs(f1x*f1x+f1y*f1y) > 1e-9 : #LT changed this from 1e-20, which caused problems
		l = math.sqrt(f1x*f1x+f1y*f1y)
		return [f1x/l, f1y/l]

	if t == 0 :
		f1x = sp2[0][0]-sp1[1][0]
		f1y = sp2[0][1]-sp1[1][1]
		if abs(f1x*f1x+f1y*f1y) > 1e-9 : #LT changed this from 1e-20, which caused problems
			l = math.sqrt(f1x*f1x+f1y*f1y)
			return [f1x/l, f1y/l]
		else :
			f1x = sp2[1][0]-sp1[1][0]
			f1y = sp2[1][1]-sp1[1][1]
			if f1x*f1x+f1y*f1y != 0 :
				l = math.sqrt(f1x*f1x+f1y*f1y)
				return [f1x/l, f1y/l]
	elif t == 1 :
		f1x = sp2[1][0]-sp1[2][0]
		f1y = sp2[1][1]-sp1[2][1]
		if abs(f1x*f1x+f1y*f1y) > 1e-9 :
			l = math.sqrt(f1x*f1x+f1y*f1y)
			return [f1x/l, f1y/l]
		else :
			f1x = sp2[1][0]-sp1[1][0]
			f1y = sp2[1][1]-sp1[1][1]
			if f1x*f1x+f1y*f1y != 0 :
				l = math.sqrt(f1x*f1x+f1y*f1y)
				return [f1x/l, f1y/l]
	else :
		return [1.,0.]

				
def csp_normalized_normal(sp1,sp2,t) :
	nx,ny = csp_normalized_slope(sp1,sp2,t)
	return [-ny, nx]


def csp_parameterize(sp1,sp2):
	return bezmisc.bezierparameterize(csp_segment_to_bez(sp1,sp2))


def csp_concat_subpaths(*s):
	
	def concat(s1,s2) :
		if s1 == [] : return s2
		if s2 == [] : return s1
		if (s1[-1][1][0]-s2[0][1][0])**2 + (s1[-1][1][1]-s2[0][1][1])**2 > 0.00001 :
	 		return s1[:-1]+[ [s1[-1][0],s1[-1][1],s1[-1][1]],  [s2[0][1],s2[0][1],s2[0][2]] ] + s2[1:]		
	 	else :
	 		return s1[:-1]+[ [s1[-1][0],s2[0][1],s2[0][2]] ] + s2[1:]		
	 		
	if len(s) == 0 : return []
	if len(s) ==1 : return s[0]
	result = s[0]
	for s1 in s[1:]:
		result = concat(result,s1)
	return result

def csp_subpaths_end_to_start_distance2(s1,s2):
	return (s1[-1][1][0]-s2[0][1][0])**2 + (s1[-1][1][1]-s2[0][1][1])**2


def csp_clip_by_line(csp,l1,l2) :
	result = []
	for i in range(len(csp)):
		s = csp[i]
		intersections = []
		for j in range(1,len(s)) :
			intersections += [  [j,int_] for int_ in csp_line_intersection(l1,l2,s[j-1],s[j])]
		splitted_s = csp_subpath_split_by_points(s, intersections)
		for s in splitted_s[:] :
			clip = False
			for p in csp_true_bounds([s]) :
				if (l1[1]-l2[1])*p[0] + (l2[0]-l1[0])*p[1] + (l1[0]*l2[1]-l2[0]*l1[1])<-0.01 : 
					clip = True
					break
			if clip :
				splitted_s.remove(s)
		result += splitted_s
	return result


def csp_subpath_line_to(subpath, points, prepend = False) :
	# Appends subpath with line or polyline.
	if len(points)>0 :
		if not prepend : 
			if len(subpath)>0:
				subpath[-1][2] = subpath[-1][1][:]
			if type(points[0]) == type([1,1]) :
				for p in points :
					subpath += [ [p[:],p[:],p[:]] ]
			else: 
				subpath += [ [points,points,points] ]
		else : 		
			if len(subpath)>0:
				subpath[0][0] = subpath[0][1][:]
			if type(points[0]) == type([1,1]) :
				for p in points :
					subpath = [ [p[:],p[:],p[:]] ] + subpath
			else: 
				subpath = [ [points,points,points] ] + subpath
	return subpath

	
def csp_join_subpaths(csp) :
	result = csp[:]
	done_smf = True
	joined_result = []
	while done_smf :
		done_smf = False
		while len(result)>0:
			s1 = result[-1][:]
			del(result[-1])
			j = 0
			joined_smf = False
			while j<len(joined_result) :
				if csp_subpaths_end_to_start_distance2(joined_result[j],s1) <0.000001 :
					joined_result[j] = csp_concat_subpaths(joined_result[j],s1)
					done_smf = True
					joined_smf = True
					break				
				if csp_subpaths_end_to_start_distance2(s1,joined_result[j]) <0.000001 :
					joined_result[j] = csp_concat_subpaths(s1,joined_result[j])
					done_smf = True
					joined_smf = True
					break				
				j += 1
			if not joined_smf : joined_result += [s1[:]]
		if done_smf : 
			result = joined_result[:]
			joined_result = []
	return joined_result


def triangle_cross(a,b,c):
	return (a[0]-b[0])*(c[1]-b[1]) - (c[0]-b[0])*(a[1]-b[1])
	

def csp_segment_convex_hull(sp1,sp2):
	a,b,c,d = sp1[1][:], sp1[2][:], sp2[0][:], sp2[1][:]
	
	abc = triangle_cross(a,b,c)
	abd = triangle_cross(a,b,d)
	bcd = triangle_cross(b,c,d)
	cad = triangle_cross(c,a,d)
	if abc == 0 and abd == 0 : return [min(a,b,c,d), max(a,b,c,d)]
	if abc == 0 : return [d, min(a,b,c), max(a,b,c)]
	if abd == 0 : return [c, min(a,b,d), max(a,b,d)]
	if bcd == 0 : return [a, min(b,c,d), max(b,c,d)]
	if cad == 0 : return [b, min(c,a,d), max(c,a,d)]
	
	m1, m2, m3  =  abc*abd>0, abc*bcd>0, abc*cad>0
	if m1 and m2 and m3 : return [a,b,c]
	if     m1 and     m2 and not m3 : return [a,b,c,d]
	if     m1 and not m2 and     m3 : return [a,b,d,c]
	if not m1 and     m2 and     m3 : return [a,d,b,c]
	if m1 and not (m2 and m3) : return [a,b,d]
	if not (m1 and m2) and m3 : return [c,a,d]
	if not (m1 and m3) and m2 : return [b,c,d]
	
	raise ValueError, "csp_segment_convex_hull happened which is something that shouldn't happen!"	

	
################################################################################
###		Bezier additional functions
################################################################################

def bez_bounds_intersect(bez1, bez2) :
	return bounds_intersect(bez_bound(bez2), bez_bound(bez1))


def bez_bound(bez) :
	return [
				min(bez[0][0], bez[1][0], bez[2][0], bez[3][0]),
				min(bez[0][1], bez[1][1], bez[2][1], bez[3][1]),
				max(bez[0][0], bez[1][0], bez[2][0], bez[3][0]),
				max(bez[0][1], bez[1][1], bez[2][1], bez[3][1]),
			]


def bounds_intersect(a, b) :
	return not ( (a[0]>b[2]) or (b[0]>a[2]) or (a[1]>b[3]) or (b[1]>a[3]) )


def tpoint((x1,y1),(x2,y2),t):
	return [x1+t*(x2-x1),y1+t*(y2-y1)]


def bez_to_csp_segment(bez) :
	return [bez[0],bez[0],bez[1]], [bez[2],bez[3],bez[3]]


def bez_split(a,t=0.5) :
	 a1 = tpoint(a[0],a[1],t)
	 at = tpoint(a[1],a[2],t)
	 b2 = tpoint(a[2],a[3],t)
	 a2 = tpoint(a1,at,t)
	 b1 = tpoint(b2,at,t)
	 a3 = tpoint(a2,b1,t)
	 return [a[0],a1,a2,a3], [a3,b1,b2,a[3]]

	
def bez_at_t(bez,t) :
	return csp_at_t([bez[0],bez[0],bez[1]],[bez[2],bez[3],bez[3]],t)


def bez_to_point_distance(bez,p,needed_dist=[0.,1e100]):
	# returns [d^2,t]
	return csp_seg_to_point_distance(bez_to_csp_segment(bez),p,needed_dist)


def bez_normalized_slope(bez,t):
	return csp_normalized_slope([bez[0],bez[0],bez[1]], [bez[2],bez[3],bez[3]],t)	

################################################################################
###	Some vector functions
################################################################################
	
def normalize((x,y)) :
	l = math.sqrt(x**2+y**2)
	if l == 0 : return [0.,0.]
	else : 		return [x/l, y/l]


def cross(a,b) :
	return a[1] * b[0] - a[0] * b[1]


def dot(a,b) :
	return a[0] * b[0] + a[1] * b[1]


def rotate_ccw(d) :
	return [-d[1],d[0]]

def rotate_cw(d) :
	return [d[1],-d[0]]


def vectors_ccw(a,b):
	return a[0]*b[1]-b[0]*a[1] < 0

def vector_add(a,b) :
	return [a[0]+b[0],a[1]+b[1]]

def vector_mul(a,b) :
	return [a[0]*b,a[1]*b]


def vector_from_to_length(a,b):
	return math.sqrt((a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1]))

################################################################################
###	Common functions
################################################################################

def matrix_mul(a,b) :
	return [ [ sum([a[i][k]*b[k][j] for k in range(len(a[0])) ])   for j in range(len(b[0]))]   for i in range(len(a))] 
	try :
		return [ [ sum([a[i][k]*b[k][j] for k in range(len(a[0])) ])   for j in range(len(b[0]))]   for i in range(len(a))] 
	except :
		return None


def transpose(a) :
	try :
		return [ [ a[i][j] for i in range(len(a)) ] for j in range(len(a[0])) ]
	except :
		return None


def det_3x3(a):
	return  float(
		a[0][0]*a[1][1]*a[2][2] + a[0][1]*a[1][2]*a[2][0] + a[1][0]*a[2][1]*a[0][2]
		- a[0][2]*a[1][1]*a[2][0] - a[0][0]*a[2][1]*a[1][2] - a[0][1]*a[2][2]*a[1][0]
		)


def inv_3x3(a): # invert matrix 3x3
	det = det_3x3(a)
	if det==0: return None
	return	[  
		[  (a[1][1]*a[2][2] - a[2][1]*a[1][2])/det,  -(a[0][1]*a[2][2] - a[2][1]*a[0][2])/det,  (a[0][1]*a[1][2] - a[1][1]*a[0][2])/det ], 
		[ -(a[1][0]*a[2][2] - a[2][0]*a[1][2])/det,   (a[0][0]*a[2][2] - a[2][0]*a[0][2])/det, -(a[0][0]*a[1][2] - a[1][0]*a[0][2])/det ], 
		[  (a[1][0]*a[2][1] - a[2][0]*a[1][1])/det,  -(a[0][0]*a[2][1] - a[2][0]*a[0][1])/det,  (a[0][0]*a[1][1] - a[1][0]*a[0][1])/det ]
	]


def inv_2x2(a): # invert matrix 2x2
	det = a[0][0]*a[1][1] - a[1][0]*a[0][1]
	if det==0: return None
	return [
			[a[1][1]/det, -a[0][1]/det],
			[-a[1][0]/det, a[0][0]/det]
			]


def small(a) :
	global small_tolerance
	return abs(a)<small_tolerance

					
def atan2(*arg):	
	if len(arg)==1 and ( type(arg[0]) == type([0.,0.]) or type(arg[0])==type((0.,0.)) ) :
		return (math.pi/2 - math.atan2(arg[0][0], arg[0][1]) ) % math.pi2
	elif len(arg)==2 :
		
		return (math.pi/2 - math.atan2(arg[0],arg[1]) ) % math.pi2
	else :
		raise ValueError, "Bad argumets for atan! (%s)" % arg  

def get_text(node) :
	value = None
	if node.text!=None : value = value +"\n" + node.text if value != None else node.text
	for k in node :
		if k.tag == inkex.addNS('tspan','svg'):
			if k.text!=None : value = value +"\n" + k.text if value != None else k.text
	return value



def draw_text(text,x,y, group = None, style = None, font_size = 10, gcodetools_tag = None) :
	if style == None : 
		style = "font-family:DejaVu Sans;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:DejaVu Sans;fill:#000000;fill-opacity:1;stroke:none;"
	style += "font-size:%fpx;"%font_size
	attributes = {			'x':	str(x),
							inkex.addNS("space","xml"):"preserve",
							'y':	str(y),
							'style' : style
						}
	if gcodetools_tag!=None : 
		attributes["gcodetools"] = str(gcodetools_tag)
 
	if group == None:
		group = options.doc_root

	t = inkex.etree.SubElement(	group, inkex.addNS('text','svg'), attributes)
	text = str(text).split("\n")
	for s in text :
		span = inkex.etree.SubElement( t, inkex.addNS('tspan','svg'), 
						{
							'x':	str(x),
							'y':	str(y),
							inkex.addNS("role","sodipodi"):"line",
						})					
		y += font_size
		span.text = str(s)

def draw_csp(csp, stroke = "#f00", fill = "none", comment = "", width = 0.354, group = None, style = None, gcodetools_tag = None) :
	if style == None : 
		style = "fill:%s;fill-opacity:1;stroke:%s;stroke-width:%s"%(fill,stroke,width)
	attributes = {			'd':	cubicsuperpath.formatPath(csp),
							'style' : style
				}
	if comment != '':
		attributes['comment'] = comment
	if group == None :
		group = options.doc_root
		
	return inkex.etree.SubElement( group, inkex.addNS('path','svg'), attributes) 
	
def draw_pointer(x,color = "#f00", figure = "cross", group = None, comment = "", fill=None, width = .1, size = 10., text = None, font_size=None, pointer_type=None, attrib = None) :
	size = size/2
	if attrib == None : attrib = {}
	if pointer_type == None: 
		pointer_type = "Pointer"
	attrib["gcodetools"] = pointer_type
	if group == None:
		group = options.self.current_layer
	if text != None	:
		if font_size == None : font_size = 7
		group = inkex.etree.SubElement( group, inkex.addNS('g','svg'), {"gcodetools": pointer_type+" group"} )		
		draw_text(text,x[0]+size*2.2,x[1]-size, group = group, font_size = font_size) 
	if figure  == "line" :
		s = ""
		for i in range(1,len(x)/2) :
			s+= " %s, %s " %(x[i*2],x[i*2+1])
		attrib.update({"d": "M %s,%s L %s"%(x[0],x[1],s), "style":"fill:none;stroke:%s;stroke-width:%f;"%(color,width),"comment":str(comment)})			
		inkex.etree.SubElement( group, inkex.addNS('path','svg'), attrib)
	elif figure == "arrow" :
		if fill == None : fill = "#12b3ff"
		fill_opacity = "0.8" 
		d = "m %s,%s " % (x[0],x[1]) + re.sub("([0-9\-.e]+)",(lambda match: str(float(match.group(1))*size*2.)), "0.88464,-0.40404 c -0.0987,-0.0162 -0.186549,-0.0589 -0.26147,-0.1173 l 0.357342,-0.35625 c 0.04631,-0.039 0.0031,-0.13174 -0.05665,-0.12164 -0.0029,-1.4e-4 -0.0058,-1.4e-4 -0.0087,0 l -2.2e-5,2e-5 c -0.01189,0.004 -0.02257,0.0119 -0.0305,0.0217 l -0.357342,0.35625 c -0.05818,-0.0743 -0.102813,-0.16338 -0.117662,-0.26067 l -0.409636,0.88193 z")
		attrib.update({"d": d, "style":"fill:%s;stroke:none;fill-opacity:%s;"%(fill,fill_opacity),"comment":str(comment)})			
		inkex.etree.SubElement( group, inkex.addNS('path','svg'), attrib)
	else :
		attrib.update({"d": "m %s,%s l %f,%f %f,%f %f,%f %f,%f , %f,%f"%(x[0],x[1], size,size, -2*size,-2*size, size,size, size,-size, -2*size,2*size ), "style":"fill:none;stroke:%s;stroke-width:%f;"%(color,width),"comment":str(comment)})
		inkex.etree.SubElement( group, inkex.addNS('path','svg'), attrib)


def straight_segments_intersection(a,b, true_intersection = True) : # (True intersection means check ta and tb are in [0,1])
 	ax,bx,cx,dx, ay,by,cy,dy = a[0][0],a[1][0],b[0][0],b[1][0], a[0][1],a[1][1],b[0][1],b[1][1] 
	if (ax==bx and ay==by) or (cx==dx and cy==dy) : return False, 0, 0
	if (bx-ax)*(dy-cy)-(by-ay)*(dx-cx)==0 :	# Lines are parallel
		ta = (ax-cx)/(dx-cx) if cx!=dx else (ay-cy)/(dy-cy)
		tb = (bx-cx)/(dx-cx) if cx!=dx else (by-cy)/(dy-cy)
		tc = (cx-ax)/(bx-ax) if ax!=bx else (cy-ay)/(by-ay)
		td = (dx-ax)/(bx-ax) if ax!=bx else (dy-ay)/(by-ay)
		return ("Overlap" if 0<=ta<=1 or 0<=tb<=1 or  0<=tc<=1 or  0<=td<=1 or not true_intersection else False), (ta,tb), (tc,td)
	else :
		ta = ( (ay-cy)*(dx-cx)-(ax-cx)*(dy-cy) ) / ( (bx-ax)*(dy-cy)-(by-ay)*(dx-cx) )
		tb = ( ax-cx+ta*(bx-ax) ) / (dx-cx) if dx!=cx else ( ay-cy+ta*(by-ay) ) / (dy-cy)
		return (0<=ta<=1 and 0<=tb<=1 or not true_intersection), ta, tb
	
	

def isnan(x): return type(x) is float and x != x

def isinf(x): inf = 1e5000; return x == inf or x == -inf

def between(c,x,y):
		return x-straight_tolerance<=c<=y+straight_tolerance or y-straight_tolerance<=c<=x+straight_tolerance

def cubic_solver_real(a,b,c,d):
	# returns only real roots of a cubic equation.
	roots = cubic_solver(a,b,c,d)
	res = []
	for root in roots :
		if type(root) is complex :	
			if -1e-10<root.imag<1e-10 :
				res.append(root.real)
		else :
			res.append(root)
	return res 
	
	
def cubic_solver(a,b,c,d):	
	if a!=0:
		#	Monics formula see http://en.wikipedia.org/wiki/Cubic_function#Monic_formula_of_roots
		a,b,c = (b/a, c/a, d/a)
		m = 2*a**3 - 9*a*b + 27*c
		k = a**2 - 3*b
		n = m**2 - 4*k**3
		w1 = -.5 + .5*cmath.sqrt(3)*1j
		w2 = -.5 - .5*cmath.sqrt(3)*1j
		if n>=0 :
			t = m+math.sqrt(n)
			m1 = pow(t/2,1./3) if t>=0 else -pow(-t/2,1./3)
			t = m-math.sqrt(n)
			n1 = pow(t/2,1./3) if t>=0 else -pow(-t/2,1./3)
		else :
			m1 = pow(complex((m+cmath.sqrt(n))/2),1./3)
			n1 = pow(complex((m-cmath.sqrt(n))/2),1./3)
		x1 = -1./3 * (a + m1 + n1)
		x2 = -1./3 * (a + w1*m1 + w2*n1)
		x3 = -1./3 * (a + w2*m1 + w1*n1)
		return [x1,x2,x3]
	elif b!=0:
		det = c**2-4*b*d
		if det>0 :
			return [(-c+math.sqrt(det))/(2*b),(-c-math.sqrt(det))/(2*b)]
		elif d == 0 :
			return [-c/(b*b)] 	
		else :
			return [(-c+cmath.sqrt(det))/(2*b),(-c-cmath.sqrt(det))/(2*b)]
	elif c!=0 :
		return [-d/c]
	else : return []


################################################################################
###		print_ prints any arguments into specified log file
################################################################################

def print_(*arg):
	f = open(options.log_filename,"a")
	for s in arg :
		s = str(unicode(s).encode('unicode_escape'))+" "
		f.write( s )
	f.write("\n")
	f.close()


################################################################################
###		Point (x,y) operations
################################################################################
class P:
	def __init__(self, x, y=None):
		if not y==None:
			self.x, self.y = float(x), float(y)
		else:
			self.x, self.y = float(x[0]), float(x[1])
	def __add__(self, other): return P(self.x + other.x, self.y + other.y)
	def __sub__(self, other): return P(self.x - other.x, self.y - other.y)
	def __neg__(self): return P(-self.x, -self.y)
	def __mul__(self, other):
		if isinstance(other, P):
			return self.x * other.x + self.y * other.y
		return P(self.x * other, self.y * other)
	__rmul__ = __mul__
	def __div__(self, other): return P(self.x / other, self.y / other)
	def mag(self): return math.hypot(self.x, self.y)
	def unit(self):
		h = self.mag()
		if h: return self / h
		else: return P(0,0)
	def dot(self, other): return self.x * other.x + self.y * other.y
	def rot(self, theta):
		c = math.cos(theta)
		s = math.sin(theta)
		return P(self.x * c - self.y * s,  self.x * s + self.y * c)
	def angle(self): return math.atan2(self.y, self.x)
	def __repr__(self): return '%f,%f' % (self.x, self.y)
	def pr(self): return "%.2f,%.2f" % (self.x, self.y)
	def to_list(self): return [self.x, self.y]	
	def ccw(self): return P(-self.y,self.x)
	def l2(self): return self.x*self.x + self.y*self.y


class Arc():
	def __init__(self,st,end,c,a):
		self.st = P(st)
		self.end = P(end)
		self.c = P(c)
		self.r = (P(st)-P(c)).mag()
		self.a = ( (self.st-self.c).angle() - (self.end-self.c).angle() ) % math.pi2
		if a<0 : self.a -= math.pi2

	def offset(self, r):
		if self.a>0 :
			r += self.r
		else :
			r = self.r - r
		
		if self.r != 0 :
			self.st = self.c + (self.st-self.c)*r/self.r
			self.end = self.c + (self.end-self.c)*r/self.r
			self.r = r
	
	def length(self):
		return abs(self.a*self.r)
	

	def draw(self, group, style, layer, transform, num = 0, reverse_angle = 1):
		st = P(gcodetools.transform(self.st.to_list(), layer, True))
		c = P(gcodetools.transform(self.c.to_list(), layer, True))
		a = self.a * reverse_angle
		r = (st-c)
		a_st = (math.atan2(r.x,-r.y) - math.pi/2) % (math.pi*2)
		r = r.mag()
		if a<0:
			a_end = a_st+a
			style = style['biarc%s'%(num%2)]
		else: 
			a_end = a_st
			a_st = a_st+a
			style = style['biarc%s_r'%(num%2)]
		
		attr = {
				'style': style,
				 inkex.addNS('cx','sodipodi'):		str(c.x),
				 inkex.addNS('cy','sodipodi'):		str(c.y),
				 inkex.addNS('rx','sodipodi'):		str(r),
				 inkex.addNS('ry','sodipodi'):		str(r),
				 inkex.addNS('start','sodipodi'):	str(a_st),
				 inkex.addNS('end','sodipodi'):		str(a_end),
				 inkex.addNS('open','sodipodi'):	'true',
				 inkex.addNS('type','sodipodi'):	'arc',
				 "gcodetools": "Preview",
				}	
		if transform != [] :
			attr["transform"] = transform	
		inkex.etree.SubElement(	group, inkex.addNS('path','svg'), attr)
		
	def intersect(self,b) :
		return []


class Line():
	def __init__(self,st,end):
		if st.__class__ == P :
			st = st.to_list()
		if end.__class__ == P :
			end = end.to_list()
		self.st = P(st)
		self.end = P(end)
		self.l = self.length() 
		if self.l != 0 :
			self.n = ((self.end-self.st)/self.l).ccw()
		else: 
			self.n = [0,1]
				
	def offset(self, r):
		self.st -= self.n*r
		self.end -= self.n*r
		
	def l2(self): return (self.st-self.end).l2()
	def length(self): return (self.st-self.end).mag()
	
	def draw(self, group, style, layer, transform, num = 0, reverse_angle = 1):
		st = gcodetools.transform(self.st.to_list(), layer, True)
		end = gcodetools.transform(self.end.to_list(), layer, True)


		attr = {	'style': style['line'],
					'd':'M %s,%s L %s,%s' % (st[0],st[1],end[0],end[1]),
					"gcodetools": "Preview",
				}
		if transform != [] :
			attr["transform"] = transform		
		inkex.etree.SubElement(	group, inkex.addNS('path','svg'),  attr	)
	
	def intersect(self,b) :
		if b.__class__ == Line :
			if self.l < 10e-8 or b.l < 10e-8 : return []
			v1 = self.end - self.st
			v2 = b.end - b.st
			x = v1.x*v2.y - v2.x*v1.y 
			if x == 0 :
				# lines are parallel
				res = []

				if (self.st.x-b.st.x)*v1.y - (self.st.y-b.st.y)*v1.x  == 0:
					# lines are the same
					if v1.x != 0 :
						if 0<=(self.st.x-b.st.x)/v2.x<=1 :  res.append(self.st)
						if 0<=(self.end.x-b.st.x)/v2.x<=1 :  res.append(self.end)
						if 0<=(b.st.x-self.st.x)/v1.x<=1 :  res.append(b.st)
						if 0<=(b.end.x-b.st.x)/v1.x<=1 :  res.append(b.end)
					else :
						if 0<=(self.st.y-b.st.y)/v2.y<=1 :  res.append(self.st)
						if 0<=(self.end.y-b.st.y)/v2.y<=1 :  res.append(self.end)
						if 0<=(b.st.y-self.st.y)/v1.y<=1 :  res.append(b.st)
						if 0<=(b.end.y-b.st.y)/v1.y<=1 :  res.append(b.end)
				return res
			else :
				t1 = ( -v1.x*(b.end.y-self.end.y) + v1.y*(b.end.x-self.end.x) ) / x
				t2 = ( -v1.y*(self.st.x-b.st.x) + v1.x*(self.st.y-b.st.y) ) / x

				gcodetools.error((x,t1,t2), "warning")
				if 0<=t1<=1 and 0<=t2<=1 : return [ self.st+v1*t1 ]
				else : return []					
		else: return []	
			
			
	
	
class Biarc:
	def __init__(self, items=None):
		if items == None :
			self.items = []
		else: 	
			self.items = items
			
	def l(self) : 
		return sum([i.length() for i in items])
	
	def close(self) :
		for subitems in self.items:
			if (subitems[0].st-subitems[-1].end).l2()>10e-16 :
				subitems.append(Line(subitems[-1].end,subitems[0].st))
	
	def offset(self,r) :
		# offset each element
		self.close()
		for subitems in self.items :
			for item in subitems :
				item.offset(r)
		self.connect(r)
		
	def connect(self, r) :				
		for subitems in self.items :
			for a,b in zip(subitems, subitems[1:]) :
				i = a.intersect(b)
				for p in i : 
					draw_pointer(p.to_list())
				
						
				
						
	def clip_offset(self):
		pass	
		
	def draw(self, layer, group=None, style=styles["biarc_style"]):
		global gcodetools
		gcodetools.set_markers()

		for i in [0,1]:
			style['biarc%s_r'%i] = simplestyle.parseStyle(style['biarc%s'%i])
			style['biarc%s_r'%i]["marker-start"] = "url(#DrawCurveMarker_r)"
			del(style['biarc%s_r'%i]["marker-end"])
			style['biarc%s_r'%i] = simplestyle.formatStyle(style['biarc%s_r'%i])
		
		if group==None:
			if "preview_groups" not in dir(options.self) :
				gcodetools.preview_groups = { layer: inkex.etree.SubElement( gcodetools.layers[min(1,len(gcodetools.layers)-1)], inkex.addNS('g','svg'), {"gcodetools": "Preview group"} ) }
			elif layer not in gcodetools.preview_groups :
				gcodetools.preview_groups[layer] = inkex.etree.SubElement( gcodetools.layers[min(1,len(gcodetools.layers)-1)], inkex.addNS('g','svg'), {"gcodetools": "Preview group"} )
			group = gcodetools.preview_groups[layer]
		
		transform = gcodetools.get_transforms(group)
		if transform != [] : 
			transform = gcodetools.reverse_transform(transform)
			transform = simpletransform.formatTransform(transform)

		a,b,c = [0.,0.], [1.,0.], [0.,1.]
		k = (b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1])
		a,b,c = gcodetools.transform(a, layer, True), gcodetools.transform(b, layer, True), gcodetools.transform(c, layer, True)
		if ((b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1]))*k > 0 : reverse_angle = -1
		else : reverse_angle = 1 


		num = 0
		for subitems in self.items :
			for item in subitems :
				num += 1
				#if num>1 : break
				item.draw(group, style, layer, transform, num, reverse_angle)

	def from_old_style(self, curve) :
		#Crve defenitnion [start point, type = {'arc','line','move','end'}, arc center, arc angle, end point, [zstart, zend]]		
		self.items = []
		for sp in curve:
			print_(sp)
			if sp[1] == 'move':				
				self.items.append([])
			if sp[1] == 'arc':
				self.items[-1].append(Arc(sp[0],sp[4],sp[2],sp[3]))
			if sp[1] == 'line':
				self.items[-1].append(Line(sp[0],sp[4]))
		
			
		
			
	
################################################################################
###
### Offset function 
###
### This function offsets given cubic super path.  
### It's based on src/livarot/PathOutline.cpp from Inkscape's source code.
###
###
################################################################################
def csp_offset(csp, r) :
	offset_tolerance = 0.05
	offset_subdivision_depth = 10
	time_ = time.time()
	time_start  = time_
	print_("Offset start at %s"% time_)
	print_("Offset radius %s"% r)
	
	
	def csp_offset_segment(sp1,sp2,r) :
		result = []
		t = csp_get_t_at_curvature(sp1,sp2,1/r)
		if len(t) == 0 : t =[0.,1.]
		t.sort()
		if t[0]>.00000001 : t = [0.]+t 
		if t[-1]<.99999999 : t.append(1.) 
		for st,end in zip(t,t[1:]) :	
			c = csp_curvature_at_t(sp1,sp2,(st+end)/2)
			sp = csp_split_by_two_points(sp1,sp2,st,end)
			if sp[1]!=sp[2]:
				if (c>1/r and r<0 or c<1/r and r>0) :
					offset = offset_segment_recursion(sp[1],sp[2],r, offset_subdivision_depth, offset_tolerance) 
				else : # This part will be clipped for sure... TODO Optimize it...
					offset = offset_segment_recursion(sp[1],sp[2],r, offset_subdivision_depth, offset_tolerance) 
					
				if result==[] :
					result = offset[:]
				else: 
					if csp_subpaths_end_to_start_distance2(result,offset)<0.0001 :
						result = csp_concat_subpaths(result,offset)
					else:
						
						intersection = csp_get_subapths_last_first_intersection(result,offset)
						if intersection != [] :
							i,t1,j,t2 = intersection
							sp1_,sp2_,sp3_ = csp_split(result[i-1],result[i],t1)
							result = result[:i-1] + [ sp1_, sp2_ ]
							sp1_,sp2_,sp3_ = csp_split(offset[j-1],offset[j],t2)
							result = csp_concat_subpaths( result, [sp2_,sp3_] + offset[j+1:] )
						else : 
							pass # ???						
							#raise ValueError, "Offset curvature clipping error"
		#draw_csp([result])							
		return result					
	
						
	def create_offset_segment(sp1,sp2,r) :
		# See	Gernot Hoffmann "Bezier Curves"  p.34 -> 7.1 Bezier Offset Curves
		p0,p1,p2,p3 = P(sp1[1]),P(sp1[2]),P(sp2[0]),P(sp2[1])
		s0,s1,s3 = p1-p0,p2-p1,p3-p2
		n0 = s0.ccw().unit() if s0.l2()!=0 else P(csp_normalized_normal(sp1,sp2,0))
		n3 = s3.ccw().unit() if s3.l2()!=0 else P(csp_normalized_normal(sp1,sp2,1))
		n1 = s1.ccw().unit() if s1.l2()!=0 else (n0.unit()+n3.unit()).unit()

		q0,q3 = p0+r*n0, p3+r*n3
		c = csp_curvature_at_t(sp1,sp2,0)
		q1 = q0 + (p1-p0)*(1- (r*c if abs(c)<100 else 0) )
		c = csp_curvature_at_t(sp1,sp2,1)
		q2 = q3 + (p2-p3)*(1- (r*c if abs(c)<100 else 0) ) 

		
		return [[q0.to_list(), q0.to_list(), q1.to_list()],[q2.to_list(), q3.to_list(), q3.to_list()]]
	
	
	def csp_get_subapths_last_first_intersection(s1,s2):
		_break = False
		for i in range(1,len(s1)) :
			sp11, sp12 = s1[-i-1], s1[-i]
			for j in range(1,len(s2)) :
				sp21,sp22 = s2[j-1], s2[j] 
				intersection = csp_segments_true_intersection(sp11,sp12,sp21,sp22)
				if intersection != [] :
					_break = True
					break 
			if _break:break
		if _break :
			intersection = max(intersection)
			return [len(s1)-i,intersection[0], j,intersection[1]]
		else : 
			return []
	
		
	def csp_join_offsets(prev,next,sp1,sp2,sp1_l,sp2_l,r):
		if len(next)>1 :
			if (P(prev[-1][1])-P(next[0][1])).l2()<0.001 : 
				return prev,[],next
			intersection = csp_get_subapths_last_first_intersection(prev,next)
			if intersection != [] :
				i,t1,j,t2 = intersection
				sp1_,sp2_,sp3_ = csp_split(prev[i-1],prev[i],t1)
				sp3_,sp4_,sp5_ = csp_split(next[j-1], next[j],t2)
				return prev[:i-1] + [ sp1_, sp2_ ], [], [sp4_,sp5_] + next[j+1:] 
			
		# Offsets do not intersect... will add an arc...
		start = (P(csp_at_t(sp1_l,sp2_l,1.)) + r*P(csp_normalized_normal(sp1_l,sp2_l,1.))).to_list()
		end   = (P(csp_at_t(sp1,sp2,0.)) + r*P(csp_normalized_normal(sp1,sp2,0.))).to_list()
		arc = csp_from_arc(start, end, sp1[1], r, csp_normalized_slope(sp1_l,sp2_l,1.) )
		if arc == [] :
			return prev,[],next
		else:
			# Clip prev by arc
			if csp_subpaths_end_to_start_distance2(prev,arc)>0.00001 :
				intersection = csp_get_subapths_last_first_intersection(prev,arc)
				if intersection != [] :
					i,t1,j,t2 = intersection
					sp1_,sp2_,sp3_ = csp_split(prev[i-1],prev[i],t1)
					sp3_,sp4_,sp5_ = csp_split(arc[j-1],arc[j],t2)
					prev = prev[:i-1] + [ sp1_, sp2_ ]
					arc = [sp4_,sp5_] + arc[j+1:] 
				#else : raise ValueError, "Offset curvature clipping error"
			# Clip next by arc
			if next == [] :
				return prev,[],arc
			if csp_subpaths_end_to_start_distance2(arc,next)>0.00001 :
				intersection = csp_get_subapths_last_first_intersection(arc,next)
				if intersection != [] :
					i,t1,j,t2 = intersection
					sp1_,sp2_,sp3_ = csp_split(arc[i-1],arc[i],t1)
					sp3_,sp4_,sp5_ = csp_split(next[j-1],next[j],t2)
					arc = arc[:i-1] + [ sp1_, sp2_ ]
					next = [sp4_,sp5_] + next[j+1:] 
				#else : raise ValueError, "Offset curvature clipping error"

			return prev,arc,next
		
		
	def offset_segment_recursion(sp1,sp2,r, depth, tolerance) :
		sp1_r,sp2_r = create_offset_segment(sp1,sp2,r)
		err = max(
				csp_seg_to_point_distance(sp1_r,sp2_r, (P(csp_at_t(sp1,sp2,.25)) + P(csp_normalized_normal(sp1,sp2,.25))*r).to_list())[0], 
				csp_seg_to_point_distance(sp1_r,sp2_r, (P(csp_at_t(sp1,sp2,.50)) + P(csp_normalized_normal(sp1,sp2,.50))*r).to_list())[0],
				csp_seg_to_point_distance(sp1_r,sp2_r, (P(csp_at_t(sp1,sp2,.75)) + P(csp_normalized_normal(sp1,sp2,.75))*r).to_list())[0],
				)

		if  err>tolerance**2 and depth>0:
			#print_(csp_seg_to_point_distance(sp1_r,sp2_r, (P(csp_at_t(sp1,sp2,.25)) + P(csp_normalized_normal(sp1,sp2,.25))*r).to_list())[0], tolerance)
			if depth > offset_subdivision_depth-2 :
				t = csp_max_curvature(sp1,sp2)
				t = max(.1,min(.9 ,t))
			else :
				t = .5
			sp3,sp4,sp5 = csp_split(sp1,sp2,t)
			r1 = offset_segment_recursion(sp3,sp4,r, depth-1, tolerance)
			r2 = offset_segment_recursion(sp4,sp5,r, depth-1, tolerance)
			return r1[:-1]+ [[r1[-1][0],r1[-1][1],r2[0][2]]] + r2[1:]
		else :
			#draw_csp([[sp1_r,sp2_r]])
			#draw_pointer(sp1[1]+sp1_r[1], "#057", "line")
			#draw_pointer(sp2[1]+sp2_r[1], "#705", "line")
			return [sp1_r,sp2_r]
		

	############################################################################
	# Some small definitions
	############################################################################
	csp_len = len(csp)

	############################################################################
	# Prepare the path
	############################################################################
	# Remove all small segments (segment length < 0.001)

	for i in xrange(len(csp)) :
		for j in xrange(len(csp[i])) :
			sp = csp[i][j]
			if (P(sp[1])-P(sp[0])).mag() < 0.001 :
				csp[i][j][0] = sp[1]
			if (P(sp[2])-P(sp[0])).mag() < 0.001 :
				csp[i][j][2] = sp[1]
	for i in xrange(len(csp)) :
		for j in xrange(1,len(csp[i])) :
			if cspseglength(csp[i][j-1], csp[i][j])<0.001 : 
				csp[i] = csp[i][:j] + csp[i][j+1:]
		if cspseglength(csp[i][-1],csp[i][0])>0.001 : 
			csp[i][-1][2] = csp[i][-1][1]
			csp[i]+= [ [csp[i][0][1],csp[i][0][1],csp[i][0][1]] ]
	
	# TODO Get rid of self intersections.

	original_csp = csp[:]
	# Clip segments which has curvature>1/r. Because their offset will be selfintersecting and very nasty.
					
	print_("Offset prepared the path in %s"%(time.time()-time_))
	print_("Path length = %s"% sum([len(i)for i in csp] ) )
	time_ = time.time()

	############################################################################
	# Offset
	############################################################################
	# Create offsets for all segments in the path. And join them together inside each subpath. 		
	unclipped_offset = [[] for i in xrange(csp_len)]
	offsets_original = [[] for i in xrange(csp_len)]
	join_points = [[] for i in xrange(csp_len)]
	intersection = [[] for i in xrange(csp_len)]
	for i in xrange(csp_len) :
		subpath = csp[i]
		subpath_offset = []
		last_offset_len = 0
		for sp1,sp2 in zip(subpath, subpath[1:]) : 
			segment_offset = csp_offset_segment(sp1,sp2,r)
			if subpath_offset == [] :
				subpath_offset = segment_offset
				
				prev_l = len(subpath_offset)
			else : 
				prev, arc, next = csp_join_offsets(subpath_offset[-prev_l:],segment_offset,sp1,sp2,sp1_l,sp2_l,r)
				#draw_csp([prev],"Blue")
				#draw_csp([arc],"Magenta")
				subpath_offset = csp_concat_subpaths(subpath_offset[:-prev_l+1],prev,arc,next)
				prev_l = len(next)				
			sp1_l, sp2_l = sp1[:], sp2[:]
						
		# Join last and first offsets togother to close the curve
		
		prev, arc, next = csp_join_offsets(subpath_offset[-prev_l:], subpath_offset[:2], subpath[0], subpath[1], sp1_l,sp2_l, r)
		subpath_offset[:2] = next[:]
		subpath_offset = csp_concat_subpaths(subpath_offset[:-prev_l+1],prev,arc)
		#draw_csp([prev],"Blue")
		#draw_csp([arc],"Red")
		#draw_csp([next],"Red")

		# Collect subpath's offset and save it to unclipped offset list. 	
		unclipped_offset[i] = subpath_offset[:]	

		#for k,t in intersection[i]:
		#	draw_pointer(csp_at_t(subpath_offset[k-1], subpath_offset[k], t))
			
	#inkex.etree.SubElement( options.doc_root, inkex.addNS('path','svg'), {"d": cubicsuperpath.formatPath(unclipped_offset), "style":"fill:none;stroke:#0f0;"} )	
	print_("Offsetted path in %s"%(time.time()-time_))
	time_ = time.time()
	
	#for i in range(len(unclipped_offset)):
	#	draw_csp([unclipped_offset[i]], color = ["Green","Red","Blue"][i%3], width = .1)
	#return []
	############################################################################
	# Now to the clipping. 
	############################################################################
	# First of all find all intersection's between all segments of all offseted subpaths, including self intersections.

	#TODO define offset tolerance here
	global small_tolerance
	small_tolerance = 0.01
	summ = 0
	summ1 = 0 	 
	for subpath_i in xrange(csp_len) :
		for subpath_j in xrange(subpath_i,csp_len) :
			subpath = unclipped_offset[subpath_i]
			subpath1 = unclipped_offset[subpath_j]
			for i in xrange(1,len(subpath)) :
				# If subpath_i==subpath_j we are looking for self intersections, so 
				# we'll need search intersections only for xrange(i,len(subpath1))
				for j in ( xrange(i,len(subpath1)) if subpath_i==subpath_j else xrange(len(subpath1))) :
					if subpath_i==subpath_j and j==i :
						# Find self intersections of a segment
						sp1,sp2,sp3 = csp_split(subpath[i-1],subpath[i],.5)
						intersections = csp_segments_intersection(sp1,sp2,sp2,sp3)
						summ +=1 
						for t in intersections :
							summ1 += 1
							if not ( small(t[0]-1) and small(t[1]) ) and 0<=t[0]<=1 and 0<=t[1]<=1 :
								intersection[subpath_i] += [ [i,t[0]/2],[j,t[1]/2+.5] ]
					else :
						intersections = csp_segments_intersection(subpath[i-1],subpath[i],subpath1[j-1],subpath1[j])
						summ +=1 
						for t in intersections :
							summ1 += 1
							#TODO tolerance dependence to cpsp_length(t)
							if len(t) == 2 and 0<=t[0]<=1 and 0<=t[1]<=1 and not (
									subpath_i==subpath_j and (
									(j-i-1) % (len(subpath)-1) == 0 and small(t[0]-1) and small(t[1]) or 
									(i-j-1) % (len(subpath)-1) == 0 and small(t[1]-1) and small(t[0]) )  ) :
								intersection[subpath_i] += [ [i,t[0]] ]
								intersection[subpath_j] += [ [j,t[1]] ]	
								#draw_pointer(csp_at_t(subpath[i-1],subpath[i],t[0]),"#f00")
								#print_(t)
								#print_(i,j)
							elif len(t)==5 and t[4]=="Overlap":	
								intersection[subpath_i] += [ [i,t[0]], [i,t[1]] ]
								intersection[subpath_j] += [ [j,t[1]], [j,t[3]] ]

	print_("Intersections found in %s"%(time.time()-time_))
	print_("Examined %s segments"%(summ))
	print_("found %s intersections"%(summ1))
	time_ = time.time()
								
	########################################################################
	# Split unclipped offset by intersection points into splitted_offset
	########################################################################
	splitted_offset = []
	for i in xrange(csp_len) :
		subpath = unclipped_offset[i]
		if len(intersection[i]) > 0 :
			parts = csp_subpath_split_by_points(subpath, intersection[i])
			# Close	parts list to close path (The first and the last parts are joined together)		
			if  [1,0.] not in intersection[i] : 
				parts[0][0][0] = parts[-1][-1][0]
				parts[0] = csp_concat_subpaths(parts[-1], parts[0])
				splitted_offset += parts[:-1]
			else: 
				splitted_offset += parts[:]
		else :
			splitted_offset += [subpath[:]]
	
	#for i in range(len(splitted_offset)):
	#	draw_csp([splitted_offset[i]], color = ["Green","Red","Blue"][i%3])
	print_("Splitted in %s"%(time.time()-time_))
	time_ = time.time()

	
	########################################################################
	# Clipping
	########################################################################		
	result = []
	for subpath_i in range(len(splitted_offset)):
		clip = False
		s1 = splitted_offset[subpath_i]
		for subpath_j in range(len(splitted_offset)):
			s2 = splitted_offset[subpath_j]
			if (P(s1[0][1])-P(s2[-1][1])).l2()<0.0001 and ( (subpath_i+1) % len(splitted_offset) != subpath_j ): 
				if dot(csp_normalized_normal(s2[-2],s2[-1],1.),csp_normalized_slope(s1[0],s1[1],0.))*r<-0.0001 :
					clip = True
					break
			if (P(s2[0][1])-P(s1[-1][1])).l2()<0.0001 and ( (subpath_j+1) % len(splitted_offset) != subpath_i ):
				if dot(csp_normalized_normal(s2[0],s2[1],0.),csp_normalized_slope(s1[-2],s1[-1],1.))*r>0.0001 :
					clip = True
					break
			
		if not clip :
			result += [s1[:]]
		elif options.offset_draw_clippend_path :
			draw_csp([s1],color="Red",width=.1)
			draw_pointer( csp_at_t(s2[-2],s2[-1],1.)+
				(P(csp_at_t(s2[-2],s2[-1],1.))+ P(csp_normalized_normal(s2[-2],s2[-1],1.))*10).to_list(),"Green", "line"  )
			draw_pointer( csp_at_t(s1[0],s1[1],0.)+
				(P(csp_at_t(s1[0],s1[1],0.))+ P(csp_normalized_slope(s1[0],s1[1],0.))*10).to_list(),"Red", "line"  )
				
	# Now join all together and check closure and orientation of result
	joined_result = csp_join_subpaths(result)
	# Check if each subpath from joined_result is closed
	#draw_csp(joined_result,color="Green",width=1)

	
	for s in joined_result[:] :
		if csp_subpaths_end_to_start_distance2(s,s) > 0.001 :
			# Remove open parts
			if options.offset_draw_clippend_path:
				draw_csp([s],color="Orange",width=1)
				draw_pointer(s[0][1], comment= csp_subpaths_end_to_start_distance2(s,s))
				draw_pointer(s[-1][1], comment = csp_subpaths_end_to_start_distance2(s,s))
			joined_result.remove(s)
		else : 			
			# Remove small parts
			minx,miny,maxx,maxy = csp_true_bounds([s])
			if (minx[0]-maxx[0])**2 + (miny[1]-maxy[1])**2 < 0.1 :
				joined_result.remove(s)
	print_("Clipped and joined path in %s"%(time.time()-time_))
	time_ = time.time()
			
	########################################################################
	# Now to the Dummy cliping: remove parts from splitted offset if their 
	# centers are  closer to the original path than offset radius. 
	########################################################################		
	
	r1,r2 = ( (0.99*r)**2, (1.01*r)**2 ) if abs(r*.01)<1 else  ((abs(r)-1)**2, (abs(r)+1)**2)
	for s in joined_result[:]:
		dist = csp_to_point_distance(original_csp, s[int(len(s)/2)][1], dist_bounds = [r1,r2], tolerance = .000001)
		if not r1 < dist[0] < r2 : 
			joined_result.remove(s)
			if options.offset_draw_clippend_path:
				draw_csp([s], comment = math.sqrt(dist[0]))
				draw_pointer(csp_at_t(csp[dist[1]][dist[2]-1],csp[dist[1]][dist[2]],dist[3])+s[int(len(s)/2)][1],"blue", "line", comment = [math.sqrt(dist[0]),i,j,sp]  )

	print_("-----------------------------")
	print_("Total offset time %s"%(time.time()-time_start))
	print_()
	return joined_result
	
	
		


################################################################################
###
###		Biarc function
###
###		Calculates biarc approximation of cubic super path segment
###		splits segment if needed or approximates it with straight line
###
################################################################################
def biarc(sp1, sp2, z1, z2, depth=0):
	def biarc_split(sp1,sp2, z1, z2, depth): 
		if depth<options.biarc_max_split_depth:
			sp1,sp2,sp3 = csp_split(sp1,sp2)
			l1, l2 = cspseglength(sp1,sp2), cspseglength(sp2,sp3)
			if l1+l2 == 0 : zm = z1
			else : zm = z1+(z2-z1)*l1/(l1+l2)
			return biarc(sp1,sp2,z1,zm,depth+1)+biarc(sp2,sp3,zm,z2,depth+1)
		else: return [ [sp1[1],'line', 0, 0, sp2[1], [z1,z2]] ]

	P0, P4 = P(sp1[1]), P(sp2[1])
	TS, TE, v = (P(sp1[2])-P0), -(P(sp2[0])-P4), P0 - P4
	tsa, tea, va = TS.angle(), TE.angle(), v.angle()
	if TE.mag()<straight_distance_tolerance and TS.mag()<straight_distance_tolerance:	
		# Both tangents are zerro - line straight
		return [ [sp1[1],'line', 0, 0, sp2[1], [z1,z2]] ]
	if TE.mag() < straight_distance_tolerance:
		TE = -(TS+v).unit()
		r = TS.mag()/v.mag()*2
	elif TS.mag() < straight_distance_tolerance:
		TS = -(TE+v).unit()
		r = 1/( TE.mag()/v.mag()*2 )
	else:	
		r=TS.mag()/TE.mag()
	TS, TE = TS.unit(), TE.unit()
	tang_are_parallel = ((tsa-tea)%math.pi<straight_tolerance or math.pi-(tsa-tea)%math.pi<straight_tolerance )
	if ( tang_are_parallel  and 
				((v.mag()<straight_distance_tolerance or TE.mag()<straight_distance_tolerance or TS.mag()<straight_distance_tolerance) or
					1-abs(TS*v/(TS.mag()*v.mag()))<straight_tolerance)	):
				# Both tangents are parallel and start and end are the same - line straight
				# or one of tangents still smaller then tollerance

				# Both tangents and v are parallel - line straight
		return [ [sp1[1],'line', 0, 0, sp2[1], [z1,z2]] ]

	c,b,a = v*v, 2*v*(r*TS+TE), 2*r*(TS*TE-1)
	if v.mag()==0:
		return biarc_split(sp1, sp2, z1, z2, depth)
	asmall, bsmall, csmall = abs(a)<10**-10,abs(b)<10**-10,abs(c)<10**-10 
	if 		asmall and b!=0:	beta = -c/b
	elif 	csmall and a!=0:	beta = -b/a 
	elif not asmall:	 
		discr = b*b-4*a*c
		if discr < 0:	raise ValueError, (a,b,c,discr)
		disq = discr**.5
		beta1 = (-b - disq) / 2 / a
		beta2 = (-b + disq) / 2 / a
		if beta1*beta2 > 0 :	raise ValueError, (a,b,c,disq,beta1,beta2)
		beta = max(beta1, beta2)
	elif	asmall and bsmall:	
		return biarc_split(sp1, sp2, z1, z2, depth)
	alpha = beta * r
	ab = alpha + beta 
	P1 = P0 + alpha * TS
	P3 = P4 - beta * TE
	P2 = (beta / ab)  * P1 + (alpha / ab) * P3


	def calculate_arc_params(P0,P1,P2):
		D = (P0+P2)/2
		if (D-P1).mag()==0: return None, None
		R = D - ( (D-P0).mag()**2/(D-P1).mag() )*(P1-D).unit()
		p0a, p1a, p2a = (P0-R).angle()%(2*math.pi), (P1-R).angle()%(2*math.pi), (P2-R).angle()%(2*math.pi)
		alpha =  (p2a - p0a) % (2*math.pi)					
		if (p0a<p2a and  (p1a<p0a or p2a<p1a))	or	(p2a<p1a<p0a) : 
			alpha = -2*math.pi+alpha 
		if abs(R.x)>1000000 or abs(R.y)>1000000  or (R-P0).mag<options.min_arc_radius**2 :
			return None, None
		else :	
			return  R, alpha
	R1,a1 = calculate_arc_params(P0,P1,P2)
	R2,a2 = calculate_arc_params(P2,P3,P4)
	if R1==None or R2==None or (R1-P0).mag()<straight_tolerance or (R2-P2).mag()<straight_tolerance	: return [ [sp1[1],'line', 0, 0, sp2[1], [z1,z2]] ]
	
	d = csp_to_arc_distance(sp1,sp2, [P0,P2,R1,a1],[P2,P4,R2,a2])
	if d > options.biarc_tolerance and depth<options.biarc_max_split_depth	 : return biarc_split(sp1, sp2, z1, z2, depth)
	else:
		if R2.mag()*a2 == 0 : zm = z2
		else : zm  = z1 + (z2-z1)*(abs(R1.mag()*a1))/(abs(R2.mag()*a2)+abs(R1.mag()*a1)) 

		l = (P0-P2).l2()
		if  l < EMC_TOLERANCE_EQUAL**2 or l<EMC_TOLERANCE_EQUAL**2 * R1.l2() /100 :
			# arc should be straight otherwise it could be threated as full circle
			arc1 = [ sp1[1], 'line', 0, 0, [P2.x,P2.y], [z1,zm] ] 
		else :
			arc1 = [ sp1[1], 'arc', [R1.x,R1.y], a1, [P2.x,P2.y], [z1,zm] ] 

		l = (P4-P2).l2()
		if  l < EMC_TOLERANCE_EQUAL**2 or l<EMC_TOLERANCE_EQUAL**2 * R2.l2() /100 :
			# arc should be straight otherwise it could be threated as full circle
			arc2 = [ [P2.x,P2.y], 'line', 0, 0, [P4.x,P4.y], [zm,z2] ] 
		else :
			arc2 = [ [P2.x,P2.y], 'arc', [R2.x,R2.y], a2, [P4.x,P4.y], [zm,z2] ]
		
		return [ arc1, arc2 ]


def biarc_curve_segment_length(seg):
	if seg[1] == "arc" :
		return math.sqrt((seg[0][0]-seg[2][0])**2+(seg[0][1]-seg[2][1])**2)*seg[3]
	elif seg[1] == "line" :	
		return math.sqrt((seg[0][0]-seg[4][0])**2+(seg[0][1]-seg[4][1])**2)
	else: 
		return 0	


def biarc_curve_clip_at_l(curve, l, clip_type = "strict") :
	# get first subcurve and ceck it's length  
	subcurve, subcurve_l, moved = [], 0, False
	for seg in curve:
		if seg[1] == "move" and moved or seg[1] == "end" :	
			break
		if seg[1] == "move" : moved = True 
		subcurve_l += biarc_curve_segment_length(seg)
		if seg[1] == "arc" or seg[1] == "line" : 
			subcurve += [seg]

	if subcurve_l < l and clip_type == "strict" : return []	
	lc = 0
	if (subcurve[-1][4][0]-subcurve[0][0][0])**2 + (subcurve[-1][4][1]-subcurve[0][0][1])**2 < 10**-7 : subcurve_closed = True
	i = 0
	reverse = False
	while lc<l :
		seg = subcurve[i]
		if reverse :  
			if seg[1] == "line" :
				seg = [seg[4], "line", 0 , 0, seg[0], seg[5]] # Hmmm... Do we have to swap seg[5][0] and seg[5][1] (zstart and zend) or not?
			elif seg[1] == "arc" :
				seg = [seg[4], "arc", seg[2] , -seg[3], seg[0], seg[5]] # Hmmm... Do we have to swap seg[5][0] and seg[5][1] (zstart and zend) or not?
		ls = biarc_curve_segment_length(seg)
		if ls != 0 :
			if l-lc>ls :
				res += [seg]
			else :
				if seg[1] == "arc" :
					r  = math.sqrt((seg[0][0]-seg[2][0])**2+(seg[0][1]-seg[2][1])**2)
					x,y = seg[0][0]-seg[2][0], seg[0][1]-seg[2][1]
					a = seg[3]/ls*(l-lc)
					x,y = x*math.cos(a) - y*math.sin(a),  x*math.sin(a) + y*math.cos(a)
					x,y = x+seg[2][0], y+seg[2][1]
					res += [[ seg[0], "arc",  seg[2], a, [x,y], [seg[5][0],seg[5][1]/ls*(l-lc)]  ]]
				if seg[1] == "line" :
					res += [[ seg[0], "line",  0, 0, [(seg[4][0]-seg[0][0])/ls*(l-lc),(seg[4][1]-seg[0][1])/ls*(l-lc)], [seg[5][0],seg[5][1]/ls*(l-lc)]  ]]
		i += 1 
		if i >= len(subcurve) and not subcurve_closed: 
			reverse = not reverse
		i = i%len(subcurve)
	return res	

	
	
class Postprocessor():
	def __init__(self, error_function_handler):	
		self.error = error_function_handler 
		self.functions = {
					"remap"		: self.remap,
					"remapi"	: self.remapi ,
					"scale"		: self.scale,
					"move"		: self.move,
					"flip"		: self.flip_axis,
					"flip_axis"	: self.flip_axis,
					"round"		: self.round_coordinates,
					"parameterize"	: self.parameterize,
					"regex"			: self.re_sub_on_gcode_lines
					}
	
			
	def process(self,command):
		command = re.sub(r"\\\\",":#:#:slash:#:#:",command)
		command = re.sub(r"\\;",":#:#:semicolon:#:#:",command)
		command = command.split(";")
		for s in command: 
			s = re.sub(":#:#:slash:#:#:","\\\\",s)
			s = re.sub(":#:#:semicolon:#:#:","\\;",s)
			s = s.strip()
			if s!="" :
				self.parse_command(s)		
			
	
	def parse_command(self,command):
		r = re.match(r"([A-Za-z0-9_]+)\s*\(\s*(.*)\)",command)
		if not r:
			self.error("Parse error while postprocessing.\n(Command: '%s')"%(command), "error")
		function, parameters = r.group(1).lower(),r.group(2)
		if function in self.functions :
			print_("Postprocessor: executing function %s(%s)"%(function,parameters))
			self.functions[function](parameters)
		else : 
			self.error("Unrecognized function '%s' while postprocessing.\n(Command: '%s')"%(function,command), "error")
	
	
	def re_sub_on_gcode_lines(self, parameters):
		gcode = self.gcode.split("\n")
		self.gcode = ""
		try :
			for line in gcode :
				self.gcode += eval( "re.sub(%s,line)"%parameters) +"\n"

		except Exception as ex :	
			self.error("Bad parameters for regexp. They should be as re.sub pattern and replacement parameters! For example: r\"G0(\d)\", r\"G\\1\" \n(Parameters: '%s')\n %s"%(parameters, ex), "error")				
		
	
	def remapi(self,parameters):
		self.remap(parameters, case_sensitive = True)
	
	
	def remap(self,parameters, case_sensitive = False):
		# remap parameters should be like "x->y,y->x"
		parameters = parameters.replace("\,",":#:#:coma:#:#:")
		parameters = parameters.split(",")
		pattern, remap = [], []
		for s in parameters:
			s = s.replace(":#:#:coma:#:#:","\,")
			r = re.match("""\s*(\'|\")(.*)\\1\s*->\s*(\'|\")(.*)\\3\s*""",s)
			if not r :
				self.error("Bad parameters for remap.\n(Parameters: '%s')"%(parameters), "error")
			pattern +=[r.group(2)]	
			remap +=[r.group(4)]	
		
		
		
		for i in range(len(pattern)) :
			if case_sensitive :
				self.gcode = ireplace(self.gcode, pattern[i], ":#:#:remap_pattern%s:#:#:"%i )
			else :
				self.gcode = self.gcode.replace(pattern[i], ":#:#:remap_pattern%s:#:#:"%i)
			
		for i in range(len(remap)) :
			self.gcode = self.gcode.replace(":#:#:remap_pattern%s:#:#:"%i, remap[i])
	
	
	def transform(self, move, scale):
		axis = ["xi","yj","zk","a"]
		flip = scale[0]*scale[1]*scale[2] < 0 
		gcode = ""
		warned = []
		r_scale = scale[0]
		plane = "g17"
		for s in self.gcode.split("\n"):
			# get plane selection: 
			s_wo_comments = re.sub(r"\([^\)]*\)","",s)
			r = re.search(r"(?i)(G17|G18|G19)", s_wo_comments)
			if r :
				plane = r.group(1).lower()
				if plane == "g17" : r_scale = scale[0] # plane XY -> scale x
				if plane == "g18" : r_scale = scale[0] # plane XZ -> scale x
				if plane == "g19" : r_scale = scale[1] # plane YZ -> scale y
			# Raise warning if scale factors are not the game for G02 and G03	
			if plane not in warned:
				r = re.search(r"(?i)(G02|G03)", s_wo_comments)
				if r :
					if plane == "g17" and scale[0]!=scale[1]: self.error("Post-processor: Scale factors for X and Y axis are not the same. G02 and G03 codes will be corrupted.","warning") 
					if plane == "g18" and scale[0]!=scale[2]: self.error("Post-processor: Scale factors for X and Z axis are not the same. G02 and G03 codes will be corrupted.","warning") 
					if plane == "g19" and scale[1]!=scale[2]: self.error("Post-processor: Scale factors for Y and Z axis are not the same. G02 and G03 codes will be corrupted.","warning") 
					warned += [plane]
			# Transform		
			for i in range(len(axis)) :
				if move[i] != 0 or scale[i] != 1:
					for a in axis[i] :
						r = re.search(r"(?i)("+a+r")\s*(-?)\s*(\d*\.?\d*)", s)
						if r and r.group(3)!="":
							s = re.sub(r"(?i)("+a+r")\s*(-?)\s*(\d*\.?\d*)", r"\1 %f"%(float(r.group(2)+r.group(3))*scale[i]+(move[i] if a not in ["i","j","k"] else 0) ), s)
			#scale radius R
			if r_scale != 1 :
				r = re.search(r"(?i)(r)\s*(-?\s*(\d*\.?\d*))", s)
				if r and r.group(3)!="":
					try:
						s = re.sub(r"(?i)(r)\s*(-?)\s*(\d*\.?\d*)", r"\1 %f"%( float(r.group(2)+r.group(3))*r_scale ), s)
					except:
						pass	

			gcode += s + "\n"
			
		self.gcode = gcode
		if flip : 
			self.remapi("'G02'->'G03', 'G03'->'G02'")


	def parameterize(self,parameters) :
		planes = []
		feeds = {}
		coords = []
		gcode = ""
		coords_def = {"x":"x","y":"y","z":"z","i":"x","j":"y","k":"z","a":"a"}
		for s in self.gcode.split("\n"):
			s_wo_comments = re.sub(r"\([^\)]*\)","",s)
			# get Planes
			r = re.search(r"(?i)(G17|G18|G19)", s_wo_comments)
			if r :
				plane = r.group(1).lower()
				if plane not in planes : 
					planes += [plane]
			# get Feeds
			r = re.search(r"(?i)(F)\s*(-?)\s*(\d*\.?\d*)", s_wo_comments)
			if r :
				feed  = float (r.group(2)+r.group(3))
				if feed not in feeds :
					feeds[feed] = "#"+str(len(feeds)+20)
					
			#Coordinates
			for c in "xyzijka" :
				r = re.search(r"(?i)("+c+r")\s*(-?)\s*(\d*\.?\d*)", s_wo_comments)
				if r :
					c = coords_def[r.group(1).lower()]
					if c not in coords :
						coords += [c]
		# Add offset parametrization
		offset = {"x":"#6","y":"#7","z":"#8","a":"#9"}
		for c in coords:
			gcode += "%s  = 0 (%s axis offset)\n" %  (offset[c],c.upper())
			
		# Add scale parametrization
		if planes == [] : planes = ["g17"]
		if len(planes)>1 :  # have G02 and G03 in several planes scale_x = scale_y = scale_z required
			gcode += "#10 = 1 (Scale factor)\n"
			scale = {"x":"#10","i":"#10","y":"#10","j":"#10","z":"#10","k":"#10","r":"#10"}
		else :
			gcode += "#10 = 1 (%s Scale factor)\n" % ({"g17":"XY","g18":"XZ","g19":"YZ"}[planes[0]])
			gcode += "#11 = 1 (%s Scale factor)\n" % ({"g17":"Z","g18":"Y","g19":"X"}[planes[0]])
			scale = {"x":"#10","i":"#10","y":"#10","j":"#10","z":"#10","k":"#10","r":"#10"}
			if "g17" in planes :
				scale["z"] = "#11"				
				scale["k"] = "#11"				
			if "g18" in planes :
				scale["y"] = "#11"				
				scale["j"] = "#11"				
			if "g19" in planes :
				scale["x"] = "#11"				
				scale["i"] = "#11"				
		# Add a scale 
		if "a" in coords:
			gcode += "#12  = 1 (A axis scale)\n" 
			scale["a"] = "#12"
		
		# Add feed parametrization 
		for f in feeds :
			gcode += "%s = %f (Feed definition)\n" % (feeds[f],f)

		# Parameterize Gcode		
		for s in self.gcode.split("\n"):
			#feed replace :
			r = re.search(r"(?i)(F)\s*(-?)\s*(\d*\.?\d*)", s)
			if r and len(r.group(3))>0:
				s = re.sub(r"(?i)(F)\s*(-?)\s*(\d*\.?\d*)", "F [%s]"%feeds[float(r.group(2)+r.group(3))], s)
			#Coords XYZA replace
			for c in "xyza" :
				r = re.search(r"(?i)(("+c+r")\s*(-?)\s*(\d*\.?\d*))", s)
				if r and len(r.group(4))>0:
					s = re.sub(r"(?i)("+c+r")\s*((-?)\s*(\d*\.?\d*))", r"\1[\2*%s+%s]"%(scale[c],offset[c]), s)

			#Coords IJKR replace
			for c in "ijkr" :
				r = re.search(r"(?i)(("+c+r")\s*(-?)\s*(\d*\.?\d*))", s)
				if r and len(r.group(4))>0:
					s = re.sub(r"(?i)("+c+r")\s*((-?)\s*(\d*\.?\d*))", r"\1[\2*%s]"%scale[c], s)

			gcode += s + "\n"
	
		self.gcode = gcode	

	
	def round_coordinates(self,parameters) :
		try: 
			round_ = int(parameters)
		except :	
			self.error("Bad parameters for round. Round should be an integer! \n(Parameters: '%s')"%(parameters), "error")		
		gcode = ""
		for s in self.gcode.split("\n"):
			for a in "xyzijkaf" :
				r = re.search(r"(?i)("+a+r")\s*(-?\s*(\d*\.?\d*))", s)
				if r : 
					
					if r.group(2)!="":
						s = re.sub(
									r"(?i)("+a+r")\s*(-?)\s*(\d*\.?\d*)", 
									(r"\1 %0."+str(round_)+"f" if round_>0 else r"\1 %d")%round(float(r.group(2)),round_),
									s)
			gcode += s + "\n"
		self.gcode = gcode
	

	def scale(self, parameters):
		parameters = parameters.split(",")
		scale = [1.,1.,1.,1.]
		try :
			for i in range(len(parameters)) :
				if float(parameters[i])==0 : 
					self.error("Bad parameters for scale. Scale should not be 0 at any axis! \n(Parameters: '%s')"%(parameters), "error")		
				scale[i] = float(parameters[i])
		except :
			self.error("Bad parameters for scale.\n(Parameters: '%s')"%(parameters), "error")
		self.transform([0,0,0,0],scale)		

	
	def move(self, parameters):
		parameters = parameters.split(",")
		move = [0.,0.,0.,0.]
		try :
			for i in range(len(parameters)) :
				move[i] = float(parameters[i])
		except :
			self.error("Bad parameters for move.\n(Parameters: '%s')"%(parameters), "error")
		self.transform(move,[1.,1.,1.,1.])		


	def flip_axis(self, parameters):
		parameters = parameters.lower()
		axis = {"x":1.,"y":1.,"z":1.,"a":1.}	
		for p in parameters: 
			if p in [","," ","	","\r","'",'"'] : continue
			if p not in ["x","y","z","a"] : 
				self.error("Bad parameters for flip_axis. Parameter should be string consists of 'xyza' \n(Parameters: '%s')"%(parameters), "error")
			axis[p] = -axis[p]
		self.scale("%f,%f,%f,%f"%(axis["x"],axis["y"],axis["z"],axis["a"]))	

	
			
################################################################################
###		Polygon class
################################################################################
class Polygon:
	def __init__(self, polygon=None):
		self.polygon = [] if polygon==None else polygon[:]
	
	
	def move(self, x, y) :
		for i in range(len(self.polygon)) :
			for j in range(len(self.polygon[i])) :
				self.polygon[i][j][0] += x
				self.polygon[i][j][1] += y
	
	
	def bounds(self) : 
		minx,miny,maxx,maxy = 1e400, 1e400, -1e400, -1e400
		for poly in self.polygon :
			for p in poly :
				if minx > p[0] : minx = p[0]
				if miny > p[1] : miny = p[1]
				if maxx < p[0] : maxx = p[0]
				if maxy < p[1] : maxy = p[1]
		return minx*1,miny*1,maxx*1,maxy*1		
	
	
	def width(self):
		b = self.bounds()
		return b[2]-b[0]
	
	
	def rotate_(self,sin,cos) :
		self.polygon = [
				[
					[point[0]*cos - point[1]*sin,point[0]*sin + point[1]*cos] for point in subpoly
				]			
				for subpoly in self.polygon
			]

	
	def rotate(self, a):
		cos, sin = math.cos(a), math.sin(a)
		self.rotate_(sin,cos)
	
			
	def drop_into_direction(self, direction, surface) :
		# Polygon is a list of simple polygons
		# Surface is a polygon + line y = 0 
		# Direction is [dx,dy]  
		if len(self.polygon) == 0 or len(self.polygon[0])==0 : return
		if direction[0]**2 + direction[1]**2 <1e-10 : return
		direction = normalize(direction)
		sin,cos = direction[0], -direction[1]
		self.rotate_(-sin,cos)
		surface.rotate_(-sin,cos)
		self.drop_down(surface, zerro_plane = False)
		self.rotate_(sin,cos)
		surface.rotate_(sin,cos)
		
			
	def centroid(self):
		centroids = []
		sa = 0
		for poly in self.polygon:
			cx,cy,a = 0,0,0
			for i in range(len(poly)):
				[x1,y1],[x2,y2] = poly[i-1],poly[i]
				cx += (x1+x2)*(x1*y2-x2*y1)
				cy += (y1+y2)*(x1*y2-x2*y1)
				a  += (x1*y2-x2*y1)
			a *= 3.
			if abs(a)>0 :
				cx /= a
				cy /= a
				sa += abs(a)
				centroids += [ [cx,cy,a] ]
		if sa == 0 : return	[0.,0.]
		cx,cy = 0.,0.
		for c in centroids :
			cx += c[0]*c[2]
			cy += c[1]*c[2]
		cx /= sa
		cy /= sa
		return [cx,cy]

			
	def drop_down(self, surface, zerro_plane = True) :
		# Polygon is a list of simple polygons
		# Surface is a polygon + line y = 0 
		# Down means min y (0,-1)  
		if len(self.polygon) == 0 or len(self.polygon[0])==0 : return
		# Get surface top point
		top = surface.bounds()[3]
		if zerro_plane : top = max(0, top)
		# Get polygon bottom point
		bottom = self.bounds()[1]
		self.move(0, top - bottom + 10)		
		# Now get shortest distance from surface to polygon in positive x=0 direction
		# Such distance = min(distance(vertex, edge)...)  where edge from surface and 
		# vertex from polygon and vice versa...
		dist = 1e300
		for poly in surface.polygon :
			for i in range(len(poly)) :
				for poly1 in self.polygon :
					for i1 in range(len(poly1)) :
						st,end = poly[i-1], poly[i] 
						vertex = poly1[i1]
						if st[0]<=vertex[0]<= end[0] or end[0]<=vertex[0]<=st[0] :
							if st[0]==end[0] : d = min(vertex[1]-st[1],vertex[1]-end[1])
							else : d = vertex[1] - st[1] - (end[1]-st[1])*(vertex[0]-st[0])/(end[0]-st[0]) 
							if dist > d  : dist = d
						# and vice versa just change the sign because vertex now under the edge
						st,end = poly1[i1-1], poly1[i1] 
						vertex = poly[i]
						if st[0]<=vertex[0]<=end[0] or end[0]<=vertex[0]<=st[0] :
							if st[0]==end[0] : d = min(- vertex[1]+st[1],-vertex[1]+end[1])
							else : d =  - vertex[1] + st[1] + (end[1]-st[1])*(vertex[0]-st[0])/(end[0]-st[0]) 
							if dist > d  : dist = d
		
		if zerro_plane and dist > 10 + top : dist = 10 + top 
		#print_(dist, top, bottom)
		#self.draw()
		self.move(0, -dist)		
		
					
	def draw(self,color="#075",width=.1, group = None) :
		csp = [csp_subpath_line_to([],poly+[poly[0]]) for poly in self.polygon]
		draw_csp( csp, color=color,width=width, group = group)
			
			
	
	def add(self, add) :
		if type(add) == type([]) :
			self.polygon += add[:]
		else :	
			self.polygon += add.polygon[:]

	
	def point_inside(self,p) :
		inside = False
		for poly in self.polygon :
			for i in range(len(poly)):
				st,end = poly[i-1], poly[i]
				if p==st or p==end : return True # point is a vertex = point is on the edge
				if st[0]>end[0] : st, end = end, st # This will be needed to check that edge if open only at rigth end
				c = (p[1]-st[1])*(end[0]-st[0])-(end[1]-st[1])*(p[0]-st[0])
				#print_(c)
				if st[0]<=p[0]<end[0] : 
					if c<0 : 
						inside = not inside
					elif c == 0 : return True # point is on the edge
				elif st[0]==end[0]==p[0] and (st[1]<=p[1]<=end[1] or end[1]<=p[1]<=st[1]) : # point is on the edge
					return True
		return inside			

	
	def hull(self) :
		# Add vertices at all self intersection points. 
		hull = []
		for i1 in range(len(self.polygon)):
			poly1 = self.polygon[i1]
			poly_ = []
			for j1 in range(len(poly1)):
				s, e = poly1[j1-1],poly1[j1]
				poly_ += [s]
				
				# Check self intersections 
				for j2 in range(j1+1,len(poly1)):
					s1, e1 = poly1[j2-1],poly1[j2]
					int_ = line_line_intersection_points(s,e,s1,e1)
					for p in int_ :
						if point_to_point_d2(p,s)>0.000001 and point_to_point_d2(p,e)>0.000001 : 
							poly_ += [p]
				# Check self intersections with other polys  
				for i2 in range(len(self.polygon)):
					if i1==i2 : continue
					poly2 = self.polygon[i2]
					for j2 in range(len(poly2)):
						s1, e1 = poly2[j2-1],poly2[j2]
						int_ = line_line_intersection_points(s,e,s1,e1)
						for p in int_ :
							if point_to_point_d2(p,s)>0.000001 and point_to_point_d2(p,e)>0.000001 : 
								poly_ += [p]
			hull += [poly_]
		# Create the dictionary containing all edges in both directions
		edges = {}
		for poly in self.polygon :
			for i in range(len(poly)):
				s,e = tuple(poly[i-1]), tuple(poly[i])
				if (point_to_point_d2(e,s)<0.000001) : continue
				break_s, break_e = False, False
				for p in edges :
					if point_to_point_d2(p,s)<0.000001 : 
						break_s = True
						s = p
					if point_to_point_d2(p,e)<0.000001 : 
						break_e = True
						e = p
					if break_s and break_e : break
				l = point_to_point_d(s,e)
				if not break_s and not break_e : 
					edges[s] = [ [s,e,l] ]
					edges[e] = [ [e,s,l] ]
					#draw_pointer(s+e,"red","line")
					#draw_pointer(s+e,"red","line")
				else : 
					if e in edges :	
						for edge in edges[e] :	
							if point_to_point_d2(edge[1],s)<0.000001 :
								break
						if point_to_point_d2(edge[1],s)>0.000001 :
							edges[e] += [ [e,s,l] ]
							#draw_pointer(s+e,"red","line")
							
					else : 
						edges[e] = [ [e,s,l] ]
						#draw_pointer(s+e,"green","line")
					if s in edges :	
						for edge in edges[s] :	
							if  point_to_point_d2(edge[1],e)<0.000001 :
								break
						if point_to_point_d2(edge[1],e)>0.000001 :
							edges[s] += [ [s,e, l] ]
							#draw_pointer(s+e,"red","line")
					else : 
						edges[s] = [ [s,e,l] ]
						#draw_pointer(s+e,"green","line")

		
		def angle_quadrant(sin,cos):
			# quadrants are (0,pi/2], (pi/2,pi], (pi,3*pi/2], (3*pi/2, 2*pi], i.e. 0 is in the 4-th quadrant
			if sin>0 and cos>=0 : return 1
			if sin>=0 and cos<0 : return 2
			if sin<0 and cos<=0 : return 3
			if sin<=0 and cos>0 : return 4
			
		
		def angle_is_less(sin,cos,sin1,cos1):
			# 0 = 2*pi is the largest angle
			if [sin1, cos1] == [0,1] : return True
			if [sin, cos] == [0,1] : return False
			if angle_quadrant(sin,cos)>angle_quadrant(sin1,cos1) : 
				return False
			if angle_quadrant(sin,cos)<angle_quadrant(sin1,cos1) : 
				return True
			if sin>=0 and cos>0 : return sin<sin1
			if sin>0 and cos<=0 : return sin>sin1
			if sin<=0 and cos<0 : return sin>sin1
			if sin<0 and cos>=0 : return sin<sin1

			
		def get_closes_edge_by_angle(edges, last):
			# Last edge is normalized vector of the last edge.
			min_angle = [0,1]
			next = last
			last_edge = [(last[0][0]-last[1][0])/last[2], (last[0][1]-last[1][1])/last[2]]
			for p in edges:
				#draw_pointer(list(p[0])+[p[0][0]+last_edge[0]*40,p[0][1]+last_edge[1]*40], "Red", "line", width=1)
				#print_("len(edges)=",len(edges))
				cur = [(p[1][0]-p[0][0])/p[2],(p[1][1]-p[0][1])/p[2]]
				cos, sin = dot(cur,last_edge),  cross(cur,last_edge)
				#draw_pointer(list(p[0])+[p[0][0]+cur[0]*40,p[0][1]+cur[1]*40], "Orange", "line", width=1, comment = [sin,cos])
				#print_("cos, sin=",cos,sin)
				#print_("min_angle_before=",min_angle)

				if 	angle_is_less(sin,cos,min_angle[0],min_angle[1]) : 
					min_angle = [sin,cos]
					next = p
				#print_("min_angle=",min_angle)

			return next		
			
		# Join edges together into new polygon cutting the vertexes inside new polygon
		self.polygon = []
		len_edges = sum([len(edges[p]) for p in edges]) 
		loops = 0 
		
		while len(edges)>0 :
			poly = []
			if loops > len_edges  : raise ValueError, "Hull error"
			loops+=1
			# Find left most vertex.
			start = (1e100,1)
			for edge in edges : 
				start = min(start, min(edges[edge])) 
			last = [(start[0][0]-1,start[0][1]),start[0],1]
			first_run = True
			loops1 = 0
			while (last[1]!=start[0] or first_run) : 	
				first_run = False
				if loops1 > len_edges  : raise ValueError, "Hull error"
				loops1 += 1
				next = get_closes_edge_by_angle(edges[last[1]],last)
				#draw_pointer(next[0]+next[1],"Green","line", comment=i, width= 1)
				#print_(next[0],"-",next[1]) 

				last = next
				poly += [ list(last[0]) ]				
			self.polygon += [ poly ]
			# Remove all edges that are intersects new poly (any vertex inside new poly)
			poly_ = Polygon([poly])
			for p in edges.keys()[:] : 
				if poly_.point_inside(list(p)) : del edges[p]
		self.draw(color="Green", width=1)	


class Arangement_Genetic:
	# gene = [fittness, order, rotation, xposition]
	# spieces = [gene]*shapes count
	# population = [spieces]
	def __init__(self, polygons, material_width):
		self.population = []
		self.genes_count = len(polygons)
		self.polygons = polygons
		self.width = material_width
		self.mutation_factor = 0.1
		self.order_mutate_factor = 1.
		self.move_mutate_factor = 1.

	
	def add_random_species(self,count):
		for i in range(count):
			specimen = []
			order = range(self.genes_count)
			random.shuffle(order)
			for j in order:
				specimen += [ [j, random.random(), random.random()] ]
			self.population += [ [None,specimen] ]

	
	def species_distance2(self,sp1,sp2) :
		# retun distance, each component is normalized
		s = 0
		for j in range(self.genes_count) :
			s += ((sp1[j][0]-sp2[j][0])/self.genes_count)**2 + (( sp1[j][1]-sp2[j][1]))**2 + ((sp1[j][2]-sp2[j][2]))**2
		return s

	
	def similarity(self,sp1,top) :
		# Define similarity as a simple distance between two points in len(gene)*len(spiece) -th dimensions
		# for sp2 in top_spieces sum(|sp1-sp2|)/top_count
		sim = 0
		for sp2 in top : 
			sim += math.sqrt(species_distance2(sp1,sp2[1]))
		return sim/len(top)
		
	
	def leave_top_species(self,count):
		self.population.sort()
		res = [  copy.deepcopy(self.population[0]) ]
		del self.population[0]
		for i in range(count-1) :
			t = []
			for j in range(20) : 
				i1 = random.randint(0,len(self.population)-1) 
				t += [ [self.population[i1][0],i1] ] 
			t.sort()
			res += [  copy.deepcopy(self.population[t[0][1]]) ]
			del self.population[t[0][1]]
		self.population = res		
		#del self.population[0]
		#for c in range(count-1) :
		#	rank = []
		#	for i in range(len(self.population)) :	
		#		sim = self.similarity(self.population[i][1],res)
		#		rank += [ [self.population[i][0] / sim if sim>0 else 1e100,i] ]
		#	rank.sort()
		#	res += [  copy.deepcopy(self.population[rank[0][1]]) ]
		#	print_(rank[0],self.population[rank[0][1]][0])
		#	print_(res[-1])
		#	del self.population[rank[0][1]]
			
		self.population = res
			
			
	def populate_species(self,count, parent_count):
		self.population.sort()
		self.inc = 0
		for c in range(count):
			parent1 = random.randint(0,parent_count-1)
			parent2 = random.randint(0,parent_count-1)
			if parent1==parent2 : parent2 = (parent2+1) % parent_count
			parent1, parent2 = self.population[parent1][1], self.population[parent2][1]
			i1,i2 = 0, 0
			genes_order = []
			specimen = [ [0,0.,0.] for i in range(self.genes_count) ]
			
			self.incest_mutation_multiplyer = 1.
			self.incest_mutation_count_multiplyer = 1.

			if self.species_distance2(parent1, parent2) <= .01/self.genes_count :
				# OMG it's a incest :O!!!
				# Damn you bastards!
				self.inc +=1
				self.incest_mutation_multiplyer = 2. 
				self.incest_mutation_count_multiplyer = 2. 
			else :
				pass
#				if random.random()<.01 : print_(self.species_distance2(parent1, parent2))	
			start_gene = random.randint(0,self.genes_count)
			end_gene = (max(1,random.randint(0,self.genes_count),int(self.genes_count/4))+start_gene) % self.genes_count
			if end_gene<start_gene : 
				end_gene, start_gene = start_gene, end_gene
				parent1, parent2 = parent2, parent1
			for i in range(start_gene,end_gene) : 
				#rotation_mutate_param = random.random()/100
				#xposition_mutate_param = random.random()/100
				tr = 1. #- rotation_mutate_param
				tp = 1. #- xposition_mutate_param
				specimen[i] = [parent1[i][0], parent1[i][1]*tr+parent2[i][1]*(1-tr),parent1[i][2]*tp+parent2[i][2]*(1-tp)]
				genes_order += [ parent1[i][0] ]

			for i in range(0,start_gene)+range(end_gene,self.genes_count) : 
				tr = 0. #rotation_mutate_param
				tp = 0. #xposition_mutate_param
				j = i 
				while parent2[j][0] in genes_order :
					j = (j+1)%self.genes_count
				specimen[i] = [parent2[j][0], parent1[i][1]*tr+parent2[i][1]*(1-tr),parent1[i][2]*tp+parent2[i][2]*(1-tp)]
				genes_order += [ parent2[j][0] ]						
				

			for i in range(random.randint(self.mutation_genes_count[0],self.mutation_genes_count[0]*self.incest_mutation_count_multiplyer )) :
				if random.random() < self.order_mutate_factor * self.incest_mutation_multiplyer : 
					i1,i2 = random.randint(0,self.genes_count-1),random.randint(0,self.genes_count-1)
					specimen[i1][0], specimen[i2][0] = specimen[i2][0], specimen[i1][0]
				if random.random() < self.move_mutation_factor * self.incest_mutation_multiplyer: 
					i1 = random.randint(0,self.genes_count-1)
					specimen[i1][1] =  (specimen[i1][1]+random.random()*math.pi2*self.move_mutation_multiplier)%1.
					specimen[i1][2] =  (specimen[i1][2]+random.random()*self.move_mutation_multiplier)%1.
			self.population += [ [None,specimen] ]	

	
	def test_spiece_drop_down(self,spiece) : 
		surface = Polygon()
		for p in spiece :
			time_ = time.time()
			poly = Polygon(copy.deepcopy(self.polygons[p[0]].polygon))
			poly.rotate(p[1]*math.pi2)
			w = poly.width()
			left = poly.bounds()[0]
			poly.move( -left + (self.width-w)*p[2],0)
			poly.drop_down(surface)
			surface.add(poly)
		return surface

	
	def test(self,test_function): 
		time_ = time.time()
		for i in range(len(self.population)) :
			if self.population[i][0] == None :
				surface = test_function(self.population[i][1])
				b = surface.bounds()
				self.population[i][0] = (b[3]-b[1])*(b[2]-b[0])
		self.population.sort() 				

	def test_spiece_centroid(self,spiece) : 
		poly = Polygon(	self.polygons[spiece[0][0]].polygon[:])
		poly.rotate(spiece[0][1]*math.pi2)
		surface  = Polygon(poly.polygon)
		for p in spiece[1:] :
			poly = Polygon(self.polygons[p[0]].polygon[:])
			c = surface.centroid()
			surface.move(-c[0],-c[1])
			c1 = poly.centroid()
			poly.move(-c1[0],-c1[1])
			poly.rotate(p[1]*math.pi2+p[2]*math.pi2)
			surface.rotate(p[2]*math.pi2)
			poly.drop_down(surface)
			surface.add(poly)
			surface.rotate(-p[2]*math.pi2)
		return surface
		
				
	def test_inline(self) : 
		###
		###	Fast test function using weave's from scipy inline function
		###
		try :
			converters is None 
		except :	
			try:
				from scipy import weave
				from scipy.weave import converters
			except:
				options.self.error("For this function Scipy is needed. See http://www.cnc-club.ru/gcodetools for details.","error")		
	
		# Prepare vars 
		poly_, subpoly_, points_ = [], [], []
		for  poly in self.polygons :
			p = poly.polygon
			poly_ += [len(subpoly_), len(subpoly_)+len(p)*2]
			for subpoly in p :
				subpoly_ += [len(points_), len(points_)+len(subpoly)*2+2]
				for point in subpoly :
					points_ += point
				points_ += subpoly[0] # Close subpolygon

		test_ = []
		population_ = []
		for spiece in self.population:
			test_.append( spiece[0] if spiece[0] != None else -1)
			for sp in spiece[1]:
				population_ += sp
			
		lp_, ls_, l_, lt_ = len(poly_), len(subpoly_), len(points_), len(test_)
		 
		f = open('inline_test.c', 'r')
		code = f.read()
		f.close()
		
		f = open('inline_test_functions.c', 'r')
		functions = f.read()
		f.close()
		
		stdout_ = sys.stdout
		s = ''		
		sys.stdout = s 

		test = weave.inline(
							code,
							['points_','subpoly_','poly_', 'lp_', 'ls_', 'l_', 'lt_','test_', 'population_'],
							compiler='gcc',
							support_code = functions,
							)
		if s!='' : options.self.error(s,"warning")		
		sys.stdout = stdout_
		
		for i in range(len(test_)):
			self.population[i][0] = test_[i]
		
		
		
		
		#surface.draw()

		
################################################################################
###
###		Gcodetools class
###
################################################################################

class Gcodetools(inkex.Effect):

	def export_gcode(self,gcode, no_headers = False) :
		if self.options.postprocessor != ""  or self.options.postprocessor_custom != "" :
			postprocessor = Postprocessor(self.error)
			postprocessor.gcode = gcode
			if self.options.postprocessor != "" :
				postprocessor.process(self.options.postprocessor)
			if self.options.postprocessor_custom != "" :
				postprocessor.process(self.options.postprocessor_custom)

		if not no_headers :
			postprocessor.gcode = self.header + postprocessor.gcode + self.footer

		f = open(self.options.directory+self.options.file, "w")	
		f.write(postprocessor.gcode)
		f.close()							


################################################################################
###		In/out paths:
###		TODO move it to the bottom
################################################################################
	def plasma_prepare_path(self) :
	
		def add_arc(sp1,sp2,end = False,l=10.,r=10.) :
			if not end :
				n = csp_normalized_normal(sp1,sp2,0.)
				return csp_reverse([arc_from_s_r_n_l(sp1[1],r,n,-l)])[0]
			else: 
				n = csp_normalized_normal(sp1,sp2,1.)
				return arc_from_s_r_n_l(sp2[1],r,n,l)
				
		def add_normal(sp1,sp2,end = False,l=10.,r=10.) :
			# r is needed only for be compatible with add_arc
			if not end : 
				n = csp_normalized_normal(sp1,sp2,0.)
				p = [n[0]*l+sp1[1][0],n[1]*l+sp1[1][1]]
				return csp_subpath_line_to([], [p,sp1[1]])
			else: 
				n = csp_normalized_normal(sp1,sp2,1.)
				p = [n[0]*l+sp2[1][0],n[1]*l+sp2[1][1]]
				return csp_subpath_line_to([], [sp2[1],p])
				
		def add_tangent(sp1,sp2,end = False,l=10.,r=10.) :
			# r is needed only for be compatible with add_arc
			if not end : 
				n = csp_normalized_slope(sp1,sp2,0.)
				p = [-n[0]*l+sp1[1][0],-n[1]*l+sp1[1][1]]
				return csp_subpath_line_to([], [p,sp1[1]])
			else: 
				n = csp_normalized_slope(sp1,sp2,1.)
				p = [n[0]*l+sp2[1][0],n[1]*l+sp2[1][1]]
				return csp_subpath_line_to([], [sp2[1],p])
	
		if not self.options.in_out_path and not self.options.plasma_prepare_corners and self.options.in_out_path_do_not_add_reference_point: 
			self.error("Warning! Extenstion is not said to do anything! Enable one of Create in-out paths or Prepare corners checkboxes or disable Do not add in-out referense point!")
			return

		# Add in-out-reference point if there is no one yet.		
		if ( (len(self.in_out_reference_points)==0 and self.options.in_out_path
			or not self.options.in_out_path and not self.options.plasma_prepare_corners )
			 and not self.options.in_out_path_do_not_add_reference_point) :
					self.options.orientation_points_count = "in-out reference point"		
					self.orientation()
			
		if self.options.in_out_path or self.options.plasma_prepare_corners:
			self.set_markers()
			add_func = {"Round":add_arc, "Perpendicular": add_normal, "Tangent": add_tangent}[self.options.in_out_path_type]
			if self.options.in_out_path_type == "Round" and self.options.in_out_path_len > self.options.in_out_path_radius*3/2*math.pi :
				self.error("In-out len is to big for in-out radius will cropp it to be r*3/2*pi!", "warning") 
			
			if self.selected_paths == {} and self.options.auto_select_paths:
				self.selected_paths = self.paths
				self.error(_("No paths are selected! Trying to work on all available paths."),"warning")

			if self.selected_paths == {}:
				self.error(_("Nothing is selected. Please select something."),"warning")
			a = self.options.plasma_prepare_corners_tolerance
			corner_tolerance = cross([1.,0.], [math.cos(a),math.sin(a)])

			for layer in self.layers :
				if layer in self.selected_paths :
					max_dist =	self.transform_scalar(self.options.in_out_path_point_max_dist, layer, reverse=True)
					l = 		self.transform_scalar(self.options.in_out_path_len, layer, reverse=True)
					plasma_l = 	self.transform_scalar(self.options.plasma_prepare_corners_distance, layer, reverse=True)
					r = 		self.transform_scalar(self.options.in_out_path_radius, layer, reverse=True)
					l = min(l,r*3/2*math.pi)

					for path in self.selected_paths[layer]:
						csp = self.apply_transforms( path, cubicsuperpath.parsePath(path.get("d")) )
						csp = csp_remove_zerro_segments(csp)
						res = []

						for subpath in csp :
						# Find closes point to in-out reference point
						# If subpath is open skip this step 
							if self.options.in_out_path :
								# split and reverse path for further add in-out points
								if point_to_point_d2(subpath[0][1], subpath[-1][1]) < 1.e-10 :
									d = [1e100,1,1,1.]
									for p in self.in_out_reference_points : 
										d1 = csp_to_point_distance([subpath], p, dist_bounds = [0,max_dist], tolerance=.01)
										if d1[0] < d[0] : 
											d = d1[:]
											p_ = p
									if d[0] < max_dist**2 :
										# Lets find is there any angles near this point to put in-out path in 
										# the angle if it's possible 
										# remove last node to make iterations easier
										subpath[0][0] = subpath[-1][0]
										del subpath[-1]
										max_cross = [-1e100, None]
										for j in range(len(subpath)) :
											sp1,sp2,sp3 = subpath[j-2],subpath[j-1],subpath[j]
											if point_to_point_d2(sp2[1],p_)<max_dist**2:
												s1,s2 = csp_normalized_slope(sp1,sp2,1.), csp_normalized_slope(sp2,sp3,0.)
												max_cross = max(max_cross,[cross(s1,s2),j-1])
										# return back last point		
										subpath.append(subpath[0])
										if max_cross[1] !=None  and max_cross[0]>corner_tolerance :
											# there's an angle near the point
											j = max_cross[1]
											if j<0 : j -= 1
											if j!=0 :
												subpath	= csp_concat_subpaths(subpath[j:],subpath[:j+1])
										else :
											# have to cut path's segment
											d,i,j,t = d
											sp1,sp2,sp3 = csp_split(subpath[j-1],subpath[j],t)
											subpath = csp_concat_subpaths([sp2,sp3], subpath[j:], subpath[:j], [sp1,sp2]) 							

							if self.options.plasma_prepare_corners :
								# prepare corners
								# find corners and add some nodes
								# corner at path's start/end is ignored
								res_ = [subpath[0]]
								for sp2, sp3 in zip(subpath[1:],subpath[2:]) :
									sp1 = res_[-1]
									s1,s2 = csp_normalized_slope(sp1,sp2,1.), csp_normalized_slope(sp2,sp3,0.)
									if cross(s1,s2) > corner_tolerance :
										# got a corner to process
										S1,S2 = P(s1),P(s2)
										N = (S1-S2).unit()*plasma_l
										SP2= P(sp2[1])
										P1 = (SP2 + N)
										res_ += [
													[sp2[0],sp2[1],  (SP2+S1*plasma_l).to_list() ],
													[ (P1-N.ccw()/2 ).to_list(), P1.to_list(), (P1+N.ccw()/2).to_list()], 
													[(SP2-S2*plasma_l).to_list(), sp2[1],sp2[2]]
												]
									else: 
										res_ += [sp2]
								res_ += [sp3]
								subpath = res_
							if self.options.in_out_path :
								# finally add let's add in-out paths... 
								subpath = csp_concat_subpaths(
													add_func(subpath[0],subpath[1],False,l,r),
													subpath,
													add_func(subpath[-2],subpath[-1],True,l,r)
													)

							
							res += [ subpath ] 
							
							
						if self.options.in_out_path_replace_original_path :
							path.set("d", cubicsuperpath.formatPath( self.apply_transforms(path,res,True) ))
						else:	
							draw_csp(res, width=1, style=styles["in_out_path_style"] )
		
################################################################################
###		Arrangement: arranges paths by givven params
###		TODO move it to the bottom
################################################################################
	def arrangement(self) :
		paths = self.selected_paths
		surface = Polygon()
		polygons = []
		time_ = time.time()
		print_("Arrangement start at %s"%(time_))
		original_paths = []
		for layer in self.layers :
			if layer in paths :
				for path in paths[layer] :
					csp = cubicsuperpath.parsePath(path.get("d"))
					polygon = Polygon()
					for subpath in csp :
						for sp1, sp2 in zip(subpath,subpath[1:]) :
							polygon.add([csp_segment_convex_hull(sp1,sp2)])
					#print_("Reduced edges count from", sum([len(poly) for poly in polygon.polygon ]) )
					polygon.hull()
					original_paths += [path]
					polygons += [polygon]
					
		print_("Paths hull computed in %s sec."%(time.time()-time_))
		print_("Got %s polygons having average %s edges each."% ( len(polygons), float(sum([ sum([len(poly) for poly in polygon.polygon]) for polygon in polygons ])) / len(polygons) ) )
		time_ = time.time()
		
#		material_width = self.options.arrangement_material_width
#		population = Arangement_Genetic(polygons, material_width)
#		population.add_random_species(1)
#		population.test_population_centroid()
##		return
		material_width = self.options.arrangement_material_width
		population = Arangement_Genetic(polygons, material_width)
		
		
		print_("Genetic algorithm start at %s"%(time_))
		start_time = time.time()
		time_ = time.time()
		
	

		population.add_random_species(50)
		#population.test(population.test_spiece_centroid)
		print_("Initial population done in %s"%(time.time()-time_))
		time_ = time.time()
		pop = copy.deepcopy(population)
		population_count = self.options.arrangement_population_count
		last_champ = -1
		champions_count = 0
		
		
		
		
		for i in range(population_count):
			population.leave_top_species(20)
			population.move_mutation_multiplier = random.random()/2
			
			population.order_mutation_factor = .2
			population.move_mutation_factor = 1.
			population.mutation_genes_count = [1,2]
			population.populate_species(250, 20)
			print_("Populate done at %s"%(time.time()-time_))			
			"""
			randomize = i%100 < 40
			if 	i%100 < 40 : 
				population.add_random_species(250)
			if  40<= i%100 < 100 : 
				population.mutation_genes_count = [1,max(2,int(population.genes_count/4))]  #[1,max(2,int(population.genes_count/2))] if 40<=i%100<60 else [1,max(2,int(population.genes_count/10))]
				population.move_mutation_multiplier = 1. if 40<=i%100<80 else .1
				population.move_mutation_factor = (-(i%100)/30+10/3) if 50<=i%100<100 else .5
				population.order_mutation_factor = 1./(i%100-79) if 80<=i%100<100 else 1.
				population.populate_species(250, 10)
			"""
			if self.options.arrangement_inline_test :
				population.test_inline()
			else:		
				population.test(population.test_spiece_centroid)
				
			print_("Test done at %s"%(time.time()-time_))
			draw_new_champ = False
			print_()

			
			if population.population[0][0]!= last_champ : 
				draw_new_champ = True
				improve = last_champ-population.population[0][0]
				last_champ = population.population[0][0]*1

			
			print_("Cicle %s done in %s"%(i,time.time()-time_))
			time_ = time.time()
			print_("%s incests been found"%population.inc)
			print_()

			if i == 0  or i == population_count-1 or draw_new_champ :
				colors = ["blue"]
				
				surface = population.test_spiece_centroid(population.population[0][1])
				b = surface.bounds()
				x,y = 400* (champions_count%10), 700*int(champions_count/10)
				surface.move(x-b[0],y-b[1])
				surface.draw(width=2, color=colors[0])
				draw_text("Step = %s\nSquare = %f\nSquare improvement = %f\nTime from start = %f"%(i,(b[2]-b[0])*(b[3]-b[1]),improve,time.time()-start_time),x,y-50)
				champions_count += 1
				"""
				spiece = population.population[0][1]
				poly = Polygon(copy.deepcopy(population.polygons[spiece[0][0]].polygon))
				poly.rotate(spiece[0][2]*math.pi2)
				surface  = Polygon(poly.polygon)
				poly.draw(width = 2, color= "Violet")
				for p in spiece[1:] :
					poly = Polygon(copy.deepcopy(population.polygons[p[0]].polygon))
					poly.rotate(p[2]*math.pi2)
					direction = [math.cos(p[1]*math.pi2), -math.sin(p[1]*math.pi2)]
					normalize(direction)
					c = surface.centroid()
					c1 = poly.centroid()
					poly.move(c[0]-c1[0]-direction[0]*400,c[1]-c1[1]-direction[1]*400)
					c = surface.centroid()
					c1 = poly.centroid()
					poly.draw(width = 5, color= "Violet")
					draw_pointer(c+c1,"Green","line")
					direction = normalize(direction)
					
					
					sin,cos = direction[0], direction[1]
					poly.rotate_(-sin,cos)
					surface.rotate_(-sin,cos)
#					poly.draw(color = "Violet",width=4)					
					surface.draw(color = "Orange",width=4)					
					poly.rotate_(sin,cos)
					surface.rotate_(sin,cos)


					poly.drop_into_direction(direction,surface)
					surface.add(poly)
				
				"""
		# Now we'll need apply transforms to original paths
		
		
	def __init__(self):
		inkex.Effect.__init__(self)
		self.OptionParser.add_option("-d", "--directory",					action="store", type="string", 		dest="directory", default="/home/",					help="Directory for gcode file")
		self.OptionParser.add_option("-f", "--filename",					action="store", type="string", 		dest="file", default="-1.0",						help="File name")			
		self.OptionParser.add_option("",   "--add-numeric-suffix-to-filename", action="store", type="inkbool",	dest="add_numeric_suffix_to_filename", default=True,help="Add numeric suffix to filename")			
		self.OptionParser.add_option("",   "--Zscale",						action="store", type="float", 		dest="Zscale", default="1.0",						help="Scale factor Z")				
		self.OptionParser.add_option("",   "--Zoffset",						action="store", type="float", 		dest="Zoffset", default="0.0",						help="Offset along Z")
		self.OptionParser.add_option("-s", "--Zsafe",						action="store", type="float", 		dest="Zsafe", default="0.5",						help="Z above all obstacles")
		self.OptionParser.add_option("-z", "--Zsurface",					action="store", type="float", 		dest="Zsurface", default="0.0",						help="Z of the surface")
		self.OptionParser.add_option("-c", "--Zdepth",						action="store", type="float", 		dest="Zdepth", default="-0.125",					help="Z depth of cut")
		self.OptionParser.add_option("",   "--Zstep",						action="store", type="float", 		dest="Zstep", default="-0.125",						help="Z step of cutting")		
		self.OptionParser.add_option("-p", "--feed",						action="store", type="float", 		dest="feed", default="4.0",							help="Feed rate in unit/min")

		self.OptionParser.add_option("",   "--biarc-tolerance",				action="store", type="float", 		dest="biarc_tolerance", default="1",				help="Tolerance used when calculating biarc interpolation.")				
		self.OptionParser.add_option("",   "--biarc-max-split-depth",		action="store", type="int", 		dest="biarc_max_split_depth", default="4",			help="Defines maximum depth of splitting while approximating using biarcs.")				
		self.OptionParser.add_option("",   "--path-to-gcode-order",			action="store", type="string", 		dest="path_to_gcode_order", default="path by path",	help="Defines cutting order path by path or layer by layer.")				
		self.OptionParser.add_option("",   "--path-to-gcode-depth-function",action="store", type="string", 		dest="path_to_gcode_depth_function", default="zd",	help="Path to gcode depth function.")				
		self.OptionParser.add_option("",   "--path-to-gcode-sort-paths",	action="store", type="inkbool",		dest="path_to_gcode_sort_paths", default=True,		help="Sort paths to reduce rapid distance.")		
		self.OptionParser.add_option("",   "--comment-gcode",				action="store", type="string", 		dest="comment_gcode", default="",					help="Comment Gcode")				
		self.OptionParser.add_option("",   "--comment-gcode-from-properties",action="store", type="inkbool", 	dest="comment_gcode_from_properties", default=False,help="Get additional comments from Object Properties")				



		self.OptionParser.add_option("",   "--tool-diameter",				action="store", type="float", 		dest="tool_diameter", default="3",					help="Tool diameter used for area cutting")		
		self.OptionParser.add_option("",   "--max-area-curves",				action="store", type="int", 		dest="max_area_curves", default="100",				help="Maximum area curves for each area")
		self.OptionParser.add_option("",   "--area-inkscape-radius",		action="store", type="float", 		dest="area_inkscape_radius", default="0",			help="Area curves overlaping (depends on tool diameter [0,0.9])")
		self.OptionParser.add_option("",   "--area-tool-overlap",			action="store", type="float", 		dest="area_tool_overlap", default="-10",			help="Radius for preparing curves using inkscape")
		self.OptionParser.add_option("",   "--unit",						action="store", type="string", 		dest="unit", default="G21 (All units in mm)",		help="Units")
		self.OptionParser.add_option("",   "--active-tab",					action="store", type="string", 		dest="active_tab", default="",						help="Defines which tab is active")

		self.OptionParser.add_option("",   "--area-fill-angle",				action="store", type="float", 		dest="area_fill_angle", default="0",					help="Fill area with lines heading this angle")
		self.OptionParser.add_option("",   "--area-fill-shift",				action="store", type="float", 		dest="area_fill_shift", default="0",					help="Shift the lines by tool d * shift")
		self.OptionParser.add_option("",   "--area-fill-method",			action="store", type="string", 		dest="area_fill_method", default="zig-zag",					help="Filling method either zig-zag or spiral")

		self.OptionParser.add_option("",   "--area-find-artefacts-diameter",action="store", type="float", 		dest="area_find_artefacts_diameter", default="1",					help="Artefacts seeking radius")
		self.OptionParser.add_option("",   "--area-find-artefacts-action",	action="store", type="string",	 	dest="area_find_artefacts_action", default="mark with an arrow",	help="Artefacts action type")

		self.OptionParser.add_option("",   "--auto_select_paths",			action="store", type="inkbool",		dest="auto_select_paths", default=True,				help="Select all paths if nothing is selected.")		

		self.OptionParser.add_option("",   "--loft-distances",				action="store", type="string", 		dest="loft_distances", default="10",				help="Distances between paths.")
		self.OptionParser.add_option("",   "--loft-direction",				action="store", type="string", 		dest="loft_direction", default="crosswise",			help="Direction of loft's interpolation.")
		self.OptionParser.add_option("",   "--loft-interpolation-degree",	action="store", type="float",		dest="loft_interpolation_degree", default="2",		help="Which interpolation use to loft the paths smooth interpolation or staright.")

		self.OptionParser.add_option("",   "--min-arc-radius",				action="store", type="float", 		dest="min_arc_radius", default=".1",				help="All arc having radius less than minimum will be considered as straight line")		

		self.OptionParser.add_option("",   "--engraving-sharp-angle-tollerance",action="store", type="float",	dest="engraving_sharp_angle_tollerance", default="150",		help="All angles thar are less than engraving-sharp-angle-tollerance will be thought sharp")		
		self.OptionParser.add_option("",   "--engraving-max-dist",			action="store", type="float", 		dest="engraving_max_dist", default="10",					help="Distance from original path where engraving is not needed (usually it's cutting tool diameter)")		
		self.OptionParser.add_option("",   "--engraving-newton-iterations", action="store", type="int", 		dest="engraving_newton_iterations", default="4",			help="Number of sample points used to calculate distance")		
		self.OptionParser.add_option("",   "--engraving-draw-calculation-paths",action="store", type="inkbool",	dest="engraving_draw_calculation_paths", default=False,		help="Draw additional graphics to debug engraving path")		
		self.OptionParser.add_option("",   "--engraving-cutter-shape-function",action="store", type="string", 	dest="engraving_cutter_shape_function", default="w",		help="Cutter shape function z(w). Ex. cone: w. ")

		self.OptionParser.add_option("",   "--lathe-width",					action="store", type="float", 		dest="lathe_width", default=10.,							help="Lathe width")
		self.OptionParser.add_option("",   "--lathe-fine-cut-width",		action="store", type="float", 		dest="lathe_fine_cut_width", default=1.,					help="Fine cut width")
		self.OptionParser.add_option("",   "--lathe-fine-cut-count",		action="store", type="int", 		dest="lathe_fine_cut_count", default=1.,					help="Fine cut count")
		self.OptionParser.add_option("",   "--lathe-create-fine-cut-using",	action="store", type="string",		dest="lathe_create_fine_cut_using", default="Move path",			help="Create fine cut using")
		self.OptionParser.add_option("",   "--lathe-x-axis-remap",			action="store", type="string", 		dest="lathe_x_axis_remap", default="X",						help="Lathe X axis remap")
		self.OptionParser.add_option("",   "--lathe-z-axis-remap",			action="store", type="string", 		dest="lathe_z_axis_remap", default="Z",						help="Lathe Z axis remap")

		self.OptionParser.add_option("",   "--lathe-rectangular-cutter-width",action="store", type="float", 	dest="lathe_rectangular_cutter_width", default="4",		help="Rectangular cutter width")

		self.OptionParser.add_option("",   "--create-log",					action="store", type="inkbool", 	dest="log_create_log", default=False,				help="Create log files")
		self.OptionParser.add_option("",   "--log-filename",				action="store", type="string", 		dest="log_filename", default='',					help="Create log files")

		self.OptionParser.add_option("",   "--orientation-points-count",	action="store", type="string", 		dest="orientation_points_count", default="2",			help="Orientation points count")
		self.OptionParser.add_option("",   "--tools-library-type",			action="store", type="string", 		dest="tools_library_type", default='cylinder cutter',	help="Create tools definition")

		self.OptionParser.add_option("",   "--dxfpoints-action",			action="store", type="string", 		dest="dxfpoints_action", default='replace',			help="dxfpoint sign toggle")
																										  
		self.OptionParser.add_option("",   "--help-language",				action="store", type="string", 		dest="help_language", default='http://www.cnc-club.ru/forum/viewtopic.php?f=33&t=35',	help="Open help page in webbrowser.")

		self.OptionParser.add_option("",   "--offset-radius",				action="store", type="float", 		dest="offset_radius", default=10.,		help="Offset radius")
		self.OptionParser.add_option("",   "--offset-step",					action="store", type="float", 		dest="offset_step", default=10.,		help="Offset step")
		self.OptionParser.add_option("",   "--offset-draw-clippend-path",	action="store", type="inkbool",		dest="offset_draw_clippend_path", default=False,		help="Draw clipped path")		
		self.OptionParser.add_option("",   "--offset-just-get-distance",	action="store", type="inkbool",		dest="offset_just_get_distance", default=False,		help="Don't do offset just get distance")		
	
		self.OptionParser.add_option("",   "--arrangement-material-width",	action="store", type="float",		dest="arrangement_material_width", default=500,		help="Materials width for arrangement")		
		self.OptionParser.add_option("",   "--arrangement-population-count",action="store", type="int",			dest="arrangement_population_count", default=100,	help="Genetic algorithm populations count")		
		self.OptionParser.add_option("",   "--arrangement-inline-test",		action="store", type="inkbool", 	dest="arrangement_inline_test", default=False,	help="Use C-inline test (some additional packets will be needed)")


		self.OptionParser.add_option("",   "--postprocessor",				action="store", type="string", 		dest="postprocessor", default='',			help="Postprocessor command.")
		self.OptionParser.add_option("",   "--postprocessor-custom",		action="store", type="string", 		dest="postprocessor_custom", default='',	help="Postprocessor custom command.")
	
		self.OptionParser.add_option("",   "--graffiti-max-seg-length",		action="store", type="float", 		dest="graffiti_max_seg_length", default=1.,	help="Graffiti maximum segment length.")
		self.OptionParser.add_option("",   "--graffiti-min-radius",			action="store", type="float", 		dest="graffiti_min_radius", default=10.,	help="Graffiti minimal connector's radius.")
		self.OptionParser.add_option("",   "--graffiti-start-pos",			action="store", type="string", 		dest="graffiti_start_pos", default="(0;0)",	help="Graffiti Start position (x;y).")
		self.OptionParser.add_option("",   "--graffiti-create-linearization-preview",	action="store", type="inkbool", 	dest="graffiti_create_linearization_preview", default=True,	help="Graffiti create linearization preview.")
		self.OptionParser.add_option("",   "--graffiti-create-preview",		action="store", type="inkbool", 	dest="graffiti_create_preview", default=True,	help="Graffiti create preview.")
		self.OptionParser.add_option("",   "--graffiti-preview-size",		action="store", type="int", 		dest="graffiti_preview_size", default=800,	help="Graffiti preview's size.")
		self.OptionParser.add_option("",   "--graffiti-preview-emmit",		action="store", type="int", 		dest="graffiti_preview_emmit", default=800,	help="Preview's paint emmit (pts/s).")


		self.OptionParser.add_option("",   "--in-out-path",					action="store", type="inkbool", 	dest="in_out_path",	default=True,			help="Create in-out paths")
		self.OptionParser.add_option("",   "--in-out-path-do-not-add-reference-point",	action="store", type="inkbool", dest="in_out_path_do_not_add_reference_point", default=False,	help="Just add reference in-out point")
		self.OptionParser.add_option("",   "--in-out-path-point-max-dist",	action="store", type="float", 		dest="in_out_path_point_max_dist", default=10.,	help="In-out path max distance to reference point")
		self.OptionParser.add_option("",   "--in-out-path-type",			action="store", type="string", 		dest="in_out_path_type", default="Round",	help="In-out path type")
		self.OptionParser.add_option("",   "--in-out-path-len",				action="store", type="float", 		dest="in_out_path_len", default=10.,		help="In-out path length")
		self.OptionParser.add_option("",   "--in-out-path-replace-original-path",action="store", type="inkbool", dest="in_out_path_replace_original_path", default=False,	help="Replace original path")
		self.OptionParser.add_option("",   "--in-out-path-radius",			action="store", type="float", 		dest="in_out_path_radius", default=10.,		help="In-out path radius for round path")

		self.OptionParser.add_option("",   "--plasma-prepare-corners",		action="store", type="inkbool",		dest="plasma_prepare_corners", default=True,	help="Prepare corners")
		self.OptionParser.add_option("",   "--plasma-prepare-corners-distance", action="store", type="float",	dest="plasma_prepare_corners_distance", default=10.,help="Stepout distance for corners")
		self.OptionParser.add_option("",   "--plasma-prepare-corners-tolerance", action="store", type="float",	dest="plasma_prepare_corners_tolerance", default=10.,help="Maximum angle for corner (0-180 deg)")

		self.default_tool = {
					"name": "Default tool",
					"id": "default tool",
					"diameter":10.,
					"shape": "10",
					"penetration angle":90.,
					"penetration feed":100.,
					"depth step":1.,
					"feed":400.,
					"in trajectotry":"",
					"out trajectotry":"",
					"gcode before path":"",
					"gcode after path":"",
					"sog":"",
					"spinlde rpm":"",
					"CW or CCW":"",
					"tool change gcode":" ",
					"4th axis meaning": " ",
					"4th axis scale": 1.,
					"4th axis offset": 0.,
					"passing feed":"800",					
					"fine feed":"800",					
				}			
		self.tools_field_order = [
					'name',
					'id',
					'diameter',
					'feed',
					'shape',
					'penetration angle',
					'penetration feed',
					"passing feed",
					'depth step',
					"in trajectotry",
					"out trajectotry",
					"gcode before path",
					"gcode after path",
					"sog",
					"spinlde rpm",
					"CW or CCW",
					"tool change gcode",
				]


	def parse_curve(self, p, layer, w = None, f = None):
			c = []
			if len(p)==0 : 
				return []
			p = self.transform_csp(p, layer)
			

			### Sort to reduce Rapid distance	
			k = range(1,len(p))
			keys = [0]
			while len(k)>0:
				end = p[keys[-1]][-1][1]
				dist = None
				for i in range(len(k)):
					start = p[k[i]][0][1]
					dist = max(   ( -( ( end[0]-start[0])**2+(end[1]-start[1])**2 ) ,i)	,   dist )
				keys += [k[dist[1]]]
				del k[dist[1]]
			for k in keys:
				subpath = p[k]
				c += [ [	[subpath[0][1][0],subpath[0][1][1]]   , 'move', 0, 0] ]
				for i in range(1,len(subpath)):
					sp1 = [  [subpath[i-1][j][0], subpath[i-1][j][1]] for j in range(3)]
					sp2 = [  [subpath[i  ][j][0], subpath[i  ][j][1]] for j in range(3)]
					c += biarc(sp1,sp2,0,0) if w==None else biarc(sp1,sp2,-f(w[k][i-1]),-f(w[k][i]))
#					l1 = biarc(sp1,sp2,0,0) if w==None else biarc(sp1,sp2,-f(w[k][i-1]),-f(w[k][i]))
#					print_((-f(w[k][i-1]),-f(w[k][i]), [i1[5] for i1 in l1]) )
				c += [ [ [subpath[-1][1][0],subpath[-1][1][1]]  ,'end',0,0] ]
			return c


################################################################################
### 	Draw csp 
################################################################################

	def draw_csp(self, csp, layer=None, group=None, fill='none', stroke='#178ade', width=0.354, style=None):
		if layer!=None :
			csp = self.transform_csp(csp,layer,reverse=True)
		if group==None and layer==None:
			group = self.document.getroot()
		elif group==None and layer!=None :
			group = layer
		csp = self.apply_transforms(group,csp, reverse=True)
		if style!=None :
			return draw_csp(csp, group=group, style=style)
		else :
			return draw_csp(csp, group=group, fill=fill, stroke=stroke, width=width)
				



	def draw_curve(self, curve, layer, group=None, style=styles["biarc_style"]):
		self.set_markers()

		for i in [0,1]:
			style['biarc%s_r'%i] = simplestyle.parseStyle(style['biarc%s'%i])
			style['biarc%s_r'%i]["marker-start"] = "url(#DrawCurveMarker_r)"
			del(style['biarc%s_r'%i]["marker-end"])
			style['biarc%s_r'%i] = simplestyle.formatStyle(style['biarc%s_r'%i])
		
		if group==None:
			if "preview_groups" not in dir(self) :
				self.preview_groups = { layer: inkex.etree.SubElement( self.layers[min(1,len(self.layers)-1)], inkex.addNS('g','svg'), {"gcodetools": "Preview group"} ) }
			elif layer not in self.preview_groups :
				self.preview_groups[layer] = inkex.etree.SubElement( self.layers[min(1,len(self.layers)-1)], inkex.addNS('g','svg'), {"gcodetools": "Preview group"} )
			group = self.preview_groups[layer]

		s, arcn = '', 0
		
		transform = self.get_transforms(group)
		if transform != [] : 
			transform = self.reverse_transform(transform)
			transform = simpletransform.formatTransform(transform)
		
		a,b,c = [0.,0.], [1.,0.], [0.,1.]
		k = (b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1])
		a,b,c = self.transform(a, layer, True), self.transform(b, layer, True), self.transform(c, layer, True)
		if ((b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1]))*k > 0 : reverse_angle = 1
		else : reverse_angle = -1 
		for sk in curve:
			si = sk[:]
			si[0], si[2] = self.transform(si[0], layer, True), (self.transform(si[2], layer, True) if type(si[2])==type([]) and len(si[2])==2 else si[2])
			
			if s!='':
				if s[1] == 'line':
					attr = {	'style': style['line'],
								'd':'M %s,%s L %s,%s' % (s[0][0], s[0][1], si[0][0], si[0][1]),
								"gcodetools": "Preview",
							}
					if transform != [] :
						attr["transform"] = transform		
					inkex.etree.SubElement(	group, inkex.addNS('path','svg'),  attr	)
				elif s[1] == 'arc':
					arcn += 1
					sp = s[0]
					c = s[2]
					s[3] = s[3]*reverse_angle
						
					a =  ( (P(si[0])-P(c)).angle() - (P(s[0])-P(c)).angle() )%math.pi2 #s[3]
					if s[3]*a<0: 
							if a>0:	a = a-math.pi2
							else: a = math.pi2+a
					r = math.sqrt( (sp[0]-c[0])**2 + (sp[1]-c[1])**2 )
					a_st = ( math.atan2(sp[0]-c[0],- (sp[1]-c[1])) - math.pi/2 ) % (math.pi*2)
					st = style['biarc%s' % (arcn%2)][:]
					if a>0:
						a_end = a_st+a
						st = style['biarc%s'%(arcn%2)]
					else: 
						a_end = a_st*1
						a_st = a_st+a
						st = style['biarc%s_r'%(arcn%2)]
					
					attr = {
							'style': st,
							 inkex.addNS('cx','sodipodi'):		str(c[0]),
							 inkex.addNS('cy','sodipodi'):		str(c[1]),
							 inkex.addNS('rx','sodipodi'):		str(r),
							 inkex.addNS('ry','sodipodi'):		str(r),
							 inkex.addNS('start','sodipodi'):	str(a_st),
							 inkex.addNS('end','sodipodi'):		str(a_end),
							 inkex.addNS('open','sodipodi'):	'true',
							 inkex.addNS('type','sodipodi'):	'arc',
							 "gcodetools": "Preview",
							}	
						
					if transform != [] :
						attr["transform"] = transform	
					inkex.etree.SubElement(	group, inkex.addNS('path','svg'), attr)
			s = si
	

	def check_dir(self):
		if self.options.directory[-1] not in ["/","\\"]:
			if "\\" in self.options.directory :
				self.options.directory += "\\"
			else :
				self.options.directory += "/"
		print_("Checking directory: '%s'"%self.options.directory)
		if (os.path.isdir(self.options.directory)):
			if (os.path.isfile(self.options.directory+'header')):
				f = open(self.options.directory+'header', 'r')
				self.header = f.read()
				f.close()
			else:
				self.header = defaults['header']
			if (os.path.isfile(self.options.directory+'footer')):
				f = open(self.options.directory+'footer','r')
				self.footer = f.read()
				f.close()
			else:
				self.footer = defaults['footer']
			self.header += self.options.unit + "\n" 
		else: 
			self.error(_("Directory does not exist! Please specify existing directory at Preferences tab!"),"error")
			return False

		if self.options.add_numeric_suffix_to_filename :
			dir_list = os.listdir(self.options.directory)
			if "." in self.options.file : 
				r = re.match(r"^(.*)(\..*)$",self.options.file)
				ext = r.group(2)
				name = r.group(1)
			else: 	
				ext = ""
				name = self.options.file
			max_n = 0
			for s in dir_list :
				r = re.match(r"^%s_0*(\d+)%s$"%(re.escape(name),re.escape(ext) ), s)
				if r :
					max_n = max(max_n,int(r.group(1)))
			filename = name + "_" + ( "0"*(4-len(str(max_n+1))) + str(max_n+1) ) + ext
			self.options.file = filename

		if self.options.directory[-1] not in ["/","\\"]:
			if "\\" in self.options.directory :
				self.options.directory += "\\"
			else :
				self.options.directory += "/"

		try: 	
			f = open(self.options.directory+self.options.file, "w")	
			f.close()							
		except:
			self.error(_("Can not write to specified file!\n%s"%(self.options.directory+self.options.file)),"error")
			return False
		return True
			


################################################################################
###
###		Generate Gcode
###		Generates Gcode on given curve.
###
###		Curve definition [start point, type = {'arc','line','move','end'}, arc center, arc angle, end point, [zstart, zend]]		
###
################################################################################
	def generate_gcode(self, curve, layer, depth):
		Zauto_scale = self.Zauto_scale[layer]
		tool = self.tools[layer][0]
		g = ""

		def c(c):
			c = [c[i] if i<len(c) else None for i in range(6)]
			if c[5] == 0 : c[5]=None
			s,s1 = [" X", " Y", " Z", " I", " J", " K"], ["","","","","",""]
			m,a = [1,1,self.options.Zscale*Zauto_scale,1,1,self.options.Zscale*Zauto_scale], [0,0,self.options.Zoffset,0,0,0]
			r = ''	
			for i in range(6):
				if c[i]!=None:
					r += s[i] + ("%f" % (c[i]*m[i]+a[i])) + s1[i]
			return r

		def calculate_angle(a, current_a):
			return  min(					
						[abs(a-current_a%math.pi2+math.pi2), a+current_a-current_a%math.pi2+math.pi2],
						[abs(a-current_a%math.pi2-math.pi2), a+current_a-current_a%math.pi2-math.pi2],
						[abs(a-current_a%math.pi2),			 a+current_a-current_a%math.pi2])[1]
		if len(curve)==0 : return ""	
				
		try :
			self.last_used_tool == None
		except :
			self.last_used_tool = None
		print_("working on curve")
		print_(curve)
		
		if tool != self.last_used_tool :
			g += ( "(Change tool to %s)\n" % re.sub("\"'\(\)\\\\"," ",tool["name"]) ) + tool["tool change gcode"] + "\n"

		lg, zs, f =  'G00', self.options.Zsafe, " F%f"%tool['feed'] 
		current_a = 0
		go_to_safe_distance = "G00" + c([None,None,zs]) + "\n" 
		penetration_feed = " F%s"%tool['penetration feed'] 
		for i in range(1,len(curve)):
		#	Creating Gcode for curve between s=curve[i-1] and si=curve[i] start at s[0] end at s[4]=si[0]
			s, si = curve[i-1], curve[i]
			feed = f if lg not in ['G01','G02','G03'] else ''
			if s[1]	== 'move':
				g += go_to_safe_distance + "G00" + c(si[0]) + "\n" + tool['gcode before path'] + "\n"
				lg = 'G00'
			elif s[1] == 'end':
				g += go_to_safe_distance + tool['gcode after path'] + "\n"
				lg = 'G00'
			elif s[1] == 'line':
				if tool['4th axis meaning'] == "tangent knife" : 
					a = atan2(si[0][0]-s[0][0],si[0][1]-s[0][1])
					a = calculate_angle(a, current_a)
					g+="G01 A%s\n" % (a*tool['4th axis scale']+tool['4th axis offset'])
					current_a = a
				if lg=="G00": g += "G01" + c([None,None,s[5][0]+depth]) + penetration_feed +"(Penetrate)\n"	
				g += "G01" +c(si[0]+[s[5][1]+depth]) + feed + "\n"
				lg = 'G01'
			elif s[1] == 'arc':
				r = [(s[2][0]-s[0][0]), (s[2][1]-s[0][1])]
				if tool['4th axis meaning'] == "tangent knife" : 
					if s[3]<0 : # CW
						a1 = atan2(s[2][1]-s[0][1],-s[2][0]+s[0][0]) + math.pi 
					else: #CCW
						a1 = atan2(-s[2][1]+s[0][1],s[2][0]-s[0][0]) + math.pi
					a = calculate_angle(a1, current_a)
					g+="G01 A%s\n" % (a*tool['4th axis scale']+tool['4th axis offset'])
					current_a = a
					axis4 = " A%s"%((current_a+s[3])*tool['4th axis scale']+tool['4th axis offset'])
					current_a = current_a+s[3]
				else : axis4 = ""
				if lg=="G00": g += "G01" + c([None,None,s[5][0]+depth]) + penetration_feed + "(Penetrate)\n"				
				if (r[0]**2 + r[1]**2)>self.options.min_arc_radius**2:
					r1, r2 = (P(s[0])-P(s[2])), (P(si[0])-P(s[2]))
					if abs(r1.mag()-r2.mag()) < 0.001 :
						g += ("G02" if s[3]<0 else "G03") + c(si[0]+[ s[5][1]+depth, (s[2][0]-s[0][0]),(s[2][1]-s[0][1])  ]) + feed + axis4 + "\n"
					else:
						r = (r1.mag()+r2.mag())/2
						g += ("G02" if s[3]<0 else "G03") + c(si[0]+[s[5][1]+depth]) + " R%f" % (r) + feed  + axis4 + "\n"
					lg = 'G02'
				else:
					if tool['4th axis meaning'] == "tangent knife" : 
						a = atan2(si[0][0]-s[0][0],si[0][1]-s[0][1]) + math.pi
						a = calculate_angle(a, current_a)
						g+="G01 A%s\n" % (a*tool['4th axis scale']+tool['4th axis offset'])
						current_a = a
					g += "G01" +c(si[0]+[s[5][1]+depth]) + feed + "\n"
					lg = 'G01'
		if si[1] == 'end':
			g += go_to_safe_distance + tool['gcode after path'] + "\n"
		return g


	def get_transforms(self,g):
		root = self.document.getroot()
		trans = []
		while (g!=root):
			if 'transform' in g.keys():
				t = g.get('transform')
				t = simpletransform.parseTransform(t)
				trans = simpletransform.composeTransform(t,trans) if trans != [] else t
				print_(trans)
			g=g.getparent()
		return trans 
	
	def reverse_transform(self,transform):
		trans = numpy.array(transform + [[0,0,1]])
		if numpy.linalg.det(trans)!=0 :
			trans = numpy.linalg.inv(trans).tolist()[:2]		
			return trans
		else :
		 return transform
		

	def apply_transforms(self,g,csp, reverse=False):
		trans = self.get_transforms(g)
		if trans != []:
			if not reverse :
				simpletransform.applyTransformToPath(trans, csp)
			else :
				simpletransform.applyTransformToPath(self.reverse_transform(trans), csp)
		return csp
		
		

	def transform_scalar(self,x,layer,reverse=False):
		return self.transform([x,0],layer,reverse)[0] - self.transform([0,0],layer,reverse)[0]

	def transform(self,source_point, layer, reverse=False):
		if layer not in self.transform_matrix:
			for i in range(self.layers.index(layer),-1,-1):
				if self.layers[i] in self.orientation_points : 
					break
			if self.layers[i] not in self.orientation_points :
				self.error(_("Orientation points for '%s' layer have not been found! Please add orientation points using Orientation tab!") % layer.get(inkex.addNS('label','inkscape')),"no_orientation_points")
			elif self.layers[i] in self.transform_matrix :
				self.transform_matrix[layer] = self.transform_matrix[self.layers[i]]
				self.Zcoordinates[layer] = self.Zcoordinates[self.layers[i]]
			else :
				orientation_layer = self.layers[i]
				if len(self.orientation_points[orientation_layer])>1 : 
					self.error(_("There are more than one orientation point groups in '%s' layer") % orientation_layer.get(inkex.addNS('label','inkscape')),"more_than_one_orientation_point_groups")
				points = self.orientation_points[orientation_layer][0]
				if len(points)==2:
					points += [ [ [(points[1][0][1]-points[0][0][1])+points[0][0][0], -(points[1][0][0]-points[0][0][0])+points[0][0][1]], [-(points[1][1][1]-points[0][1][1])+points[0][1][0], points[1][1][0]-points[0][1][0]+points[0][1][1]] ] ]
				if len(points)==3:
					print_("Layer '%s' Orientation points: " % orientation_layer.get(inkex.addNS('label','inkscape')))
					for point in points:
						print_(point)
					#	Zcoordinates definition taken from Orientatnion point 1 and 2 
					self.Zcoordinates[layer] = [max(points[0][1][2],points[1][1][2]), min(points[0][1][2],points[1][1][2])]
					matrix = numpy.array([
								[points[0][0][0], points[0][0][1], 1, 0, 0, 0, 0, 0, 0],
								[0, 0, 0, points[0][0][0], points[0][0][1], 1, 0, 0, 0],
								[0, 0, 0, 0, 0, 0, points[0][0][0], points[0][0][1], 1],
								[points[1][0][0], points[1][0][1], 1, 0, 0, 0, 0, 0, 0],
								[0, 0, 0, points[1][0][0], points[1][0][1], 1, 0, 0, 0],
								[0, 0, 0, 0, 0, 0, points[1][0][0], points[1][0][1], 1],
								[points[2][0][0], points[2][0][1], 1, 0, 0, 0, 0, 0, 0],
								[0, 0, 0, points[2][0][0], points[2][0][1], 1, 0, 0, 0],
								[0, 0, 0, 0, 0, 0, points[2][0][0], points[2][0][1], 1]
							])
								
					if numpy.linalg.det(matrix)!=0 :
						m = numpy.linalg.solve(matrix,
							numpy.array(
								[[points[0][1][0]], [points[0][1][1]], [1], [points[1][1][0]], [points[1][1][1]], [1], [points[2][1][0]], [points[2][1][1]], [1]]	
										)
							).tolist()
						self.transform_matrix[layer] = [[m[j*3+i][0] for i in range(3)] for j in range(3)]
					
					else :
						self.error(_("Orientation points are wrong! (if there are two orientation points they should not be the same. If there are three orientation points they should not be in a straight line.)"),"wrong_orientation_points")
				else :
					self.error(_("Orientation points are wrong! (if there are two orientation points they should not be the same. If there are three orientation points they should not be in a straight line.)"),"wrong_orientation_points")

			self.transform_matrix_reverse[layer] = numpy.linalg.inv(self.transform_matrix[layer]).tolist()		
			print_("\n Layer '%s' transformation matrixes:" % layer.get(inkex.addNS('label','inkscape')) )
			print_(self.transform_matrix)
			print_(self.transform_matrix_reverse)

			###self.Zauto_scale[layer]  = math.sqrt( (self.transform_matrix[layer][0][0]**2 + self.transform_matrix[layer][1][1]**2)/2 )
			### Zautoscale is absolete
			self.Zauto_scale[layer] = 1
			print_("Z automatic scale = %s (computed according orientation points)" % self.Zauto_scale[layer])

		x,y = source_point[0], source_point[1]
		if not reverse :
			t = self.transform_matrix[layer]
		else :
			t = self.transform_matrix_reverse[layer]
		return [t[0][0]*x+t[0][1]*y+t[0][2], t[1][0]*x+t[1][1]*y+t[1][2]]


	def transform_csp(self, csp_, layer, reverse = False):
		csp = [  [ [csp_[i][j][0][:],csp_[i][j][1][:],csp_[i][j][2][:]]  for j in range(len(csp_[i])) ]   for i in range(len(csp_)) ]
		for i in xrange(len(csp)):
			for j in xrange(len(csp[i])): 
				for k in xrange(len(csp[i][j])): 
					csp[i][j][k] = self.transform(csp[i][j][k],layer, reverse)
		return csp
	
		
################################################################################
###		Errors handling function, notes are just printed into Logfile, 
###		warnings are printed into log file and warning message is displayed but
###		extension continues working, errors causes log and execution is halted
###		Notes, warnings and errors could be assigned to space or comma or dot 
###		sepparated strings (case is ignoreg).
################################################################################
	def error(self, s, type_= "Warning"):
		notes = "Note "
		warnings = """
						Warning tools_warning
						orientation_warning
						bad_orientation_points_in_some_layers
						more_than_one_orientation_point_groups
						more_than_one_tool
						orientation_have_not_been_defined
						tool_have_not_been_defined
						selection_does_not_contain_paths
						selection_does_not_contain_paths_will_take_all
						selection_is_empty_will_comupe_drawing
						selection_contains_objects_that_are_not_paths
						Continue
						"""
		errors = """
						Error 	
						wrong_orientation_points	
						area_tools_diameter_error
						no_tool_error
						active_layer_already_has_tool
						active_layer_already_has_orientation_points
					"""
		s = str(s)
		if type_.lower() in re.split("[\s\n,\.]+", errors.lower()) :
			print_(s)
			inkex.errormsg(s+"\n")		
			sys.exit()
		elif type_.lower() in re.split("[\s\n,\.]+", warnings.lower()) :
			print_(s)
			inkex.errormsg(s+"\n")		
		elif type_.lower() in re.split("[\s\n,\.]+", notes.lower()) :
			print_(s)
		else :
			print_(s)
			inkex.errormsg(s)		
			sys.exit()
	

################################################################################
###		Set markers
################################################################################
	def set_markers(self) :
		self.get_defs()
		# Add marker to defs if it doesnot exists
		if "CheckToolsAndOPMarker" not in self.defs : 
			defs = inkex.etree.SubElement( self.document.getroot(), inkex.addNS("defs","svg"))
			marker = inkex.etree.SubElement( defs, inkex.addNS("marker","svg"), {"id":"CheckToolsAndOPMarker","orient":"auto","refX":"-4","refY":"-1.687441","style":"overflow:visible"})
			inkex.etree.SubElement( marker, inkex.addNS("path","svg"), 
	
					{	"d":"	m -4.588864,-1.687441 0.0,0.0 L -9.177728,0.0 c 0.73311,-0.996261 0.728882,-2.359329 0.0,-3.374882",
						"style": "fill:#000044; fill-rule:evenodd;stroke:none;"	}
				)

		if "DrawCurveMarker" not in self.defs : 
			defs = inkex.etree.SubElement( self.document.getroot(), inkex.addNS("defs","svg"))
			marker = inkex.etree.SubElement( defs, inkex.addNS("marker","svg"), {"id":"DrawCurveMarker","orient":"auto","refX":"-4","refY":"-1.687441","style":"overflow:visible"})
			inkex.etree.SubElement( marker, inkex.addNS("path","svg"), 
					{	"d":"m -4.588864,-1.687441 0.0,0.0 L -9.177728,0.0 c 0.73311,-0.996261 0.728882,-2.359329 0.0,-3.374882",
						"style": "fill:#000044; fill-rule:evenodd;stroke:none;"	}
				)

		if "DrawCurveMarker_r" not in self.defs : 
			defs = inkex.etree.SubElement( self.document.getroot(), inkex.addNS("defs","svg"))
			marker = inkex.etree.SubElement( defs, inkex.addNS("marker","svg"), {"id":"DrawCurveMarker_r","orient":"auto","refX":"4","refY":"-1.687441","style":"overflow:visible"})
			inkex.etree.SubElement( marker, inkex.addNS("path","svg"), 
					{	"d":"m 4.588864,-1.687441 0.0,0.0 L 9.177728,0.0 c -0.73311,-0.996261 -0.728882,-2.359329 0.0,-3.374882",
						"style": "fill:#000044; fill-rule:evenodd;stroke:none;"	}
				)

		if "InOutPathMarker" not in self.defs : 
			defs = inkex.etree.SubElement( self.document.getroot(), inkex.addNS("defs","svg"))
			marker = inkex.etree.SubElement( defs, inkex.addNS("marker","svg"), {"id":"InOutPathMarker","orient":"auto","refX":"-4","refY":"-1.687441","style":"overflow:visible"})
			inkex.etree.SubElement( marker, inkex.addNS("path","svg"), 
					{	"d":"m -4.588864,-1.687441 0.0,0.0 L -9.177728,0.0 c 0.73311,-0.996261 0.728882,-2.359329 0.0,-3.374882",
						"style": "fill:#0072a7; fill-rule:evenodd;stroke:none;"	}
				)


	
################################################################################
###		Get defs from svg
################################################################################
	def get_defs(self) :
		self.defs = {}
		def recursive(g) :
			for i in g:
				if i.tag == inkex.addNS("defs","svg") : 
					for j in i: 
						self.defs[j.get("id")] = i
				if i.tag ==inkex.addNS("g",'svg') :
					recursive(i)
		recursive(self.document.getroot())


################################################################################
###
###		Get Gcodetools info from the svg
###
################################################################################
	def get_info(self):
		self.selected_paths = {}
		self.paths = {}		
		self.tools = {}
		self.orientation_points = {}
		self.graffiti_reference_points = {}
		self.layers = [self.document.getroot()]
		self.Zcoordinates = {}
		self.transform_matrix = {}
		self.transform_matrix_reverse = {}
		self.Zauto_scale = {}
		self.in_out_reference_points = []
		self.my3Dlayer = None

		def recursive_search(g, layer, selected=False):
			items = g.getchildren()
			items.reverse()
			for i in items:
				if selected:
					self.selected[i.get("id")] = i
				if i.tag == inkex.addNS("g",'svg') and i.get(inkex.addNS('groupmode','inkscape')) == 'layer':
					if i.get(inkex.addNS('label','inkscape')) == '3D' :
						self.my3Dlayer=i
					else :
						self.layers += [i]
						recursive_search(i,i)

				elif i.get('gcodetools') == "Gcodetools orientation group" :
					points = self.get_orientation_points(i)
					if points != None :
						self.orientation_points[layer] = self.orientation_points[layer]+[points[:]] if layer in self.orientation_points else [points[:]]
						print_("Found orientation points in '%s' layer: %s" % (layer.get(inkex.addNS('label','inkscape')), points))
					else :
						self.error(_("Warning! Found bad orientation points in '%s' layer. Resulting Gcode could be corrupt!") % layer.get(inkex.addNS('label','inkscape')), "bad_orientation_points_in_some_layers") 

				#Need to recognise old files ver 1.6.04 and earlier
				elif i.get("gcodetools") == "Gcodetools tool definition" or i.get("gcodetools") == "Gcodetools tool defenition"  :
					tool = self.get_tool(i)
					self.tools[layer] = self.tools[layer] + [tool.copy()] if layer in self.tools else [tool.copy()]
					print_("Found tool in '%s' layer: %s" % (layer.get(inkex.addNS('label','inkscape')), tool))

				elif i.get("gcodetools") == "Gcodetools graffiti reference point" :
					point = self.get_graffiti_reference_points(i)
					if point != [] :
						self.graffiti_reference_points[layer] = self.graffiti_reference_points[layer]+[point[:]] if layer in self.graffiti_reference_points else [point]
					else :
						self.error(_("Warning! Found bad graffiti reference point in '%s' layer. Resulting Gcode could be corrupt!") % layer.get(inkex.addNS('label','inkscape')), "bad_orientation_points_in_some_layers") 
				
				elif i.tag == inkex.addNS('path','svg'):
					if "gcodetools"  not in i.keys() :
						self.paths[layer] = self.paths[layer] + [i] if layer in self.paths else [i]  
						if i.get("id") in self.selected :
							self.selected_paths[layer] = self.selected_paths[layer] + [i] if layer in self.selected_paths else [i]  

				elif i.get("gcodetools") == "In-out reference point group" :
					items_ = i.getchildren()
					items_.reverse()
					for j in items_ :
						if j.get("gcodetools") == "In-out reference point" :
							self.in_out_reference_points.append( self.apply_transforms(j,cubicsuperpath.parsePath(j.get("d")))[0][0][1] )


				elif i.tag == inkex.addNS("g",'svg'):
					recursive_search(i,layer, (i.get("id") in self.selected) )

				elif i.get("id") in self.selected :
# xgettext:no-pango-format
					self.error(_("This extension works with Paths and Dynamic Offsets and groups of them only! All other objects will be ignored!\nSolution 1: press Path->Object to path or Shift+Ctrl+C.\nSolution 2: Path->Dynamic offset or Ctrl+J.\nSolution 3: export all contours to PostScript level 2 (File->Save As->.ps) and File->Import this file."),"selection_contains_objects_that_are_not_paths")
				
					
		recursive_search(self.document.getroot(),self.document.getroot())

		if len(self.layers) == 1 : 
			self.error(_("Document has no layers! Add at least one layer using layers panel (Ctrl+Shift+L)"),"Error")
		root = self.document.getroot() 

		if  root in self.selected_paths or root in self.paths :
			self.error(_("Warning! There are some paths in the root of the document, but not in any layer! Using bottom-most layer for them."), "tools_warning" )

		if  root in self.selected_paths :
			if self.layers[-1] in self.selected_paths :
				self.selected_paths[self.layers[-1]] += self.selected_paths[root][:]
			else :	
				self.selected_paths[self.layers[-1]] = self.selected_paths[root][:]
			del self.selected_paths[root]
			
		if root in self.paths :	
			if self.layers[-1] in self.paths :
				self.paths[self.layers[-1]] += self.paths[root][:]
			else :
				self.paths[self.layers[-1]] = self.paths[root][:]
			del self.paths[root]


	def get_orientation_points(self,g):
		items = g.getchildren()
		items.reverse()
		p2, p3 = [], []
		p = None
		for i in items:
			if i.tag == inkex.addNS("g",'svg') and i.get("gcodetools") == "Gcodetools orientation point (2 points)":
				p2 += [i]
			if i.tag == inkex.addNS("g",'svg') and i.get("gcodetools") == "Gcodetools orientation point (3 points)":
				p3 += [i]
		if len(p2)==2 : p=p2 
		elif len(p3)==3 : p=p3 
		if p==None : return None
		points = []
		for i in p :	
			point = [[],[]]	
			for  node in i :
				if node.get('gcodetools') == "Gcodetools orientation point arrow":
					point[0] = self.apply_transforms(node,cubicsuperpath.parsePath(node.get("d")))[0][0][1]
				if node.get('gcodetools') == "Gcodetools orientation point text":
					r = re.match(r'(?i)\s*\(\s*(-?\s*\d*(?:,|\.)*\d*)\s*;\s*(-?\s*\d*(?:,|\.)*\d*)\s*;\s*(-?\s*\d*(?:,|\.)*\d*)\s*\)\s*',get_text(node))
					point[1] = [float(r.group(1)),float(r.group(2)),float(r.group(3))]
			if point[0]!=[] and point[1]!=[]:	points += [point]
		if len(points)==len(p2)==2 or len(points)==len(p3)==3 : return points
		else : return None
	
	def get_graffiti_reference_points(self,g):
			point = [[], '']
			for node in g :
				if node.get('gcodetools') == "Gcodetools graffiti reference point arrow":
					point[0] = self.apply_transforms(node,cubicsuperpath.parsePath(node.get("d")))[0][0][1]
				if node.get('gcodetools') == "Gcodetools graffiti reference point text":
					point[1] = get_text(node)
			if point[0]!=[] and point[1]!='' : return point
			else : return []

	def get_tool(self, g):
		tool = self.default_tool.copy()
		tool["self_group"] = g 
		for i in g:
			#	Get parameters
			if i.get("gcodetools") == "Gcodetools tool background" : 
				tool["style"] = simplestyle.parseStyle(i.get("style"))
			elif i.get("gcodetools") == "Gcodetools tool parameter" :
				key = None
				value = None
				for j in i:
					#need to recognise old tools from ver 1.6.04
					if j.get("gcodetools") == "Gcodetools tool definition field name" or j.get("gcodetools") == "Gcodetools tool defention field name":
						key = get_text(j)
					if j.get("gcodetools") == "Gcodetools tool definition field value" or j.get("gcodetools") == "Gcodetools tool defention field value":
						value = get_text(j)
						if value == "(None)": value = ""
				if value == None or key == None: continue
				#print_("Found tool parameter '%s':'%s'" % (key,value))
				if key in self.default_tool.keys() :
					 try :
						tool[key] = type(self.default_tool[key])(value)
					 except :
						tool[key] = self.default_tool[key]
						self.error(_("Warning! Tool's and default tool's parameter's (%s) types are not the same ( type('%s') != type('%s') ).") % (key, value, self.default_tool[key]), "tools_warning")
				else :
					tool[key] = value
					self.error(_("Warning! Tool has parameter that default tool has not ( '%s': '%s' ).") % (key, value), "tools_warning" )
		return tool
		
		
	def set_tool(self,layer):
#		print_(("index(layer)=",self.layers.index(layer),"set_tool():layer=",layer,"self.tools=",self.tools))
#		for l in self.layers:
#			print_(("l=",l))
		for i in range(self.layers.index(layer),-1,-1):
#			print_(("processing layer",i))
			if self.layers[i] in self.tools : 
				break
		if self.layers[i] in self.tools :
			if self.layers[i] != layer : self.tools[layer] = self.tools[self.layers[i]]
			if len(self.tools[layer])>1 : self.error(_("Layer '%s' contains more than one tool!") % self.layers[i].get(inkex.addNS('label','inkscape')), "more_than_one_tool")
			return self.tools[layer]
		else :
			self.error(_("Can not find tool for '%s' layer! Please add one with Tools library tab!") % layer.get(inkex.addNS('label','inkscape')), "no_tool_error")


################################################################################
###
###		Path to Gcode
###
################################################################################
	def path_to_gcode(self) :
		from functools import partial
		def get_boundaries(points):
			minx,miny,maxx,maxy=None,None,None,None
			out=[[],[],[],[]]
			for p in points:
				if minx==p[0]:
					out[0]+=[p]
				if minx==None or p[0]<minx: 
					minx=p[0]
					out[0]=[p]

				if miny==p[1]:
					out[1]+=[p]
				if miny==None or p[1]<miny: 
					miny=p[1]
					out[1]=[p]

				if maxx==p[0]:
					out[2]+=[p]
				if maxx==None or p[0]>maxx: 
					maxx=p[0]
					out[2]=[p]

				if maxy==p[1]:
					out[3]+=[p]
				if maxy==None or p[1]>maxy: 
					maxy=p[1]
					out[3]=[p]
			return out


		def remove_duplicates(points):
			i=0		
			out=[]
			for p in points:
				for j in xrange(i,len(points)):
					if p==points[j]: points[j]=[None,None]	
				if p!=[None,None]: out+=[p]
			i+=1
			return(out)
	
	
		def get_way_len(points):
			l=0
			for i in xrange(1,len(points)):
				l+=math.sqrt((points[i][0]-points[i-1][0])**2 + (points[i][1]-points[i-1][1])**2)
			return l

	
		def sort_dxfpoints(points):
			points=remove_duplicates(points)
#			print_(get_boundaries(get_boundaries(points)[2])[1])
			ways=[
						  # l=0, d=1, r=2, u=3
			 [3,0], # ul
			 [3,2], # ur
			 [1,0], # dl
			 [1,2], # dr
			 [0,3], # lu
			 [0,1], # ld
			 [2,3], # ru
			 [2,1], # rd
			]
#			print_(("points=",points))
			minimal_way=[]
			minimal_len=None
			minimal_way_type=None
			for w in ways:
				tpoints=points[:]
				cw=[]
#				print_(("tpoints=",tpoints))
				for j in xrange(0,len(points)):
					p=get_boundaries(get_boundaries(tpoints)[w[0]])[w[1]]
#					print_(p)
					tpoints.remove(p[0])
					cw+=p
				curlen = get_way_len(cw)
				if minimal_len==None or curlen < minimal_len: 
					minimal_len=curlen
					minimal_way=cw
					minimal_way_type=w
			
			return minimal_way

		def sort_lines(lines):
			if len(lines) == 0 : return []
			lines = [ [key]+lines[key] for key in range(len(lines))]			
			keys = [0]
			end_point = lines[0][3:]
			print_("!!!",lines,"\n",end_point)
			del lines[0]
			while len(lines)>0:
				dist = [ [point_to_point_d2(end_point,lines[i][1:3]),i] for i in range(len(lines))]
				i = min(dist)[1]
				keys.append(lines[i][0])
				end_point = lines[i][3:]
				del lines[i]
			return keys
			
		def sort_curves(curves):
			lines = []
			for curve in curves:
				lines += [curve[0][0][0] + curve[-1][-1][0]]
			return sort_lines(lines)
		
		def print_dxfpoints(points):
			gcode=""
			for point in points:
				gcode +="(drilling dxfpoint)\nG00 Z%f\nG00 X%f Y%f\nG01 Z%f F%f\nG04 P%f\nG00 Z%f\n" % (self.options.Zsafe,point[0],point[1],self.Zcoordinates[layer][1],self.tools[layer][0]["penetration feed"],0.2,self.options.Zsafe) 
#			print_(("got dxfpoints array=",points))
			return gcode
		
		def get_path_properties(node, recursive=True, tags={inkex.addNS('desc','svg'):"Description",inkex.addNS('title','svg'):"Title"} ) :
			res = {}
			done = False
			root = self.document.getroot()
			while not done and node != root :
				for i in node.getchildren():
					if i.tag in tags:
						res[tags[i.tag]] = i.text
					done = True	
				node =	node.getparent()
			return res

		if self.selected_paths == {} and self.options.auto_select_paths:
			paths=self.paths
			self.error(_("No paths are selected! Trying to work on all available paths."),"warning")
		else :
			paths = self.selected_paths
		self.check_dir() 
		gcode = ""
		
		biarc_group = inkex.etree.SubElement( self.selected_paths.keys()[0] if len(self.selected_paths.keys())>0 else self.layers[0], inkex.addNS('g','svg') )
		print_(("self.layers=",self.layers))
		print_(("paths=",paths))
		colors = {}
		for layer in self.layers :
			if layer in paths :
				print_(("layer",layer))
				# transform simple path to get all var about orientation
				self.transform_csp([ [ [[0,0],[0,0],[0,0]],  [[0,0],[0,0],[0,0]] ] ], layer)
			
				self.set_tool(layer)
				curves = []
				dxfpoints = []

				try :
					depth_func = eval('lambda c,d,s: ' + self.options.path_to_gcode_depth_function.strip('"'))				
				except:
					self.error("Bad depth function! Enter correct function at Path to Gcode tab!") 

				for path in paths[layer] :
					if "d" not in path.keys() : 
						self.error(_("Warning: One or more paths do not have 'd' parameter, try to Ungroup (Ctrl+Shift+G) and Object to Path (Ctrl+Shift+C)!"),"selection_contains_objects_that_are_not_paths")
						continue					
					csp = cubicsuperpath.parsePath(path.get("d"))
					csp = self.apply_transforms(path, csp)
					id_ = path.get("id")
					
					def set_comment(match, path):
						if match.group(1) in path.keys() :
							return path.get(match.group(1))
						else: 
							return "None"
					if self.options.comment_gcode != "" :
						comment = re.sub("\[([A-Za-z_\-\:]+)\]", partial(set_comment, path=path), self.options.comment_gcode)
						comment = comment.replace(":newline:","\n") 
						comment = gcode_comment_str(comment)
					else:
						comment = ""
					if self.options.comment_gcode_from_properties :
						tags = get_path_properties(path)
						for tag in tags :
							comment += gcode_comment_str("%s: %s"%(tag,tags[tag]))

					style = simplestyle.parseStyle(path.get("style"))
					colors[id_] = simplestyle.parseColor(style['stroke'] if "stroke"  in style and style['stroke']!='none' else "#000")
					if path.get("dxfpoint") == "1":
						tmp_curve=self.transform_csp(csp, layer)
						x=tmp_curve[0][0][0][0]
						y=tmp_curve[0][0][0][1]
						print_("got dxfpoint (scaled) at (%f,%f)" % (x,y))
						dxfpoints += [[x,y]]
					else:
						
						zd,zs = self.Zcoordinates[layer][1],	self.Zcoordinates[layer][0]
						c = 1. - float(sum(colors[id_]))/255/3
						curves += 	[
										 [  
										 	[id_, depth_func(c,zd,zs), comment],
										 	[ self.parse_curve([subpath], layer) for subpath in csp  ]
										 ]
									]
#				for c in curves : 
#					print_(c)
				dxfpoints=sort_dxfpoints(dxfpoints)
				gcode+=print_dxfpoints(dxfpoints)
				
				
				for curve in curves :
					for subcurve in curve[1] :
						self.draw_curve(subcurve, layer)
					
				if self.options.path_to_gcode_order == 'subpath by subpath':
					curves_ = []
					for curve in curves :
						curves_ += [ [curve[0],[subcurve]]  for subcurve in curve[1] ]  
					curves = curves_	

					self.options.path_to_gcode_order = 'path by path'
					
				if self.options.path_to_gcode_order == 'path by path':
					if self.options.path_to_gcode_sort_paths :
						keys = sort_curves( [curve[1] for curve in curves] )
					else :
						keys = range(len(curves))
					for key in keys:
						d = curves[key][0][1]
						for step in range( 0,  int(math.ceil( abs((zs-d)/self.tools[layer][0]["depth step"] )) ) ):
							z = max(d, zs - abs(self.tools[layer][0]["depth step"]*(step+1)))
							
							gcode += gcode_comment_str("\nStart cutting path id: %s"%curves[key][0][0])
							if curves[key][0][2] != "()" :
								gcode += curves[key][0][2] # add comment
								
							for curve in curves[key][1]:
								gcode += self.generate_gcode(curve, layer, z)
								
							gcode += gcode_comment_str("End cutting path id: %s\n\n"%curves[key][0][0])
							
				else:	# pass by pass
					mind = min( [curve[0][1] for curve in curves] )	
					for step in range( 0,  int(math.ceil( abs((zs-mind)/self.tools[layer][0]["depth step"] )) ) ):
						z = zs - abs(self.tools[layer][0]["depth step"]*(step))
						curves_ = []
						for curve in curves:
							if curve[0][1]<z : 
								curves_.append(curve)

						z = zs - abs(self.tools[layer][0]["depth step"]*(step+1))
						gcode += "\n(Pass at depth %s)\n"%z

						if self.options.path_to_gcode_sort_paths :
							keys = sort_curves( [curve[1] for curve in curves_] )		
						else :
							keys = range(len(curves_))
						for key in keys:
				
							gcode += gcode_comment_str("Start cutting path id: %s"%curves[key][0][0])
							if curves[key][0][2] != "()" :
								gcode += curves[key][0][2] # add comment

							for subcurve in curves_[key][1]:
								gcode += self.generate_gcode(subcurve, layer, max(z,curves_[key][0][1]))
				
							gcode += gcode_comment_str("End cutting path id: %s\n\n"%curves[key][0][0])

							
		self.export_gcode(gcode)
	
################################################################################
###
###		dxfpoints
###
################################################################################
	def dxfpoints(self):
		if self.selected_paths == {}:
			self.error(_("Nothing is selected. Please select something to convert to drill point (dxfpoint) or clear point sign."),"warning")
		for layer in self.layers :
			if layer in self.selected_paths :
				for path in self.selected_paths[layer]:
#					print_(("processing path",path.get('d')))
					if self.options.dxfpoints_action == 'replace':
#						print_("trying to set as dxfpoint")
						
						path.set("dxfpoint","1")
						r = re.match("^\s*.\s*(\S+)",path.get("d"))
						if r!=None:
							print_(("got path=",r.group(1)))
							path.set("d","m %s 2.9375,-6.343750000001 0.8125,1.90625 6.843748640396,-6.84374864039 0,0 0.6875,0.6875 -6.84375,6.84375 1.90625,0.812500000001 z" % r.group(1))
							path.set("style",styles["dxf_points"])

					if self.options.dxfpoints_action == 'save':
						path.set("dxfpoint","1")

					if self.options.dxfpoints_action == 'clear' and path.get("dxfpoint") == "1":
						path.set("dxfpoint","0")
#						for id, node in self.selected.iteritems():
#							print_((id,node,node.attrib))


################################################################################
###
###		Artefacts
###
################################################################################
	def area_artefacts(self) :
			if self.selected_paths == {} and self.options.auto_select_paths:
				paths=self.paths
				self.error(_("No paths are selected! Trying to work on all available paths."),"warning")
			else :
				paths = self.selected_paths
			for layer in paths :
#				paths[layer].reverse() # Reverse list of paths to leave their order 
				for path in paths[layer] :
					parent = path.getparent()
					style = path.get("style") if "style" in path.keys() else ""
					if "d" not in path.keys() : 
						self.error(_("Warning: One or more paths do not have 'd' parameter, try to Ungroup (Ctrl+Shift+G) and Object to Path (Ctrl+Shift+C)!"),"selection_contains_objects_that_are_not_paths")
						continue		
					csp = cubicsuperpath.parsePath(path.get("d"))
					remove = []
					for i in range(len(csp)) :
						subpath = [ [point[:] for point in points] for points in csp[i]]
						subpath = self.apply_transforms(path,[subpath])[0]
						bounds = csp_simple_bound([subpath])
						if  (bounds[2]-bounds[0])**2+(bounds[3]-bounds[1])**2 < self.options.area_find_artefacts_diameter**2:
							if self.options.area_find_artefacts_action == "mark with an arrow" :
								arrow =  cubicsuperpath.parsePath( 'm %s,%s 2.9375,-6.343750000001 0.8125,1.90625 6.843748640396,-6.84374864039 0,0 0.6875,0.6875 -6.84375,6.84375 1.90625,0.812500000001 z' % (subpath[0][1][0],subpath[0][1][1]) )
 								arrow = self.apply_transforms(path,arrow,True)
								inkex.etree.SubElement(parent, inkex.addNS('path','svg'), 
										{
											'd': cubicsuperpath.formatPath(arrow),
											'style': styles["area artefact arrow"],
											'gcodetools': 'area artefact arrow',
										})
							elif self.options.area_find_artefacts_action == "mark with style" :
								inkex.etree.SubElement(parent, inkex.addNS('path','svg'), {'d': cubicsuperpath.formatPath(csp[i]), 'style': styles["area artefact"]})
								remove.append(i)
							elif self.options.area_find_artefacts_action == "delete" :
								remove.append(i)
								print_("Deleted artefact %s" % subpath )
					remove.reverse()
					for i in remove :
						del csp[i]
					if len(csp) == 0 :	
						parent.remove(path)
					else :
						path.set("d", cubicsuperpath.formatPath(csp))
							
			return
			

################################################################################
###
###		Calculate area curves
###
################################################################################
	def area(self) :
		if len(self.selected_paths)<=0:
			self.error(_("This extension requires at least one selected path."),"warning")
			return
		for layer in self.layers :
			if layer in self.selected_paths :
				self.set_tool(layer)
				if self.tools[layer][0]['diameter']<=0 : 
					self.error(_("Tool diameter must be > 0 but tool's diameter on '%s' layer is not!") % layer.get(inkex.addNS('label','inkscape')),"area_tools_diameter_error")
	
				for path in self.selected_paths[layer]:
					print_(("doing path",	path.get("style"), path.get("d")))

					area_group = inkex.etree.SubElement( path.getparent(), inkex.addNS('g','svg') )
				
					d = path.get('d')
					print_(d)
					if d==None:
						print_("omitting non-path")
						self.error(_("Warning: omitting non-path"),"selection_contains_objects_that_are_not_paths")
						continue
					csp = cubicsuperpath.parsePath(d)
				
					if path.get(inkex.addNS('type','sodipodi'))!="inkscape:offset":
						print_("Path %s is not an offset. Preparation started." % path.get("id"))
						# Path is not offset. Preparation will be needed.
						# Finding top most point in path (min y value)
						
						min_x,min_y,min_i,min_j,min_t = csp_true_bounds(csp)[1]
						
						# Reverse path if needed.
						if min_y!=float("-inf") :
							# Move outline subpath to the beginning of csp
	 					 	subp = csp[min_i]
	 					 	del csp[min_i]
	 					 	j = min_j
	 					 	# Split by the topmost point and join again
	 					 	if min_t in [0,1]:
	 					 		if min_t == 0: j=j-1
		 					 	subp[-1][2], subp[0][0] = subp[-1][1], subp[0][1]
	 					 		subp = [ [subp[j][1], subp[j][1], subp[j][2]] ] + subp[j+1:] + subp[:j] + [ [subp[j][0], subp[j][1], subp[j][1]] ]
							else: 					 		
		 						sp1,sp2,sp3 = csp_split(subp[j-1],subp[j],min_t)
		 						subp[-1][2], subp[0][0] = subp[-1][1], subp[0][1]
								subp = [ [ sp2[1], sp2[1],sp2[2] ] ] + [sp3] + subp[j+1:] + subp[:j-1] + [sp1] + [[ sp2[0], sp2[1],sp2[1] ]] 					 	  
	 					 	csp = [subp] + csp
							# reverse path if needed
							if csp_subpath_ccw(csp[0]) :
								for i in range(len(csp)):
								 	n = []
								 	for j in csp[i]:
								 		n = [  [j[2][:],j[1][:],j[0][:]]  ] + n
								 	csp[i] = n[:]

	 					 	
						d = cubicsuperpath.formatPath(csp)
						print_(("original  d=",d))
						d = re.sub(r'(?i)(m[^mz]+)',r'\1 Z ',d)
						d = re.sub(r'(?i)\s*z\s*z\s*',r' Z ',d)
						d = re.sub(r'(?i)\s*([A-Za-z])\s*',r' \1 ',d)
						print_(("formatted d=",d))
					# scale = sqrt(Xscale**2 + Yscale**2) / sqrt(1**2 + 1**2)
					p0 = self.transform([0,0],layer)
					p1 = self.transform([0,1],layer)
					scale = (P(p0)-P(p1)).mag()
					if scale == 0 : scale = 1. 
					else : scale = 1./scale
					print_(scale)
					tool_d = self.tools[layer][0]['diameter']*scale
					r = self.options.area_inkscape_radius * scale
					sign=1 if r>0 else -1
					print_("Tool diameter = %s, r = %s" % (tool_d, r))
					
					# avoiding infinite loops
					if self.options.area_tool_overlap>0.9 : self.options.area_tool_overlap = .9
			
					for i in range(self.options.max_area_curves):
						radius = - tool_d * (i*(1-self.options.area_tool_overlap)+0.5) * sign
						if abs(radius)>abs(r): 
							radius = -r
						
						inkex.etree.SubElement(	area_group, inkex.addNS('path','svg'), 
										{
											 inkex.addNS('type','sodipodi'):	'inkscape:offset',
											 inkex.addNS('radius','inkscape'):	str(radius),
											 inkex.addNS('original','inkscape'):	d,
											'style': styles["biarc_style_i"]['area']
										})
						print_(("adding curve",area_group,d,styles["biarc_style_i"]['area']))
						if radius == -r : break 


################################################################################
###
###		Polyline to biarc
###
###		Converts Polyline to Biarc
################################################################################
	def polyline_to_biarc(self):
		
		
		
		def biarc(sm, depth=0):
			def biarc_split(sp1,sp2, z1, z2, depth): 
				if depth<options.biarc_max_split_depth:
					sp1,sp2,sp3 = csp_split(sp1,sp2)
					l1, l2 = cspseglength(sp1,sp2), cspseglength(sp2,sp3)
					if l1+l2 == 0 : zm = z1
					else : zm = z1+(z2-z1)*l1/(l1+l2)
					return biarc(sp1,sp2,z1,zm,depth+1)+biarc(sp2,sp3,zm,z2,depth+1)
				else: return [ [sp1[1],'line', 0, 0, sp2[1], [z1,z2]] ]

			P0, P4 = P(sp1[1]), P(sp2[1])
			TS, TE, v = (P(sp1[2])-P0), -(P(sp2[0])-P4), P0 - P4
			tsa, tea, va = TS.angle(), TE.angle(), v.angle()
			if TE.mag()<straight_distance_tolerance and TS.mag()<straight_distance_tolerance:	
				# Both tangents are zerro - line straight
				return [ [sp1[1],'line', 0, 0, sp2[1], [z1,z2]] ]
			if TE.mag() < straight_distance_tolerance:
				TE = -(TS+v).unit()
				r = TS.mag()/v.mag()*2
			elif TS.mag() < straight_distance_tolerance:
				TS = -(TE+v).unit()
				r = 1/( TE.mag()/v.mag()*2 )
			else:	
				r=TS.mag()/TE.mag()
			TS, TE = TS.unit(), TE.unit()
			tang_are_parallel = ((tsa-tea)%math.pi<straight_tolerance or math.pi-(tsa-tea)%math.pi<straight_tolerance )
			if ( tang_are_parallel  and 
						((v.mag()<straight_distance_tolerance or TE.mag()<straight_distance_tolerance or TS.mag()<straight_distance_tolerance) or
							1-abs(TS*v/(TS.mag()*v.mag()))<straight_tolerance)	):
						# Both tangents are parallel and start and end are the same - line straight
						# or one of tangents still smaller then tollerance

						# Both tangents and v are parallel - line straight
				return [ [sp1[1],'line', 0, 0, sp2[1], [z1,z2]] ]

			c,b,a = v*v, 2*v*(r*TS+TE), 2*r*(TS*TE-1)
			if v.mag()==0:
				return biarc_split(sp1, sp2, z1, z2, depth)
			asmall, bsmall, csmall = abs(a)<10**-10,abs(b)<10**-10,abs(c)<10**-10 
			if 		asmall and b!=0:	beta = -c/b
			elif 	csmall and a!=0:	beta = -b/a 
			elif not asmall:	 
				discr = b*b-4*a*c
				if discr < 0:	raise ValueError, (a,b,c,discr)
				disq = discr**.5
				beta1 = (-b - disq) / 2 / a
				beta2 = (-b + disq) / 2 / a
				if beta1*beta2 > 0 :	raise ValueError, (a,b,c,disq,beta1,beta2)
				beta = max(beta1, beta2)
			elif	asmall and bsmall:	
				return biarc_split(sp1, sp2, z1, z2, depth)
			alpha = beta * r
			ab = alpha + beta 
			P1 = P0 + alpha * TS
			P3 = P4 - beta * TE
			P2 = (beta / ab)  * P1 + (alpha / ab) * P3


			def calculate_arc_params(P0,P1,P2):
				D = (P0+P2)/2
				if (D-P1).mag()==0: return None, None
				R = D - ( (D-P0).mag()**2/(D-P1).mag() )*(P1-D).unit()
				p0a, p1a, p2a = (P0-R).angle()%(2*math.pi), (P1-R).angle()%(2*math.pi), (P2-R).angle()%(2*math.pi)
				alpha =  (p2a - p0a) % (2*math.pi)					
				if (p0a<p2a and  (p1a<p0a or p2a<p1a))	or	(p2a<p1a<p0a) : 
					alpha = -2*math.pi+alpha 
				if abs(R.x)>1000000 or abs(R.y)>1000000  or (R-P0).mag<options.min_arc_radius**2 :
					return None, None
				else :	
					return  R, alpha
			R1,a1 = calculate_arc_params(P0,P1,P2)
			R2,a2 = calculate_arc_params(P2,P3,P4)
			if R1==None or R2==None or (R1-P0).mag()<straight_tolerance or (R2-P2).mag()<straight_tolerance	: return [ [sp1[1],'line', 0, 0, sp2[1], [z1,z2]] ]
	
			d = csp_to_arc_distance(sp1,sp2, [P0,P2,R1,a1],[P2,P4,R2,a2])
			if d > options.biarc_tolerance and depth<options.biarc_max_split_depth	 : return biarc_split(sp1, sp2, z1, z2, depth)
			else:
				if R2.mag()*a2 == 0 : zm = z2
				else : zm  = z1 + (z2-z1)*(abs(R1.mag()*a1))/(abs(R2.mag()*a2)+abs(R1.mag()*a1)) 

				l = (P0-P2).l2()
				if  l < EMC_TOLERANCE_EQUAL**2 or l<EMC_TOLERANCE_EQUAL**2 * R1.l2() /100 :
					# arc should be straight otherwise it could be threated as full circle
					arc1 = [ sp1[1], 'line', 0, 0, [P2.x,P2.y], [z1,zm] ] 
				else :
					arc1 = [ sp1[1], 'arc', [R1.x,R1.y], a1, [P2.x,P2.y], [z1,zm] ] 

				l = (P4-P2).l2()
				if  l < EMC_TOLERANCE_EQUAL**2 or l<EMC_TOLERANCE_EQUAL**2 * R2.l2() /100 :
					# arc should be straight otherwise it could be threated as full circle
					arc2 = [ [P2.x,P2.y], 'line', 0, 0, [P4.x,P4.y], [zm,z2] ] 
				else :
					arc2 = [ [P2.x,P2.y], 'arc', [R2.x,R2.y], a2, [P4.x,P4.y], [zm,z2] ]
		
				return [ arc1, arc2 ]

		
		
		
		
		for layer in self.layers :
			if layer in self.selected_paths :
				for path in self.selected_paths[layer]:
					d = path.get('d')
					if d==None:
						print_("omitting non-path")
						self.error(_("Warning: omitting non-path"),"selection_contains_objects_that_are_not_paths")
						continue
					csp = cubicsuperpath.parsePath(d)
					csp = self.apply_transforms(path, csp)
					csp = self.transform_csp(csp, layer)
					
					# lets pretend that csp is a polyline 
					poly = [ [point[1] for point in subpath] for subpath in csp ]
					
					self.draw_csp([ [ [point,point,point] for point in subpoly] for subpoly in poly ],layer)
					
					# lets create biarcs 
					for subpoly in poly :
						# lets split polyline into different smooth parths. 
						
						if len(subpoly)>2 :
							smooth = [ [subpoly[0],subpoly[1]] ]
							for p1,p2,p3 in zip(subpoly,subpoly[1:],subpoly[2:]) :
								# normalize p1p2 and p2p3 to get angle
								s1,s2 = normalize( p1[0]-p2[0], p1[1]-p2[1]), normalize( p3[0]-p2[0], p3[1]-p2[1]) 
								if cross(s1,s2) > corner_tolerance :
									#it's an angle 
									smooth += [  [p2,p3]  ]
								else: 
									smooth[-1].append(p3)							
							for sm in smooth :
								smooth_polyline_to_biarc(sm)	

################################################################################
###
###		Area fill 
###
###		Fills area with lines
################################################################################


	def area_fill(self):
		# convert degrees into rad
		self.options.area_fill_angle = self.options.area_fill_angle * math.pi / 180
		if len(self.selected_paths)<=0:
			self.error(_("This extension requires at least one selected path."),"warning")
			return
		for layer in self.layers :
			if layer in self.selected_paths :
				self.set_tool(layer)
				if self.tools[layer][0]['diameter']<=0 : 
					self.error(_("Tool diameter must be > 0 but tool's diameter on '%s' layer is not!") % layer.get(inkex.addNS('label','inkscape')),"area_tools_diameter_error")
				tool = self.tools[layer][0]
				for path in self.selected_paths[layer]:
					lines = []
					print_(("doing path",	path.get("style"), path.get("d")))
					area_group = inkex.etree.SubElement( path.getparent(), inkex.addNS('g','svg') )
					d = path.get('d')
					if d==None:
						print_("omitting non-path")
						self.error(_("Warning: omitting non-path"),"selection_contains_objects_that_are_not_paths")
						continue
					csp = cubicsuperpath.parsePath(d)
					csp = self.apply_transforms(path, csp)
					csp = csp_close_all_subpaths(csp)
					csp = self.transform_csp(csp, layer)
					#maxx = max([x,y,i,j,root],maxx)
					
					# rotate the path to get bounds in defined direction.
					a = - self.options.area_fill_angle
					rotated_path = [   [ [ [point[0]*math.cos(a) - point[1]*math.sin(a), point[0]*math.sin(a)+point[1]*math.cos(a)]  for point in sp] for sp in subpath] for subpath in csp  ]
					bounds =  csp_true_bounds(rotated_path)
					
					# Draw the lines 
					# Get path's bounds
					b = [0.0, 0.0, 0.0, 0.0] # [minx,miny,maxx,maxy]
					for k in range(4):
						i, j, t = bounds[k][2], bounds[k][3], bounds[k][4]
						b[k] = csp_at_t(rotated_path[i][j-1],rotated_path[i][j],t)[k%2]

						
					# Zig-zag
					r = tool['diameter']*(1-self.options.area_tool_overlap)
					if r<=0 :
						self.error('Tools diameter must be greater than 0!', 'error')
						return

					lines += [ [] ]

					if self.options.area_fill_method == 'zig-zag' :
						i = b[0] - self.options.area_fill_shift*r
						top = True
						last_one = True
						while (i<b[2] or last_one) : 
							if i>=b[2] : last_one = False
							if lines[-1] == [] :
								lines[-1] += [  [i,b[3]]  ]

							if top :
								lines[-1] += [ [i,b[1]],[i+r,b[1]] ]

							else :
									lines[-1] += [ [i,b[3]], [i+r,b[3]] ]

							top = not top
							i += r
					else :
					
						w, h  = b[2]-b[0] + self.options.area_fill_shift*r , b[3]-b[1] +  self.options.area_fill_shift*r
						x,y = b[0] - self.options.area_fill_shift*r, b[1] - self.options.area_fill_shift*r
						lines[-1] += [  [x,y] ]
						stage = 0
						start = True
						while w>0 and h>0 :
							stage = (stage+1)%4
							if   stage == 0 :
								y -= h
								h -= r
							elif stage == 1:
								x += w
								if not start:
									w -= r
								start = False	
							elif stage == 2 :
								y += h
								h -= r
							elif stage == 3:
								x -= w
								w -=r
							
							lines[-1] += [ [x,y] ]

						stage = (stage+1)%4							
						if w <= 0 and h>0 :
							y = y-h if stage == 0 else y+h  
						if h <= 0  and w>0 :
							x = x-w if stage == 3 else x+w  
						lines[-1] += [ [x,y] ]
					# Rotate created paths back
					a =  self.options.area_fill_angle
					lines = [ [ [point[0]*math.cos(a) - point[1]*math.sin(a), point[0]*math.sin(a)+point[1]*math.cos(a)] for point in subpath] for subpath in lines  ]

					# get the intersection points
					
					splitted_line = [ [lines[0][0]] ]
					intersections = {}
					for l1,l2, in zip(lines[0],lines[0][1:]): 
						ints = []
						
						if l1[0]==l2[0] and l1[1]==l2[1] : continue
						for i in range(len(csp)) :
							for j in range(1,len(csp[i]))  :
								sp1,sp2 = csp[i][j-1], csp[i][j]
								roots = csp_line_intersection(l1,l2,sp1,sp2)
								for t in roots :
									p = tuple(csp_at_t(sp1,sp2,t))
									if l1[0]==l2[0] :
										t1 = (p[1]-l1[1])/(l2[1]-l1[1])
									else :
										t1 = (p[0]-l1[0])/(l2[0]-l1[0])
									if 0<=t1<=1	:
										ints += [[t1, p[0],p[1], i,j,t]]
										if p in intersections :
											intersections[p]  += [ [i,j,t] ]  
										else : 	
											intersections[p]  = [ [i,j,t] ]  
										#p = self.transform(p,layer,True)
										#draw_pointer(p)
						ints.sort()
						for i in ints:
							splitted_line[-1] +=[ [ i[1], i[2]] ]
							splitted_line += [ [ [ i[1], i[2]] ] ]
						splitted_line[-1] += [  l2  ]
						i = 0
					print_(splitted_line)
					while i < len(splitted_line) :
						# check if the middle point of the first lines segment is inside the path.
						# and remove the subline if not.
						l1,l2 = splitted_line[i][0],splitted_line[i][1]
						p = [(l1[0]+l2[0])/2, (l1[1]+l2[1])/2]
						if not point_inside_csp(p, csp):
							#i +=1 						
							del splitted_line[i]
						else :
							i += 1
					
					
					
					# if we've used spiral method we'll try to save the order of cutting
					do_not_change_order = self.options.area_fill_method == 'spiral' 
					# now let's try connect splitted lines
					#while len(splitted_line)>0 :
					#TODO	
					
					# and apply back transrormations to draw them
					csp_line = csp_from_polyline(splitted_line)
					csp_line = self.transform_csp(csp_line, layer, True)
					
					self.draw_csp(csp_line, group = area_group)
#					draw_csp(lines)
					


					 					


################################################################################
###
###		Engraving
###
#LT Notes to self: See wiki.inkscape.org/wiki/index.php/PythonEffectTutorial
# To create anything in the Inkscape document, look at the XML editor for
# details of how such an element looks in XML, then follow this model.
#layer number n appears in XML as <svg:g id="layern" inkscape:label="layername">
#
#to create it, use
#Mylayer=inkex.etree.SubElement(self.document.getroot(), 'g') #Create a generic element
#Mylayer.set(inkex.addNS('label', 'inkscape'), "layername")   #Gives it a name
#Mylayer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')   #Tells Inkscape it's a layer
#
#group appears in XML as <svg:g id="gnnnnn"> where nnnnn is a number
#
#to create it, use
#Mygroup=inkex.etree.SubElement(parent, inkex.addNS('g','svg'), {"gcodetools":"My group label"})
# where parent may be the layer or a parent group. To get the parent group, you can use
#parent = self.selected_paths[layer][0].getparent()
################################################################################
	def engraving(self) :
		#global x1,y1,rx,ry
		global cspm, wl
		global nlLT, i, j
		global gcode_3Dleft ,gcode_3Dright
		global max_dist #minimum of tool radius and user's requested maximum distance
		global eye_dist
		eye_dist = 100 #3D constant. Try varying it for your eyes


		def bisect((nx1,ny1),(nx2,ny2)) :
			"""LT Find angle bisecting the normals n1 and n2

			Parameters: Normalised normals
			Returns: nx - Normal of bisector, normalised to 1/cos(a)
				   ny -
				   sinBis2 - sin(angle turned/2): positive if turning in
			Note that bisect(n1,n2) and bisect(n2,n1) give opposite sinBis2 results
			If sinturn is less than the user's requested angle tolerance, I return 0
			"""
			#We can get absolute value of cos(bisector vector)
			#Note: Need to use max in case of rounding errors
			cosBis = math.sqrt(max(0,(1.0+nx1*nx2-ny1*ny2)/2.0))
			#We can get correct sign of the sin, assuming cos is positive
			if (abs(ny1-ny2)< engraving_tolerance)  or (abs(cosBis) < engraving_tolerance) :
				if (abs(nx1-nx2)< engraving_tolerance): return(nx1,ny1,0.0)
				sinBis = math.copysign(1,ny1)
			else :
				sinBis = cosBis*(nx2-nx1)/(ny1-ny2)
			#We can correct signs by noting that the dot product
			# of bisector and either normal must be >0
			costurn=cosBis*nx1+sinBis*ny1
			if costurn == 0 : return (ny1*100,-nx1*100,1) #Path doubles back on itself
			sinturn=sinBis*nx1-cosBis*ny1
			if costurn<0 :  sinturn=-sinturn
			if 0 < sinturn*114.6 < (180-self.options.engraving_sharp_angle_tollerance) :
				sinturn=0 #set to zero if less than the user wants to see.
			return (cosBis/costurn,sinBis/costurn, sinturn)
			#end bisect

		def get_radius_to_line((x1,y1),(nx1,ny1), (nx2,ny2),(x2,y2),(nx23,ny23),(x3,y3),(nx3,ny3)):
			"""LT find biggest circle we can engrave here, if constrained by line 2-3

			Parameters:
				x1,y1,nx1,ny1 coordinates and normal of the line we're currently engraving
				nx2,ny2 angle bisector at point 2
				x2,y2 coordinates of first point of line 2-3
				nx23,ny23 normal to the line 2-3
				x3,y3 coordinates of the other end
				nx3,ny3 angle bisector at point 3
			Returns:
				radius or self.options.engraving_max_dist if line doesn't limit radius
			This function can be used in three ways:
			- With nx1=ny1=0 it finds circle centred at x1,y1
			- with nx1,ny1 normalised, it finds circle tangential at x1,y1
			- with nx1,ny1 scaled by 1/cos(a) it finds circle centred on an angle bisector
				 where a is the angle between the bisector and the previous/next normals

			If the centre of the circle tangential to the line 2-3 is outside the
			angle bisectors at its ends, ignore this line. 

			# Note that it handles corners in the conventional manner of letter cutting
			# by mitering, not rounding.
			# Algorithm uses dot products of normals to find radius
			# and hence coordinates of centre
			"""

			global max_dist

			#Start by converting coordinates to be relative to x1,y1
			x2,y2= x2-x1, y2-y1
			x3,y3= x3-x1, y3-y1

			#The logic uses vector arithmetic.
			#The dot product of two vectors gives the product of their lengths
			#multiplied by the cos of the angle between them.
			# So, the perpendicular distance from x1y1 to the line 2-3
			# is equal to the dot product of its normal and x2y2 or x3y3
			#It is also equal to the projection of x1y1-xcyc on the line's normal
			# plus the radius. But, as the normal faces inside the path we must negate it.

			#Make sure the line in question is facing x1,y1 and vice versa
			dist=-x2*nx23-y2*ny23
			if dist<0 : return max_dist
			denom=1.-nx23*nx1-ny23*ny1
			if denom < engraving_tolerance : return max_dist

			#radius and centre are:
			r=dist/denom
			cx=r*nx1
			cy=r*ny1
			#if c is not between the angle bisectors at the ends of the line, ignore
			#Use vector cross products. Not sure if I need the .0001 safety margins:
			if (x2-cx)*ny2 > (y2-cy)*nx2 +0.0001 :
				return max_dist 
			if (x3-cx)*ny3 < (y3-cy)*nx3  -0.0001 :
				return max_dist 
			return min(r, max_dist)
			#end of get_radius_to_line

		def get_radius_to_point((x1,y1),(nx,ny), (x2,y2)):
			"""LT find biggest circle we can engrave here, constrained by point x2,y2

			This function can be used in three ways:
			- With nx=ny=0 it finds circle centred at x1,y1
			- with nx,ny normalised, it finds circle tangential at x1,y1
			- with nx,ny scaled by 1/cos(a) it finds circle centred on an angle bisector
				 where a is the angle between the bisector and the previous/next normals

			Note that I wrote this to replace find_cutter_centre. It is far less
			sophisticated but, I hope, far faster.
			It turns out that finding a circle touching a point is harder than a circle
			touching a line.
			"""

			global max_dist

			#Start by converting coordinates to be relative to x1,y1
			x2,y2= x2-x1, y2-y1
			denom=nx**2+ny**2-1
			if denom<=engraving_tolerance : #Not a corner bisector
				if denom==-1 : #Find circle centre x1,y1
					return math.sqrt(x2**2+y2**2)
				#if x2,y2 not in front of the normal...
				if x2*nx+y2*ny <=0 : return max_dist
				#print_("Straight",x1,y1,nx,ny,x2,y2)
				return (x2**2+y2**2)/(2*(x2*nx+y2*ny) )
			#It is a corner bisector, so..
			discriminator = (x2*nx+y2*ny)**2 - denom*(x2**2+y2**2)
			if discriminator < 0 :
				return max_dist #this part irrelevant
			r=(x2*nx+y2*ny -math.sqrt(discriminator))/denom
			#print_("Corner",x1,y1,nx,ny,x1+x2,y1+y2,discriminator,r)
			return min(r, max_dist)
			#end of get_radius_to_point

		def bez_divide(a,b,c,d):
			"""LT recursively divide a Bezier.

			Divides until difference between each
			part and a straight line is less than some limit
			Note that, as simple as this code is, it is mathematically correct.
			Parameters:
				a,b,c and d are each a list of x,y real values
				Bezier end points a and d, control points b and c
			Returns:
				a list of Beziers.
					Each Bezier is a list with four members,
						each a list holding a coordinate pair
				Note that the final point of one member is the same as
				the first point of the next, and the control points
				there are smooth and symmetrical. I use this fact later.
			"""
			bx=b[0]-a[0]
			by=b[1]-a[1]
			cx=c[0]-a[0]
			cy=c[1]-a[1]
			dx=d[0]-a[0]
			dy=d[1]-a[1]
			limit=8*math.hypot(dx,dy)/self.options.engraving_newton_iterations
			#LT This is the only limit we get from the user currently
			if abs(dx*by-bx*dy)<limit and abs(dx*cy-cx*dy)<limit :
				return [[a,b,c,d]]
			abx=(a[0]+b[0])/2.0
			aby=(a[1]+b[1])/2.0
			bcx=(b[0]+c[0])/2.0
			bcy=(b[1]+c[1])/2.0
			cdx=(c[0]+d[0])/2.0
			cdy=(c[1]+d[1])/2.0
			abcx=(abx+bcx)/2.0
			abcy=(aby+bcy)/2.0
			bcdx=(bcx+cdx)/2.0
			bcdy=(bcy+cdy)/2.0
			m=[(abcx+bcdx)/2.0,(abcy+bcdy)/2.0]
			return bez_divide(a,[abx,aby],[abcx,abcy],m) + bez_divide(m,[bcdx,bcdy],[cdx,cdy],d)
			#end of bez_divide

		def get_biggest((x1,y1),(nx,ny)):
			"""LT Find biggest circle we can draw inside path at point x1,y1 normal nx,ny

			Parameters:
				point - either on a line or at a reflex corner
				normal - normalised to 1 if on a line, to 1/cos(a) at a corner
			Returns:
				tuple (j,i,r)
				..where j and i are indices of limiting segment, r is radius
			"""
			global max_dist, nlLT, i, j
			n1 = nlLT[j][i-1] #current node
			jjmin = -1
			iimin = -1
			r = max_dist
			# set limits within which to look for lines
			xmin, xmax = x1+r*nx-r, x1+r*nx+r
			ymin, ymax = y1+r*ny-r, y1+r*ny+r
			for jj in xrange(0,len(nlLT)) : #for every subpath of this object
				for ii in xrange(0,len(nlLT[jj])) : #for every point and line
					if nlLT[jj][ii-1][2] : #if a point
						if jj==j : #except this one
							if abs(ii-i)<3 or abs(ii-i)>len(nlLT[j])-3 : continue
						t1=get_radius_to_point((x1,y1),(nx,ny),nlLT[jj][ii-1][0] )
						#print_("Try pt   i,ii,t1,x1,y1",i,ii,t1,x1,y1)
					else: #doing a line
						if jj==j : #except this one
							if abs(ii-i)<2 or abs(ii-i)==len(nlLT[j])-1 : continue
							if abs(ii-i)==2  and nlLT[j][(ii+i)/2-1][3]<=0 : continue
							if (abs(ii-i)==len(nlLT[j])-2) and nlLT[j][-1][3]<=0 : continue
						nx2,ny2 = nlLT[jj][ii-2][1]
						x2,y2 = nlLT[jj][ii-1][0]
						nx23,ny23 = nlLT[jj][ii-1][1]
						x3,y3 = nlLT[jj][ii][0]
						nx3,ny3 = nlLT[jj][ii][1]
						if nlLT[jj][ii-2][3]>0 : #acute, so use normal, not bisector
							nx2=nx23
							ny2=ny23
						if nlLT[jj][ii][3]>0 : #acute, so use normal, not bisector
							nx3=nx23
							ny3=ny23
						x23min,x23max=min(x2,x3),max(x2,x3)
						y23min,y23max=min(y2,y3),max(y2,y3)
						#see if line in range
						if n1[2]==False and (x23max<xmin or x23min>xmax or y23max<ymin or y23min>ymax) : continue
						t1=get_radius_to_line((x1,y1),(nx,ny), (nx2,ny2),(x2,y2),(nx23,ny23), (x3,y3),(nx3,ny3))
						#print_("Try line i,ii,t1,x1,y1",i,ii,t1,x1,y1)
					if 0<=t1<r :
						r = t1
						iimin = ii
						jjmin = jj
						xmin, xmax = x1+r*nx-r, x1+r*nx+r
						ymin, ymax = y1+r*ny-r, y1+r*ny+r
				#next ii
			#next jj
			return (jjmin,iimin,r)
			#end of get_biggest

		def line_divide((x0,y0),j0,i0,(x1,y1),j1,i1,(nx,ny),length):
			"""LT recursively divide a line as much as necessary

			NOTE: This function is not currently used
			By noting which other path segment is touched by the circles at each end,
			we can see if anything is to be gained by a further subdivision, since
			if they touch the same bit of path we can move linearly between them.
			Also, we can handle points correctly.
			Parameters:
				end points and indices of limiting path, normal, length
			Returns:
				list of toolpath points
					each a list of 3 reals: x, y coordinates, radius

			"""
			global nlLT, i, j, lmin
			x2=(x0+x1)/2
			y2=(y0+y1)/2
			j2,i2,r2=get_biggest( (x2,y2), (nx,ny))
			if length<lmin : return [ [x2, y2, r2] ]
			if j2==j0 and i2==i0 : #Same as left end. Don't subdivide this part any more
				return [ [x2, y2, r2], line_divide((x2,y2),j2,i2,(x1,y1),j1,i1,(nx,ny),length/2)]
			if j2==j1 and i2==i1 : #Same as right end. Don't subdivide this part any more
				return [ line_divide((x0,y0),j0,i0,(x2,y2),j2,i2,(nx,ny),length/2), [x2, y2, r2] ]
			return [ line_divide((x0,y0),j0,i0,(x2,y2),j2,i2,(nx,ny),length/2), line_divide((x2,y2),j2,i2,(x1,y1),j1,i1,(nx,ny),length/2)]
			#end of line_divide()

		def save_point((x,y),w,i,j,ii,jj):
			"""LT Save this point and delete previous one if linear

			The point is, we generate tons of points but many may be in a straight 3D line.
			There is no benefit in saving the imtermediate points.
			"""
			global wl, cspm
			x=round(x,4) #round to 4 decimals
			y=round(y,4) #round to 4 decimals
			w=round(w,4) #round to 4 decimals
			if len(cspm)>1 :
				xy1a,xy1,xy1b,i1,j1,ii1,jj1=cspm[-1]
				w1=wl[-1]
				if i==i1 and j==j1 and ii==ii1 and jj==jj1 : #one match
					xy1a,xy2,xy1b,i1,j1,ii1,jj1=cspm[-2]
					w2=wl[-2]
					if i==i1 and j==j1 and ii==ii1 and jj==jj1 : #two matches. Now test linearity
						length1=math.hypot(xy1[0]-x,xy1[1]-y)
						length2=math.hypot(xy2[0]-x,xy2[1]-y)
						length12=math.hypot(xy2[0]-xy1[0],xy2[1]-xy1[1])
						#get the xy distance of point 1 from the line 0-2
						if length2>length1 and length2>length12 : #point 1 between them
							xydist=abs( (xy2[0]-x)*(xy1[1]-y)-(xy1[0]-x)*(xy2[1]-y) )/length2
							if xydist<engraving_tolerance : #so far so good
								wdist=w2+(w-w2)*length1/length2 -w1
								if abs(wdist)<engraving_tolerance :
									#print_("pop",j,i,xy1)
									cspm.pop()
									wl.pop()
			cspm+=[ [ [x,y],[x,y],[x,y],i,j,ii,jj ] ]
			wl+=[w]
			#end of save_point

		def draw_point((x0,y0),(x,y),w,t):
			"""LT Draw this point as a circle with a 1px dot in the middle (x,y) 
			and a 3D line from (x0,y0) down to x,y. 3D line thickness should be t/2 

			Note that points that are subsequently erased as being unneeded do get
			displayed, but this helps the user see the total area covered.
			"""
			global gcode_3Dleft ,gcode_3Dright
			if self.options.engraving_draw_calculation_paths :
				inkex.etree.SubElement(	engraving_group, inkex.addNS('path','svg'), 
						{"gcodetools": "Engraving calculation toolpath", 'style':	"fill:#ff00ff; fill-opacity:0.46; stroke:#000000; stroke-width:0.1;", inkex.addNS('cx','sodipodi'):		str(x), inkex.addNS('cy','sodipodi'):		str(y), inkex.addNS('rx','sodipodi'):	str(1), inkex.addNS('ry','sodipodi'): str(1), inkex.addNS('type','sodipodi'):	'arc'})
				#Don't draw zero radius circles
				if w:
					inkex.etree.SubElement(	engraving_group, inkex.addNS('path','svg'), 
						{"gcodetools": "Engraving calculation paths", 'style':	"fill:none; fill-opacity:0.46; stroke:#000000; stroke-width:0.1;", inkex.addNS('cx','sodipodi'):		str(x),  inkex.addNS('cy','sodipodi'):		str(y),inkex.addNS('rx','sodipodi'):		str(w), inkex.addNS('ry','sodipodi'):		str(w), inkex.addNS('type','sodipodi'):	'arc'})
					# Find slope direction for shading
					s=math.atan2(y-y0,x-x0) #-pi to pi
					# convert to 2 hex digits as a shade of red
					s2="#{0:x}0000".format(int(101*(1.5-math.sin(s+0.5))))
					inkex.etree.SubElement(	gcode_3Dleft , inkex.addNS('path','svg'),
					{ "d": "M %f,%f L %f,%f" %(x0-eye_dist,y0,x-eye_dist-0.14*w,y),
						'style': "stroke:" + s2 + "; stroke-opacity:1; stroke-width:" + str(t/2) +" ; fill:none",
						"gcodetools": "Gcode G1R"
					})
					inkex.etree.SubElement(	gcode_3Dright , inkex.addNS('path','svg'),
					{ "d": "M %f,%f L %f,%f" %(x0+eye_dist,y0,x+eye_dist+0.14*r,y),
						'style': "stroke:" + s2 + "; stroke-opacity:1; stroke-width:" + str(t/2) +" ; fill:none",
						"gcodetools": "Gcode G1L"
					})
			#end of draw_point

		#end of subfunction definitions. engraving() starts here:
		gcode = ''
		r,w, wmax = 0,0,0 #theoretical and tool-radius-limited radii in pixels
		x1,y1,nx,ny =0,0,0,0
		cspe =[]
		we = []
		if len(self.selected_paths)<=0:
			self.error(_("Please select at least one path to engrave and run again."),"warning")
			return
		if not self.check_dir() : return
		#Find what units the user uses
		unit=" mm"
		if self.options.unit == "G20 (All units in inches)" :
			unit=" inches"
		elif self.options.unit != "G21 (All units in mm)" :
			self.error(_("Unknown unit selected. mm assumed"),"warning")
		print_("engraving_max_dist mm/inch", self.options.engraving_max_dist )

		#LT See if we can use this parameter for line and Bezier subdivision:
		bitlen=20/self.options.engraving_newton_iterations

		for layer in self.layers :
			if layer in self.selected_paths :
				#Calculate scale in pixels per user unit (mm or inch)
				p1=self.orientation_points[layer][0][0]
				p2=self.orientation_points[layer][0][1]
				ol=math.hypot(p1[0][0]-p2[0][0],p1[0][1]-p2[0][1])
				oluu=math.hypot(p1[1][0]-p2[1][0],p1[1][1]-p2[1][1])
				print_("Orientation2 p1 p2 ol oluu",p1,p2,ol,oluu)
				orientation_scale = ol/oluu

				self.set_tool(layer)
				shape = self.tools[layer][0]['shape']
				if re.search('w', shape) :
					toolshape = eval('lambda w: ' + shape.strip('"'))
				else: 
					self.error(_("Tool '%s' has no shape. 45 degree cone assumed!") % self.tools[layer][0]['name'],"Continue")
					toolshape = lambda w: w
				#Get tool radius in pixels
				toolr=self.tools[layer][0]['diameter'] * orientation_scale/2
				print_("tool radius in pixels=", toolr)
				#max dist from path to engrave in user's units
				max_distuu = min(self.tools[layer][0]['diameter']/2, self.options.engraving_max_dist)
				max_dist=max_distuu*orientation_scale
				print_("max_dist pixels", max_dist )

				engraving_group = inkex.etree.SubElement( self.selected_paths[layer][0].getparent(), inkex.addNS('g','svg') )
				if self.options.engraving_draw_calculation_paths and (self.my3Dlayer  == None) :
					self.my3Dlayer=inkex.etree.SubElement(self.document.getroot(), 'g') #Create a generic element at root level
					self.my3Dlayer.set(inkex.addNS('label', 'inkscape'), "3D") #Gives it a name
					self.my3Dlayer.set(inkex.addNS('groupmode', 'inkscape'), 'layer') #Tells Inkscape it's a layer
				#Create groups for left and right eyes
				if self.options.engraving_draw_calculation_paths :
					gcode_3Dleft = inkex.etree.SubElement(self.my3Dlayer, inkex.addNS('g','svg'), {"gcodetools":"Gcode 3D L"})
					gcode_3Dright = inkex.etree.SubElement(self.my3Dlayer, inkex.addNS('g','svg'), {"gcodetools":"Gcode 3D R"})

				for node in self.selected_paths[layer] :	
					if node.tag == inkex.addNS('path','svg'):
						cspi = cubicsuperpath.parsePath(node.get('d'))
						#LT: Create my own list. n1LT[j] is for subpath j
						nlLT = []
						for j in xrange(len(cspi)): #LT For each subpath...
							# Remove zero length segments, assume closed path
							i = 0 #LT was from i=1
							while i<len(cspi[j]):
								if abs(cspi[j][i-1][1][0]-cspi[j][i][1][0])<engraving_tolerance and abs(cspi[j][i-1][1][1]-cspi[j][i][1][1])<engraving_tolerance:
									cspi[j][i-1][2] = cspi[j][i][2]
									del cspi[j][i]
								else:
									i += 1
						for csp in cspi: #LT6a For each subpath...
							#Create copies in 3D layer
							print_("csp is zz ",csp)
							cspl=[]
							cspr=[]
							#create list containing lines and points, starting with a point
							# line members: [x,y],[nx,ny],False,i
							# x,y is start of line. Normal on engraved side.
							# Normal is normalised (unit length)
							#Note that Y axis increases down the page
							# corner members: [x,y],[nx,ny],True,sin(halfangle)
							# if halfangle>0: radius 0 here. normal is bisector
							# if halfangle<0. reflex angle. normal is bisector
							# corner normals are divided by cos(halfangle)
							#so that they will engrave correctly
							print_("csp is",csp)
							nlLT.append ([])
							for i in range(0,len(csp)): #LT for each point
								#n = []
								sp0, sp1, sp2 = csp[i-2], csp[i-1], csp[i]
								if self.options.engraving_draw_calculation_paths:
									#Copy it to 3D layer objects
									spl=[]
									spr=[]
									for j in range(0,3) :
										pl=[sp2[j][0]-eye_dist,sp2[j][1]]
										pr=[sp2[j][0]+eye_dist,sp2[j][1]]
										spl+=[pl]
										spr+=[pr]
									cspl+=[spl]
									cspr+=[spr]					
								#LT find angle between this and previous segment
								x0,y0 = sp1[1]
								nx1,ny1 = csp_normalized_normal(sp1,sp2,0)
								#I don't trust this function, so test result
								if abs(1-math.hypot(nx1,ny1))> 0.00001 :
									print_("csp_normalised_normal error t=0",nx1,ny1,sp1,sp2)
									self.error(_("csp_normalised_normal error. See log."),"warning")

								nx0, ny0 = csp_normalized_normal(sp0,sp1,1)
								if abs(1-math.hypot(nx0,ny0))> 0.00001 :
									print_("csp_normalised_normal error t=1",nx0,ny0,sp1,sp2)
									self.error(_("csp_normalised_normal error. See log."),"warning")
								bx,by,s=bisect((nx0,ny0),(nx1,ny1))
								#record x,y,normal,ifCorner, sin(angle-turned/2)
								nlLT[-1] += [[ [x0,y0],[bx,by], True, s]]

								#LT now do the line
								if sp1[1]==sp1[2] and sp2[0]==sp2[1] : #straightline
									nlLT[-1]+=[[sp1[1],[nx1,ny1],False,i]]
								else : #Bezier. First, recursively cut it up:
									nn=bez_divide(sp1[1],sp1[2],sp2[0],sp2[1])
									first=True #Flag entry to divided Bezier
									for bLT in nn : #save as two line segments
										for seg in range(3) :
											if seg>0 or first :
												nx1=bLT[seg][1]-bLT[seg+1][1]
												ny1=bLT[seg+1][0]-bLT[seg][0]
												l1=math.hypot(nx1,ny1)
												if l1<engraving_tolerance :
													continue
												nx1=nx1/l1 #normalise them
												ny1=ny1/l1
												nlLT[-1]+=[[bLT[seg],[nx1,ny1], False,i]]
												first=False
											if seg<2 : #get outgoing bisector
												nx0=nx1
												ny0=ny1
												nx1=bLT[seg+1][1]-bLT[seg+2][1]
												ny1=bLT[seg+2][0]-bLT[seg+1][0]
												l1=math.hypot(nx1,ny1)
												if l1<engraving_tolerance :
													continue
												nx1=nx1/l1 #normalise them
												ny1=ny1/l1
												#bisect
												bx,by,s=bisect((nx0,ny0),(nx1,ny1))
												nlLT[-1] += [[bLT[seg+1],[bx,by], True, 0.]]
							#LT for each segment - ends here.
							print_(("engraving_draw_calculation_paths=",self.options.engraving_draw_calculation_paths))
							if self.options.engraving_draw_calculation_paths:
								#Copy complete paths to 3D layer
								#print_("cspl",cspl)
								cspl+=[cspl[0]] #Close paths
								cspr+=[cspr[0]] #Close paths
								inkex.etree.SubElement(	gcode_3Dleft , inkex.addNS('path','svg'),
								{ "d": cubicsuperpath.formatPath([cspl]),
								'style': "stroke:#808080; stroke-opacity:1; stroke-width:0.6; fill:none",
								"gcodetools": "G1L outline"
								})
								inkex.etree.SubElement(	gcode_3Dright , inkex.addNS('path','svg'),
								{ "d": cubicsuperpath.formatPath([cspr]),
								'style': "stroke:#808080; stroke-opacity:1; stroke-width:0.6; fill:none",
								"gcodetools": "G1L outline"
								})

								for p in nlLT[-1]: #For last sub-path
									if p[2]: inkex.etree.SubElement(	engraving_group, inkex.addNS('path','svg'),
										{ "d":	"M %f,%f L %f,%f" %(p[0][0],p[0][1],p[0][0]+p[1][0]*10,p[0][1]+p[1][1]*10),
											'style':	"stroke:#f000af; stroke-opacity:0.46; stroke-width:0.1; fill:none",
											"gcodetools": "Engraving normals"
										})				
									else: inkex.etree.SubElement(	engraving_group, inkex.addNS('path','svg'),
										{ "d":	"M %f,%f L %f,%f" %(p[0][0],p[0][1],p[0][0]+p[1][0]*10,p[0][1]+p[1][1]*10),
											'style':	"stroke:#0000ff; stroke-opacity:0.46; stroke-width:0.1; fill:none",
											"gcodetools": "Engraving bisectors"
										})				


						#LT6a build nlLT[j] for each subpath - ends here
						#for nnn in nlLT :
							#print_("nlLT",nnn) #LT debug stuff
				 		# Calculate offset points
						reflex=False
						for j in xrange(len(nlLT)): #LT6b for each subpath
							cspm=[] #Will be my output. List of csps.
							wl=[] #Will be my w output list
							w = r = 0 #LT initial, as first point is an angle
							for i in xrange(len(nlLT[j])) : #LT for each node
								#LT Note: Python enables wrapping of array indices
								# backwards to -1, -2, but not forwards. Hence:
								n0 = nlLT[j][i-2] #previous node
								n1 = nlLT[j][i-1] #current node
								n2 = nlLT[j][i] #next node
								#if n1[2] == True and n1[3]==0 : # A straight angle
									#continue
								x1a,y1a = n1[0] #this point/start of this line
								nx,ny = n1[1]
								x1b,y1b = n2[0] #next point/end of this line
								if n1[2] == True : # We're at a corner
									bits=1
									bit0=0
									#lastr=r #Remember r from last line
									lastw=w #Remember w from last line
									w = max_dist
									if n1[3]>0 : #acute. Limit radius
										len1=math.hypot( (n0[0][0]-n1[0][0]),( n0[0][1]-n1[0][1]) )
										if i<(len(nlLT[j])-1) :
											len2=math.hypot( (nlLT[j][i+1][0][0]-n1[0][0]),(nlLT[j][i+1][0][1]-n1[0][1]) )
										else:
											len2=math.hypot( (nlLT[j][0][0][0]-n1[0][0]),(nlLT[j][0][0][1]-n1[0][1]) )
										#set initial r value, not to be exceeded
										w = math.sqrt(min(len1,len2))/n1[3]
								else: #line. Cut it up if long.
									if n0[3]>0 and not self.options.engraving_draw_calculation_paths :
										bit0=r*n0[3] #after acute corner
									else : bit0=0.0
									length=math.hypot((x1b-x1a),(y1a-y1b))
									bit0=(min(length,bit0))
									bits=int((length-bit0)/bitlen)
									#split excess evenly at both ends
									bit0+=(length-bit0-bitlen*bits)/2
									#print_("j,i,r,bit0,bits",j,i,w,bit0,bits)
								for b in xrange(bits) : #divide line into bits
									x1=x1a+ny*(b*bitlen+bit0)
									y1=y1a-nx*(b*bitlen+bit0)
									jjmin,iimin,w=get_biggest( (x1,y1), (nx,ny))
									print_("i,j,jjmin,iimin,w",i,j,jjmin,iimin,w)
									#w = min(r, toolr)
									wmax=max(wmax,w)
									if reflex : #just after a reflex corner
										reflex = False
										if w<lastw : #need to adjust it
											draw_point((x1,y1),(n0[0][0]+n0[1][0]*w,n0[0][1]+n0[1][1]*w),w, (lastw-w)/2)
											save_point((n0[0][0]+n0[1][0]*w,n0[0][1]+n0[1][1]*w),w,i,j,iimin,jjmin)
									if n1[2] == True : # We're at a corner
										if n1[3]>0 : #acute
											save_point((x1+nx*w,y1+ny*w),w,i,j,iimin,jjmin)
											draw_point((x1,y1),(x1,y1),0,0)
											save_point((x1,y1),0,i,j,iimin,jjmin)
										elif n1[3]<0  : #reflex
											if w>lastw :
												draw_point((x1,y1),(x1+nx*lastw,y1+ny*lastw),w, (w-lastw)/2)
												wmax=max(wmax,w)
												save_point((x1+nx*w,y1+ny*w),w,i,j,iimin,jjmin)
									elif b>0 and n2[3]>0 and not self.options.engraving_draw_calculation_paths : #acute corner coming up
										if jjmin==j and iimin==i+2 : break
									draw_point((x1,y1),(x1+nx*w,y1+ny*w),w, bitlen)
									save_point((x1+nx*w,y1+ny*w),w,i,j,iimin,jjmin)

								#LT end of for each bit of this line
								if n1[2] == True and n1[3]<0 : #reflex angle
									reflex=True
								lastw = w #remember this w
							#LT next i
							cspm+=[cspm[0]]
							print_("cspm",cspm)
							wl+=[wl[0]]
							print_("wl",wl)
							#Note: Original csp_points was a list, each element
							#being 4 points, with the first being the same as the
							#last of the previous set.
							#Each point is a list of [cx,cy,r,w]
							#I have flattened it to a flat list of points.
					
							if self.options.engraving_draw_calculation_paths==True:
								node =  inkex.etree.SubElement(	engraving_group, inkex.addNS('path','svg'), 										{
														 "d":	 cubicsuperpath.formatPath([cspm]),
														'style':	styles["biarc_style_i"]['biarc1'],
														"gcodetools": "Engraving calculation paths",
													})
								for i in xrange(len(cspm)):
									inkex.etree.SubElement(	engraving_group, inkex.addNS('path','svg'), 
											{"gcodetools": "Engraving calculation paths", 'style':	"fill:none; fill-opacity:0.46; stroke:#000000; stroke-width:0.1;", inkex.addNS('cx','sodipodi'):		str(cspm[i][1][0]),  inkex.addNS('cy','sodipodi'):		str(cspm[i][1][1]),inkex.addNS('rx','sodipodi'):		str(wl[i]), inkex.addNS('ry','sodipodi'):		str(wl[i]), inkex.addNS('type','sodipodi'):	'arc'})
							cspe += [cspm]
							wluu = [] #width list in user units: mm/inches
							for w in wl :
								wluu+=[ w / orientation_scale ]
							print_("wl in pixels",wl)
							print_("wl in user units",wluu)
							#LT previously, we was in pixels so gave wrong depth
							we   +=	[wluu]
						#LT6b For each subpath - ends here			
					#LT5 if it is a path - ends here
					#print_("cspe",cspe)
					#print_("we",we)
				#LT4 for each selected object in this layer - ends here

				if cspe!=[]:
					curve = self.parse_curve(cspe, layer, we, toolshape) #convert to lines
					self.draw_curve(curve, layer, engraving_group)
					gcode += self.generate_gcode(curve, layer, self.options.Zsurface)

			#LT3 for layers loop ends here
		if gcode!='' :
			self.header+="(Tool diameter should be at least "+str(2*wmax/orientation_scale)+unit+ ")\n"
			self.header+="(Depth, as a function of radius w, must be "+ self.tools[layer][0]['shape']+ ")\n"
			self.header+="(Rapid feeds use safe Z="+ str(self.options.Zsafe) + unit + ")\n"
			self.header+="(Material surface at Z="+ str(self.options.Zsurface) + unit + ")\n"
			self.export_gcode(gcode)
		else : 	self.error(_("No need to engrave sharp angles."),"warning")


################################################################################
###
###		Orientation
###
################################################################################
	def orientation(self, layer=None) :

		if layer == None :
			layer = self.current_layer if self.current_layer is not None else self.document.getroot()
		
		transform = self.get_transforms(layer)
		if transform != [] : 
			transform = self.reverse_transform(transform)
			transform = simpletransform.formatTransform(transform)

		if self.options.orientation_points_count == "graffiti" :
			print_(self.graffiti_reference_points)
			print_("Inserting graffiti points")
			if layer in self.graffiti_reference_points: graffiti_reference_points_count =  len(self.graffiti_reference_points[layer])
			else: graffiti_reference_points_count = 0
			axis = ["X","Y","Z","A"][graffiti_reference_points_count%4]
			attr = {'gcodetools': "Gcodetools graffiti reference point"}
			if 	transform != [] :
				attr["transform"] = transform 
			g = inkex.etree.SubElement(layer, inkex.addNS('g','svg'), attr)
			inkex.etree.SubElement(	g, inkex.addNS('path','svg'), 
				{
					'style':	"stroke:none;fill:#00ff00;", 	
					'd':'m %s,%s 2.9375,-6.343750000001 0.8125,1.90625 6.843748640396,-6.84374864039 0,0 0.6875,0.6875 -6.84375,6.84375 1.90625,0.812500000001 z z' % (graffiti_reference_points_count*100, 0),
					'gcodetools': "Gcodetools graffiti reference point arrow"
				})
			
			draw_text(axis,graffiti_reference_points_count*100+10,-10, group = g, gcodetools_tag = "Gcodetools graffiti reference point text")

		elif self.options.orientation_points_count == "in-out reference point" :
			draw_pointer(group = self.current_layer, x = self.view_center, figure="arrow", pointer_type = "In-out reference point", text = "In-out point")
	
		else :
			print_("Inserting orientation points")

			if layer in self.orientation_points:
				self.error(_("Active layer already has orientation points! Remove them or select another layer!"),"active_layer_already_has_orientation_points")
			
			attr = {"gcodetools":"Gcodetools orientation group"}
			if 	transform != [] :
				attr["transform"] = transform 

			orientation_group = inkex.etree.SubElement(layer, inkex.addNS('g','svg'), attr)
			doc_height = self.unittouu(self.document.getroot().get('height'))
			if self.document.getroot().get('height') == "100%" :
				doc_height = 1052.3622047
				print_("Overruding height from 100 percents to %s" % doc_height)
			if self.options.unit == "G21 (All units in mm)" : 
				points = [[0.,0.,self.options.Zsurface],[100.,0.,self.options.Zdepth],[0.,100.,0.]]
			elif self.options.unit == "G20 (All units in inches)" :
				points = [[0.,0.,self.options.Zsurface],[5.,0.,self.options.Zdepth],[0.,5.,0.]]
			if self.options.orientation_points_count == "2" :
				points = points[:2]
			for i in points :
				g = inkex.etree.SubElement(orientation_group, inkex.addNS('g','svg'), {'gcodetools': "Gcodetools orientation point (%s points)" % self.options.orientation_points_count})
				inkex.etree.SubElement(	g, inkex.addNS('path','svg'), 
					{
						'style':	"stroke:none;fill:#000000;", 	
						'd':'m %s,%s 2.9375,-6.343750000001 0.8125,1.90625 6.843748640396,-6.84374864039 0,0 0.6875,0.6875 -6.84375,6.84375 1.90625,0.812500000001 z z' % (i[0], -i[1]+doc_height),
						'gcodetools': "Gcodetools orientation point arrow"
					})

				draw_text("(%s; %s; %s)" % (i[0],i[1],i[2]), (i[0]+10), (-i[1]-10+doc_height), group = g, gcodetools_tag = "Gcodetools orientation point text")

		
################################################################################
###
###		Tools library
###
################################################################################
	def tools_library(self, layer=None) :
		# Add a tool to the drawing
		if layer == None :
			layer = self.current_layer if self.current_layer is not None else self.document.getroot()
		if layer in self.tools:
			self.error(_("Active layer already has a tool! Remove it or select another layer!"),"active_layer_already_has_tool")

		if self.options.tools_library_type == "cylinder cutter" :
			tool = {
					"name": "Cylindrical cutter",
					"id": "Cylindrical cutter 0001",
					"diameter":10,
					"penetration angle":90,
					"feed":"400",
					"penetration feed":"100",
					"depth step":"1",
					"tool change gcode":" "
			}
		elif self.options.tools_library_type == "lathe cutter" :
			tool = {
					"name": "Lathe cutter",
					"id": "Lathe cutter 0001",
					"diameter":10,
					"penetration angle":90,
					"feed":"400",
					"passing feed":"800",
					"fine feed":"100",
					"penetration feed":"100",
					"depth step":"1",
					"tool change gcode":" "
			}
		elif self.options.tools_library_type == "cone cutter":	
			tool = {
					"name": "Cone cutter",
					"id": "Cone cutter 0001",
					"diameter":10,
					"shape":"w",
					"feed":"400",
					"penetration feed":"100",
					"depth step":"1",
					"tool change gcode":" "
			}
		elif self.options.tools_library_type == "tangent knife":	
			tool = {
					"name": "Tangent knife",
					"id": "Tangent knife 0001",
					"feed":"400",
					"penetration feed":"100",
					"depth step":"100",
					"4th axis meaning": "tangent knife",
					"4th axis scale": 1.,
					"4th axis offset": 0,
					"tool change gcode":" "
			}
			
		elif self.options.tools_library_type == "plasma cutter":	
			tool = {
				"name": "Plasma cutter",
				"id": "Plasma cutter 0001",
				"diameter":10,
				"penetration feed":100,
				"feed":400,
				"gcode before path":"""G31 Z-100 F500 (find metal)
G92 Z0 (zero z)
G00 Z10 F500 (going up)
M03 (turn on plasma)
G04 P0.2 (pause)
G01 Z1 (going to cutting z)\n""",
				"gcode after path":"M05 (turn off plasma)\n",
			}
		elif self.options.tools_library_type == "graffiti":	
			tool = {
				"name": "Graffiti",
				"id": "Graffiti 0001",
				"diameter":10,
				"penetration feed":100,
				"feed":400,
				"gcode before path":"""M03 S1(Turn spray on)\n """,
				"gcode after path":"M05 (Turn spray off)\n ",
				"tool change gcode":"(Add G00 here to change sprayer if needed)\n",
				
			}

		else :
			tool = self.default_tool
		
		tool_num = sum([len(self.tools[i]) for i in self.tools])
		colors = ["00ff00","0000ff","ff0000","fefe00","00fefe", "fe00fe", "fe7e00", "7efe00", "00fe7e", "007efe", "7e00fe", "fe007e"]
		
		tools_group = inkex.etree.SubElement(layer, inkex.addNS('g','svg'), {'gcodetools': "Gcodetools tool definition"})
		bg = inkex.etree.SubElement(	tools_group, inkex.addNS('path','svg'), 
					{'style':	"fill:#%s;fill-opacity:0.5;stroke:#444444; stroke-width:1px;"%colors[tool_num%len(colors)], "gcodetools":"Gcodetools tool background"})

		y = 0
		keys = []
		for key in self.tools_field_order:
			if key in tool: keys += [key]
		for key in tool:
			if key not in keys: keys += [key]
		for key in keys :
			g = inkex.etree.SubElement(tools_group, inkex.addNS('g','svg'), {'gcodetools': "Gcodetools tool parameter"})
			draw_text(key, 0, y, group = g, gcodetools_tag = "Gcodetools tool definition field name", font_size = 10 if key!='name' else 20)
			param = tool[key]
			if type(param)==str and re.match("^\s*$",param) : param = "(None)"
			draw_text(param, 150, y, group = g, gcodetools_tag = "Gcodetools tool definition field value", font_size = 10 if key!='name' else 20)
			v = str(param).split("\n")
			y += 15*len(v) if key!='name' else 20*len(v)

		bg.set('d',"m -20,-20 l 400,0 0,%f -400,0 z " % (y+50))
		tool = []
		tools_group.set("transform", simpletransform.formatTransform([ [1,0,self.view_center[0]-150 ], [0,1,self.view_center[1]] ] ))


################################################################################
###
###		Check tools and OP asignment
###
################################################################################
	def check_tools_and_op(self):
		if len(self.selected)<=0 :
			self.error(_("Selection is empty! Will compute whole drawing."),"selection_is_empty_will_comupe_drawing")
			paths = self.paths
		else :
			paths = self.selected_paths
		#	Set group
		group = inkex.etree.SubElement( self.selected_paths.keys()[0] if len(self.selected_paths.keys())>0 else self.layers[0], inkex.addNS('g','svg') )
		trans_ = [[1,0.3,0],[0,0.5,0]]	

		self.set_markers()

		bounds = [float('inf'),float('inf'),float('-inf'),float('-inf')]
		tools_bounds = {}
		for layer in self.layers :
			if layer in paths :
				self.set_tool(layer)
				tool = self.tools[layer][0]
				tools_bounds[layer] = tools_bounds[layer] if layer in tools_bounds else [float("inf"),float("-inf")]
				style = simplestyle.formatStyle(tool["style"])
				for path in paths[layer] :
					style = "fill:%s; fill-opacity:%s; stroke:#000044; stroke-width:1; marker-mid:url(#CheckToolsAndOPMarker);" % (
					tool["style"]["fill"] if "fill" in tool["style"] else "#00ff00", 
					tool["style"]["fill-opacity"] if "fill-opacity" in tool["style"] else "0.5")
					group.insert( 0, inkex.etree.Element(path.tag, path.attrib))
					new = group.getchildren()[0]
					new.set("style", style)
					
					trans = self.get_transforms(path)
					trans = simpletransform.composeTransform( trans_, trans if trans != [] else [[1.,0.,0.],[0.,1.,0.]])
					csp = cubicsuperpath.parsePath(path.get("d"))
					simpletransform.applyTransformToPath(trans,csp)
					path_bounds = csp_simple_bound(csp)
					trans = simpletransform.formatTransform(trans)
					bounds = [min(bounds[0],path_bounds[0]), min(bounds[1],path_bounds[1]), max(bounds[2],path_bounds[2]), max(bounds[3],path_bounds[3])]
					tools_bounds[layer] = [min(tools_bounds[layer][0], path_bounds[1]), max(tools_bounds[layer][1], path_bounds[3])] 

					new.set("transform", trans) 
					trans_[1][2] += 20
				trans_[1][2] += 100

		for layer in self.layers :
			if layer in self.tools :
				if layer in tools_bounds :
					tool = self.tools[layer][0]
					g = copy.deepcopy(tool["self_group"])
					g.attrib["gcodetools"] = "Check tools and OP asignment"
					trans = [[1,0.3,bounds[2]],[0,0.5,tools_bounds[layer][0]]]	
					g.set("transform",simpletransform.formatTransform(trans))
					group.insert( 0, g )


################################################################################
###		TODO Launch browser on help tab
################################################################################
	def help(self):
		self.error(_("""Tutorials, manuals and support can be found at\nEnglish support forum:\n	http://www.cnc-club.ru/gcodetools\nand Russian support forum:\n	http://www.cnc-club.ru/gcodetoolsru"""),"warning")
		return


################################################################################
###		Lathe
################################################################################
	def generate_lathe_gcode(self, subpath, layer, feed_type) :
		if len(subpath) <2 : return ""
		feed = " F %f" % self.tool[feed_type] 
		x,z = self.options.lathe_x_axis_remap, self.options.lathe_z_axis_remap
		flip_angle = -1 if x.lower()+z.lower() in ["xz", "yx", "zy"] else 1
		alias = {"X":"I", "Y":"J", "Z":"K", "x":"i", "y":"j", "z":"k"} 
		i_, k_ = alias[x], alias[z]
		c = [ [subpath[0][1], "move", 0, 0, 0] ]
		#draw_csp(self.transform_csp([subpath],layer,True), color = "Orange", width = .1)
		for sp1,sp2 in zip(subpath,subpath[1:]) :
			c += biarc(sp1,sp2,0,0)
		for i in range(1,len(c)) : # Just in case check end point of each segment
			c[i-1][4] = c[i][0][:]
		c += [ [subpath[-1][1], "end", 0, 0, 0] ]			
		self.draw_curve(c, layer, style = styles["biarc_style_lathe_%s" % feed_type])
		
		gcode = ("G01 %s %f %s %f" % (x, c[0][4][0], z, c[0][4][1]) ) + feed + "\n" # Just in case move to the start...
		for s in c :
			if s[1] == 'line':
				gcode += ("G01 %s %f %s %f" % (x, s[4][0], z, s[4][1]) ) + feed + "\n"
			elif s[1] == 'arc':
				r = [(s[2][0]-s[0][0]), (s[2][1]-s[0][1])]
				if (r[0]**2 + r[1]**2)>self.options.min_arc_radius**2:
					r1, r2 = (P(s[0])-P(s[2])), (P(s[4])-P(s[2]))
					if abs(r1.mag()-r2.mag()) < 0.001 :
						gcode += ("G02" if s[3]*flip_angle<0 else "G03") + (" %s %f %s %f %s %f %s %f" % (x,s[4][0],z,s[4][1],i_,(s[2][0]-s[0][0]), k_, (s[2][1]-s[0][1]) ) ) + feed + "\n"
					else:
						r = (r1.mag()+r2.mag())/2
						gcode += ("G02" if s[3]*flip_angle<0 else "G03") + (" %s %f %s %f" % (x,s[4][0],z,y[4][1]) ) + " R%f"%r + feed + "\n"
		return gcode


	def lathe(self):
		if not self.check_dir() : return
		x,z = self.options.lathe_x_axis_remap, self.options.lathe_z_axis_remap
		x = re.sub("^\s*([XYZxyz])\s*$",r"\1",x) 
		z = re.sub("^\s*([XYZxyz])\s*$",r"\1",z)
		if x not in ["X", "Y", "Z", "x", "y", "z"] or z not in ["X", "Y", "Z", "x", "y", "z"] :
			self.error(_("Lathe X and Z axis remap should be 'X', 'Y' or 'Z'. Exiting..."),"warning") 
			return
		if x.lower() == z.lower() :
			self.error(_("Lathe X and Z axis remap should be the same. Exiting..."),"warning") 
			return
		if 	x.lower()+z.lower() in ["xy","yx"] : gcode_plane_selection = "G17 (Using XY plane)\n"
		if 	x.lower()+z.lower() in ["xz","zx"] : gcode_plane_selection = "G18 (Using XZ plane)\n"
		if 	x.lower()+z.lower() in ["zy","yz"] : gcode_plane_selection = "G19 (Using YZ plane)\n"
		self.options.lathe_x_axis_remap, self.options.lathe_z_axis_remap = x, z
	
		paths = self.selected_paths
		self.tool = []
		gcode = ""
		for layer in self.layers :
			if layer in paths :
				self.set_tool(layer)
				if self.tool != self.tools[layer][0] :
					self.tool = self.tools[layer][0]
					self.tool["passing feed"]	= float(self.tool["passing feed"] if "passing feed" in self.tool else self.tool["feed"])
					self.tool["feed"]			= float(self.tool["feed"])
					self.tool["fine feed"]		= float(self.tool["fine feed"] if "fine feed" in self.tool else self.tool["feed"]) 
					gcode += ( "(Change tool to %s)\n" % re.sub("\"'\(\)\\\\"," ",self.tool["name"]) ) + self.tool["tool change gcode"] + "\n"
					
				for path in paths[layer]:
					csp = self.transform_csp(cubicsuperpath.parsePath(path.get("d")),layer)
					
					for subpath in csp :
						# Offset the path if fine cut is defined.
						fine_cut = subpath[:]
						if self.options.lathe_fine_cut_width>0 :
							r = self.options.lathe_fine_cut_width
							if self.options.lathe_create_fine_cut_using == "Move path" :
								subpath = [ [  [i2[0],i2[1]+r]  for i2 in i1]  for i1 in subpath]
							else :
								# Close the path to make offset correct 
								bound = csp_simple_bound([subpath])
								minx,miny,maxx,maxy = csp_true_bounds([subpath])
								offsetted_subpath = csp_subpath_line_to(subpath[:], [ [subpath[-1][1][0], miny[1]-r*10 ], [subpath[0][1][0], miny[1]-r*10 ], [subpath[0][1][0], subpath[0][1][1] ]  ])
								left,right = subpath[-1][1][0], subpath[0][1][0]
								if left>right : left, right = right,left
								offsetted_subpath = csp_offset([offsetted_subpath], r if not csp_subpath_ccw(offsetted_subpath) else -r ) 
								offsetted_subpath = csp_clip_by_line(offsetted_subpath,  [left,10], [left,0] )
								offsetted_subpath = csp_clip_by_line(offsetted_subpath,  [right,0], [right,10] )
								offsetted_subpath = csp_clip_by_line(offsetted_subpath,  [0, miny[1]-r], [10, miny[1]-r] )
								#draw_csp(self.transform_csp(offsetted_subpath,layer,True), color = "Green", width = 1)
								# Join offsetted_subpath together 
								# Hope there wont be any cicles
								subpath = csp_join_subpaths(offsetted_subpath)[0]
						
						# Create solid object from path and lathe_width 
						bound = csp_simple_bound([subpath])
						top_start, top_end = [subpath[0][1][0], self.options.lathe_width+self.options.Zsafe+self.options.lathe_fine_cut_width], [subpath[-1][1][0], self.options.lathe_width+self.options.Zsafe+self.options.lathe_fine_cut_width]

						gcode += ("G01 %s %f F %f \n" % (z, top_start[1], self.tool["passing feed"]) )
						gcode += ("G01 %s %f %s %f F %f \n" % (x, top_start[0], z, top_start[1], self.tool["passing feed"]) )

						subpath = csp_concat_subpaths(csp_subpath_line_to([],[top_start,subpath[0][1]]), subpath)
						subpath = csp_subpath_line_to(subpath,[top_end,top_start])
	
						
						width = max(0, self.options.lathe_width - max(0, bound[1]) )
						step = self.tool['depth step']
						steps = int(math.ceil(width/step))
						for i in range(steps+1):
							current_width = self.options.lathe_width - step*i
							intersections = []
							for j in range(1,len(subpath)) :
								sp1,sp2 = subpath[j-1], subpath[j]
								intersections += [[j,k] for k in csp_line_intersection([bound[0]-10,current_width], [bound[2]+10,current_width], sp1, sp2)]
								intersections += [[j,k] for k in csp_line_intersection([bound[0]-10,current_width+step], [bound[2]+10,current_width+step], sp1, sp2)]
							parts = csp_subpath_split_by_points(subpath,intersections)
							for part in parts :
								minx,miny,maxx,maxy = csp_true_bounds([part])
								y = (maxy[1]+miny[1])/2
								if  y > current_width+step :
									gcode += self.generate_lathe_gcode(part,layer,"passing feed")
								elif current_width <= y <= current_width+step :
									gcode += self.generate_lathe_gcode(part,layer,"feed")
								else :
									# full step cut
									part = csp_subpath_line_to([], [part[0][1], part[-1][1]] )
									gcode += self.generate_lathe_gcode(part,layer,"feed")
									
						top_start, top_end = [fine_cut[0][1][0], self.options.lathe_width+self.options.Zsafe+self.options.lathe_fine_cut_width], [fine_cut[-1][1][0], self.options.lathe_width+self.options.Zsafe+self.options.lathe_fine_cut_width]
						gcode += "\n(Fine cutting start)\n(Calculating fine cut using %s)\n"%self.options.lathe_create_fine_cut_using
						for i in range(self.options.lathe_fine_cut_count) :
							width = self.options.lathe_fine_cut_width*(1-float(i+1)/self.options.lathe_fine_cut_count ) 
							if width == 0 : 
								current_pass = fine_cut							
							else :	
								if self.options.lathe_create_fine_cut_using == "Move path" :
									current_pass = [ [  [i2[0],i2[1]+width]  for i2 in i1]  for i1 in fine_cut]
								else :
									minx,miny,maxx,maxy = csp_true_bounds([fine_cut])
									offsetted_subpath = csp_subpath_line_to(fine_cut[:], [ [fine_cut[-1][1][0], miny[1]-r*10 ], [fine_cut[0][1][0], miny[1]-r*10 ], [fine_cut[0][1][0], fine_cut[0][1][1] ]  ])
									left,right = fine_cut[-1][1][0], fine_cut[0][1][0]
									if left>right : left, right = right,left
									offsetted_subpath = csp_offset([offsetted_subpath], width if not csp_subpath_ccw(offsetted_subpath) else -width ) 
									offsetted_subpath = csp_clip_by_line(offsetted_subpath,  [left,10], [left,0] )
									offsetted_subpath = csp_clip_by_line(offsetted_subpath,  [right,0], [right,10] )
									offsetted_subpath = csp_clip_by_line(offsetted_subpath,  [0, miny[1]-r], [10, miny[1]-r] )
									current_pass = csp_join_subpaths(offsetted_subpath)[0]


							gcode += "\n(Fine cut %i-th cicle start)\n"%(i+1)					
							gcode += ("G01 %s %f %s %f F %f \n" % (x, top_start[0], z, top_start[1], self.tool["passing feed"]) )
							gcode += ("G01 %s %f %s %f F %f \n" % (x, current_pass[0][1][0], z, current_pass[0][1][1]+self.options.lathe_fine_cut_width, self.tool["passing feed"]) )
							gcode += ("G01 %s %f %s %f F %f \n" % (x, current_pass[0][1][0], z, current_pass[0][1][1], self.tool["fine feed"]) )
						
							gcode += self.generate_lathe_gcode(current_pass,layer,"fine feed")
							gcode += ("G01 %s %f F %f \n" % (z, top_start[1], self.tool["passing feed"]) )
							gcode += ("G01 %s %f %s %f F %f \n" % (x, top_start[0], z, top_start[1], self.tool["passing feed"]) )
	
		self.export_gcode(gcode)
		
################################################################################
###
###		Lathe modify path 
### 	Modifies path to fit current cutter. As for now straight rect cutter.
###
################################################################################

	def lathe_modify_path(self):
		if self.selected_paths == {} and self.options.auto_select_paths:
			paths=self.paths
			self.error(_("No paths are selected! Trying to work on all available paths."),"warning")
		else :
			paths = self.selected_paths

		for layer in self.layers :
			if layer in paths :
				width = self.options.lathe_rectangular_cutter_width
				#self.set_tool(layer)
				for path in paths[layer]:
					csp = self.transform_csp(cubicsuperpath.parsePath(path.get("d")),layer)
					new_csp = []
					for subpath in csp: 
						orientation = subpath[-1][1][0]>subpath[0][1][0]
						last_n = None
						last_o = 0
						new_subpath = []

						# Split segment at x' and y' == 0
						for sp1, sp2 in zip(subpath[:],subpath[1:]):
							ax,ay,bx,by,cx,cy,dx,dy = csp_parameterize(sp1,sp2)
							roots = cubic_solver_real(0, 3*ax, 2*bx, cx)
							roots += cubic_solver_real(0, 3*ay, 2*by, cy)
							new_subpath = csp_concat_subpaths(new_subpath, csp_seg_split(sp1,sp2,roots))
						subpath = new_subpath
						new_subpath = []
						first_seg = True
						for sp1, sp2 in zip(subpath[:],subpath[1:]):
							n = csp_normalized_normal(sp1,sp2,0)
							a  = math.atan2(n[0],n[1])
							if a == 0 or a == math.pi :
								n = csp_normalized_normal(sp1,sp2,1)
							a  = math.atan2(n[0],n[1])							
							if a!=0 and a!=math.pi:
								o = 0 if 0<a<=math.pi/2 or -math.pi<a<-math.pi/2 else 1
								if not orientation: o = 1-o
								
								# Add first horisontal straight line if needed
								if not first_seg and new_subpath==[] : new_subpath = [ [[subpath[0][i][0] - width*o ,subpath[0][i][1]] for i in range(3)] ]
								
								new_subpath = csp_concat_subpaths(
										new_subpath,
										[
											[[sp1[i][0] - width*o ,sp1[i][1]] for i in range(3)],
											[[sp2[i][0] - width*o ,sp2[i][1]] for i in range(3)]
										]
									)
							first_seg = False
							
						# Add last horisontal straigth line if needed
						if a==0 or a==math.pi :
							new_subpath +=  [ [[subpath[-1][i][0] - width*o ,subpath[-1][i][1]] for i in range(3)] ]

						
					new_csp += [new_subpath]	
					self.draw_csp(new_csp,layer)
#								
#								o = (1 if cross(n, [0,1])>0 else -1)*orientation
#								new_subpath += [  [sp1[i][0] - width*o,sp1[i][1]] for i in range(3)  ]
#							n = csp_normalized_normal(sp1,sp2,1) 
#							o = (1 if cross(n, [0,1])>0 else -1)*orientation
#							new_subpath += [  [sp2[i][0] - width*o,sp2[i][1]] for i in range(3)  ]
							
							
################################################################################
###
### Update function
###
###	Gets file containing version information from the web and compaares it with.
###	current version.
################################################################################
	
	def update(self) :
		try :
			import urllib
			f = urllib.urlopen("http://www.cnc-club.ru/gcodetools_latest_version", proxies = urllib.getproxies())
			a = f.read()
			for s in a.split("\n") :
				r = re.search(r"Gcodetools\s+latest\s+version\s*=\s*(.*)",s)
				if r : 
					ver = r.group(1).strip()
					if ver != gcodetools_current_version :
						self.error("There is a newer version of Gcodetools you can get it at: \nhttp://www.cnc-club.ru/gcodetools (English version). \nhttp://www.cnc-club.ru/gcodetools_ru (Russian version). ","Warning")					
					else :
						self.error("You are currently using latest stable version of Gcodetools.","Warning")					
					return 
			self.error("Can not check the latest version. You can check it manualy at \nhttp://www.cnc-club.ru/gcodetools (English version). \nhttp://www.cnc-club.ru/gcodetools_ru (Russian version). \nCurrent version is Gcodetools %s"%gcodetools_current_version,"Warning")					
		except :
			self.error("Can not check the latest version. You can check it manualy at \nhttp://www.cnc-club.ru/gcodetools (English version). \nhttp://www.cnc-club.ru/gcodetools_ru (Russian version). \nCurrent version is Gcodetools %s"%gcodetools_current_version,"Warning")					
				


################################################################################
### Graffiti function generates Gcode for graffiti drawer
################################################################################
	def graffiti(self) :
		# Get reference points. 
		
		def get_gcode_coordinates(point,layer):
			gcode = ''	
			pos = []
			for ref_point in self.graffiti_reference_points[layer] :
				c = math.sqrt((point[0]-ref_point[0][0])**2 + (point[1]-ref_point[0][1])**2)
				gcode += " %s %f"%(ref_point[1], c)
				pos += [c]
			return pos, gcode
		
		
		def graffiti_preview_draw_point(x1,y1,color,radius=.5):
			self.graffiti_preview = self.graffiti_preview
			r,g,b,a_ = color
			for x in range(int(x1-1-math.ceil(radius)), int(x1+1+math.ceil(radius)+1)):
				for y in range(int(y1-1-math.ceil(radius)), int(y1+1+math.ceil(radius)+1)):
					if x>=0 and y>=0 and y<len(self.graffiti_preview) and x*4<len(self.graffiti_preview[0]) :
						d = math.sqrt( (x1-x)**2 +(y1-y)**2 )
						a = float(a_)*( max(0,(1-(d-radius))) if d>radius else 1 )/256
						self.graffiti_preview[y][x*4] = int(r*a + (1-a)*self.graffiti_preview[y][x*4])
						self.graffiti_preview[y][x*4+1] = int(g*a + (1-a)*self.graffiti_preview[y][x*4+1])
						self.graffiti_preview[y][x*4+2] = int(g*b + (1-a)*self.graffiti_preview[y][x*4+2])
						self.graffiti_preview[y][x*4+3] = min(255,int(self.graffiti_preview[y][x*4+3]+a*256))

		def graffiti_preview_transform(x,y):
			tr = self.graffiti_preview_transform
			d = max(tr[2]-tr[0]+2,tr[3]-tr[1]+2) 
			return  [(x-tr[0]+1)*self.options.graffiti_preview_size/d, self.options.graffiti_preview_size - (y-tr[1]+1)*self.options.graffiti_preview_size/d]


		def draw_graffiti_segment(layer,start,end,feed,color=(0,255,0,40),emmit=1000):
			# Emit = dots per second
			l = math.sqrt(sum([(start[i]-end[i])**2 for i in range(len(start))]))
			time_ = l/feed
			c1,c2 = self.graffiti_reference_points[layer][0][0],self.graffiti_reference_points[layer][1][0]
			d = math.sqrt( (c1[0]-c2[0])**2 + (c1[1]-c2[1])**2 )
			if d == 0 : raise ValueError, "Error! Reference points should not be the same!"
			for i in range(int(time_*emmit+1)) :
				t = i/(time_*emmit)
				r1,r2 = start[0]*(1-t) + end[0]*t, start[1]*(1-t) + end[1]*t
				a = (r1**2-r2**2+d**2)/(2*d)
				h = math.sqrt(r1**2 - a**2)
				xa = c1[0] + a*(c2[0]-c1[0])/d
				ya = c1[1] + a*(c2[1]-c1[1])/d
			
				x1 = xa + h*(c2[1]-c1[1])/d
				x2 = xa - h*(c2[1]-c1[1])/d
				y1 = ya - h*(c2[0]-c1[0])/d
				y2 = ya + h*(c2[0]-c1[0])/d
				
				x = x1 if y1<y2 else x2
				y = min(y1,y2)			
				x,y = graffiti_preview_transform(x,y)
				graffiti_preview_draw_point(x,y,color)
			
		def create_connector(p1,p2,t1,t2):
			P1,P2 = P(p1), P(p2)
			N1, N2  = P(rotate_ccw(t1)), P(rotate_ccw(t2))
			r = self.options.graffiti_min_radius
			C1,C2 = P1+N1*r, P2+N2*r
			# Get closest possible centers of arcs, also we define that arcs are both ccw or both not. 
			dc, N1, N2, m = (
					(
						(((P2-N1*r) - (P1-N2*r)).l2(),-N1,-N2, 1)
							 if  vectors_ccw(t1,t2) else
						(((P2+N1*r) - (P1+N2*r)).l2(), N1, N2,-1) 
					)
					 if vectors_ccw((P1-C1).to_list(),t1) == vectors_ccw((P2-C2).to_list(),t2) else
					(
						(((P2+N1*r) - (P1-N2*r)).l2(), N1,-N2, 1) 
							 if vectors_ccw(t1,t2) else
						(((P2-N1*r) - (P1+N2*r)).l2(),-N1, N2, 1)
					)	 
				)
			dc = math.sqrt(dc)
			C1,C2 = P1+N1*r, P2+N2*r
			Dc = C2-C1 
			
			if dc == 0 :
				# can be joined by one arc
				return csp_from_arc(p1, p2, C1.to_list(), r, t1)

			cos, sin = Dc.x/dc, Dc.y/dc
			#draw_csp(self.transform_csp([[ [[C1.x-r*sin,C1.y+r*cos]]*3,[[C2.x-r*sin,C2.y+r*cos]]*3 ]],layer,reverse=True), color = "#00ff00;" )
			#draw_pointer(self.transform(C1.to_list(),layer,reverse=True))
			#draw_pointer(self.transform(C2.to_list(),layer,reverse=True))
			
			p1_end = [C1.x-r*sin*m,C1.y+r*cos*m]
			p2_st  = [C2.x-r*sin*m,C2.y+r*cos*m]
			if point_to_point_d2(p1,p1_end)<0.0001 and point_to_point_d2(p2,p2_st)<0.0001 :
				return ([[p1,p1,p1],[p2,p2,p2]])
			
			arc1 = csp_from_arc(p1, p1_end, C1.to_list(), r, t1)
			arc2 = csp_from_arc(p2_st, p2, C2.to_list(), r, [cos,sin])
			return csp_concat_subpaths(arc1,arc2)

		if not self.check_dir() : return
		if self.selected_paths == {} and self.options.auto_select_paths:
			paths=self.paths
			self.error(_("No paths are selected! Trying to work on all available paths."),"warning")
		else :
			paths = self.selected_paths
		self.tool = []
		gcode = """(Header)
(Generated by gcodetools from Inkscape.)
(Using graffiti extension.)
(Header end.)"""
		
		minx,miny,maxx,maxy = float("inf"),float("inf"),float("-inf"),float("-inf") 

		# Get all reference points and path's bounds to make preview

		for layer in self.layers :
			if layer in paths :
				# Set reference points
				if layer not in self.graffiti_reference_points:
					reference_points = None
					for i in range(self.layers.index(layer),-1,-1):
						if self.layers[i] in self.graffiti_reference_points :
							reference_points = self.graffiti_reference_points[self.layers[i]]
							self.graffiti_reference_points[layer] = self.graffiti_reference_points[self.layers[i]]
							break
					if reference_points == None :
					 	self.error('There are no graffiti reference points for layer %s'%layer,"error")
					 	
				# Transform reference points
				for i in range(len(self.graffiti_reference_points[layer])):
					self.graffiti_reference_points[layer][i][0] = self.transform(self.graffiti_reference_points[layer][i][0], layer)
					point = self.graffiti_reference_points[layer][i]
					gcode += "(Reference point %f;%f for %s axis)\n"%(point[0][0],point[0][1],point[1])

				if self.options.graffiti_create_preview :
					for point in self.graffiti_reference_points[layer]:
						minx,miny,maxx,maxy = min(minx,point[0][0]), min(miny,point[0][1]), max(maxx,point[0][0]), max(maxy,point[0][1])
					for path in paths[layer]:
						csp = cubicsuperpath.parsePath(path.get("d"))
						csp = self.apply_transforms(path, csp)
						csp = self.transform_csp(csp, layer)
						bounds = csp_simple_bound(csp)
						minx,miny,maxx,maxy = min(minx,bounds[0]), min(miny,bounds[1]), max(maxx,bounds[2]), max(maxy,bounds[3]) 
						
		if self.options.graffiti_create_preview :
			self.graffiti_preview = list([ [255]*(4*self.options.graffiti_preview_size) for i in range(self.options.graffiti_preview_size)])
			self.graffiti_preview_transform = [minx,miny,maxx,maxy]

		for layer in self.layers :
			if layer in paths :

				r = re.match("\s*\(\s*([0-9\-,.]+)\s*;\s*([0-9\-,.]+)\s*\)\s*",self.options.graffiti_start_pos)
				if r :
					start_point = [float(r.group(1)),float(r.group(2))]
				else :
					start_point = [0.,0.]
				last_sp1 = [[start_point[0],start_point[1]-10] for i in range(3)]	
				last_sp2 = [start_point for i in range(3)]

				self.set_tool(layer)
				self.tool = self.tools[layer][0]
				# Change tool every layer. (Probably layer = color so it'll be 
				# better to change it even if the tool has not been changed)
				gcode += ( "(Change tool to %s)\n" % re.sub("\"'\(\)\\\\"," ",self.tool["name"]) ) + self.tool["tool change gcode"] + "\n"
				
				subpaths = []
				for path in paths[layer]:
					# Rebuild the paths to polyline.
					csp = cubicsuperpath.parsePath(path.get("d"))
					csp = self.apply_transforms(path, csp)
					csp = self.transform_csp(csp, layer)
					subpaths += csp 
				polylines = []
				while len(subpaths)>0:
					i = min( [( point_to_point_d2(last_sp2[1],subpaths[i][0][1]),i) for i in range(len(subpaths))] )[1]
					subpath = subpaths[i][:]
					del subpaths[i]
					polylines += [ 
									['connector', create_connector(
													last_sp2[1],
													subpath[0][1],
													csp_normalized_slope(last_sp1,last_sp2,1.),
													csp_normalized_slope(subpath[0],subpath[1],0.),
									)]
								]
					polyline = []
					spl = None
					
					#  remove zerro length segments
					i = 0
					while i<len(subpath)-1:
						if 	(cspseglength(subpath[i],subpath[i+1])<0.00000001 ) : 
							subpath[i][2] = subpath[i+1][2]
							del subpath[i+1]
						else :
							i += 1
					
					for sp1, sp2 in zip(subpath,subpath[1:]) :
						if spl != None and abs(cross( csp_normalized_slope(spl,sp1,1.),csp_normalized_slope(sp1,sp2,0.) )) > 0.1 : # TODO add coefficient into inx
							# We've got sharp angle at sp1.
							polyline += [sp1]
							polylines += [['draw',polyline[:]]]
							polylines += [ 
											['connector', create_connector(
													sp1[1],
													sp1[1],
													csp_normalized_slope(spl,sp1,1.),
													csp_normalized_slope(sp1,sp2,0.),
											)]
										]
							polyline = []
						# max_segment_length 
						polyline += [ sp1 ]
						print_(polyline)
						print_(sp1)
						
						spl = sp1
					polyline += [ sp2 ]	
					polylines += [ ['draw',polyline[:]] ]

					last_sp1, last_sp2 = sp1,sp2
					
					
				# Add return to start_point
				if polylines == [] : continue
				polylines += [ ["connect1",  [ [polylines[-1][1][-1][1] for i in range(3)],[start_point for i in range(3)] ] ] ]
				
				# Make polilynes from polylines. They are still csp.
				for i in range(len(polylines)) :
					polyline = []
					l = 0
					print_("polylines",polylines)
					print_(polylines[i])
					for sp1,sp2 in zip(polylines[i][1],polylines[i][1][1:]) :
						print_(sp1,sp2)
						l = cspseglength(sp1,sp2)
						if l>0.00000001 : 
							polyline += [sp1[1]]
							parts = int(math.ceil(l/self.options.graffiti_max_seg_length))
							for j in range(1,parts):
								polyline += [csp_at_length(sp1,sp2,float(j)/parts) ]
					if l>0.00000001 :
						polyline += [sp2[1]]
					print_(i)
					polylines[i][1] = polyline
									
				t = 0
				last_state = None
				for polyline_ in polylines:
					polyline = polyline_[1]
					# Draw linearization
					if self.options.graffiti_create_linearization_preview :
						t += 1
						csp = [ [polyline[i],polyline[i],polyline[i]] for i in range(len(polyline))]
						draw_csp(self.transform_csp([csp],layer,reverse=True), color = "#00cc00;" if polyline_[0]=='draw' else "#ff5555;")

	
				# Export polyline to gcode
				# we are making trnsform from XYZA coordinates to R1...Rn
				# where R1...Rn are radius vectors from grafiti reference points
				# to current (x,y) point. Also we need to assign custom feed rate 
				# for each segment. And we'll use only G01 gcode.
					last_real_pos, g = get_gcode_coordinates(polyline[0],layer)
					last_pos = polyline[0]
					if polyline_[0] == "draw" and last_state!="draw":
						gcode += self.tool['gcode before path']+"\n"
					for point in polyline :
						real_pos, g = get_gcode_coordinates(point,layer)
						real_l = sum([(real_pos[i]-last_real_pos[i])**2 for i in range(len(last_real_pos))])
						l = (last_pos[0]-point[0])**2 + (last_pos[1]-point[1])**2 
						if l!=0:
							feed = self.tool['feed']*math.sqrt(real_l/l)
							gcode += "G01 " + g + " F %f\n"%feed
							if self.options.graffiti_create_preview :			
								draw_graffiti_segment(layer,real_pos,last_real_pos,feed,color=(0,0,255,200) if polyline_[0] == "draw" else (255,0,0,200),emmit=self.options.graffiti_preview_emmit)
							last_real_pos = real_pos
							last_pos = point[:]
					if polyline_[0] == "draw" and last_state!="draw" :
						gcode += self.tool['gcode after path']+"\n"
					last_state = polyline_[0]
		self.export_gcode(gcode, no_headers=True)				
		if self.options.graffiti_create_preview :
			try :
				# Draw reference points			
				for layer in self.graffiti_reference_points:
					for point in self.graffiti_reference_points[layer] :
						x, y = graffiti_preview_transform(point[0][0],point[0][1])
						graffiti_preview_draw_point(x,y,(0,255,0,255),radius=5)
			
				import png
				writer = png.Writer(width=self.options.graffiti_preview_size, height=self.options.graffiti_preview_size, size=None, greyscale=False, alpha=True, bitdepth=8, palette=None, transparent=None, background=None, gamma=None, compression=None, interlace=False, bytes_per_sample=None, planes=None, colormap=None, maxval=None, chunk_limit=1048576)
				f = open(self.options.directory+self.options.file+".png", 'wb') 
				writer.write(f,self.graffiti_preview) 
				f.close()
				
			except :	
				self.error("Png module have not been found!","warning")
				

										
################################################################################
###
###		Effect
###
###		Main function of Gcodetools class
###
################################################################################
	def effect(self) :
		start_time = time.time()
		global options
		options = self.options
		options.self = self
		options.doc_root = self.document.getroot()

		# define print_ function 
		global print_
		if self.options.log_create_log :
			try :
				if os.path.isfile(self.options.log_filename) : os.remove(self.options.log_filename)
				f = open(self.options.log_filename,"a")
				f.write("Gcodetools log file.\nStarted at %s.\n%s\n" % (time.strftime("%d.%m.%Y %H:%M:%S"),options.log_filename))
				f.write("%s tab is active.\n" % self.options.active_tab)
				f.close()
			except :
				print_  = lambda *x : None 
		else : print_  = lambda *x : None 
		if self.options.active_tab == '"help"' :
			self.help()
			return
		elif self.options.active_tab == '"about"' :
			self.help()
			return
		
		elif self.options.active_tab ==  '"test"' :
			self.test()
			
		elif self.options.active_tab not in ['"dxfpoints"','"path-to-gcode"', '"area_fill"', '"area"', '"area_artefacts"', '"engraving"', '"orientation"', '"tools_library"', '"lathe"', '"offset"', '"arrangement"', '"update"', '"graffiti"', '"lathe_modify_path"', '"plasma-prepare-path"']:
			self.error(_("Select one of the action tabs - Path to Gcode, Area, Engraving, DXF points, Orientation, Offset, Lathe or Tools library.\n Current active tab id is %s" % self.options.active_tab),"error")
		else:
			# Get all Gcodetools data from the scene.
			self.get_info()
			if self.options.active_tab in ['"dxfpoints"','"path-to-gcode"', '"area_fill"', '"area"', '"area_artefacts"', '"engraving"', '"lathe"', '"graffiti"', '"plasma-prepare-path"']:
				if self.orientation_points == {} :
					self.error(_("Orientation points have not been defined! A default set of orientation points has been automatically added."),"warning")
					self.orientation( self.layers[min(1,len(self.layers)-1)] )		
					self.get_info()
				if self.tools == {} :
					self.error(_("Cutting tool has not been defined! A default tool has been automatically added."),"warning")
					self.options.tools_library_type = "default"
					self.tools_library( self.layers[min(1,len(self.layers)-1)] )
					self.get_info()
			if self.options.active_tab == '"path-to-gcode"': 
				self.path_to_gcode()		
			elif self.options.active_tab == '"area_fill"': 
				self.area_fill()		
			elif self.options.active_tab == '"area"': 
				self.area()		
			elif self.options.active_tab == '"area_artefacts"': 
				self.area_artefacts()		
			elif self.options.active_tab == '"dxfpoints"': 
				self.dxfpoints()		
			elif self.options.active_tab == '"engraving"': 
				self.engraving()		
			elif self.options.active_tab == '"orientation"': 
				self.orientation()		
			elif self.options.active_tab == '"graffiti"': 
				self.graffiti()		
			elif self.options.active_tab == '"tools_library"': 
				if self.options.tools_library_type != "check":
					self.tools_library()
				else :	
					self.check_tools_and_op()
			elif self.options.active_tab == '"lathe"': 
				self.lathe()
			elif self.options.active_tab == '"lathe_modify_path"': 
				self.lathe_modify_path()
			elif self.options.active_tab == '"update"': 
				self.update()
			elif self.options.active_tab == '"offset"': 
				if self.options.offset_just_get_distance :
					for layer in self.selected_paths :
						if len(self.selected_paths[layer]) == 2 :
							csp1, csp2 = cubicsuperpath.parsePath(self.selected_paths[layer][0].get("d")), cubicsuperpath.parsePath(self.selected_paths[layer][1].get("d"))
							dist = csp_to_csp_distance(csp1,csp2)
							print_(dist)
							draw_pointer( list(csp_at_t(csp1[dist[1]][dist[2]-1],csp1[dist[1]][dist[2]],dist[3]))
										+list(csp_at_t(csp2[dist[4]][dist[5]-1],csp2[dist[4]][dist[5]],dist[6])),"red","line", comment = math.sqrt(dist[0]))
					return	
				if self.options.offset_step == 0 : self.options.offset_step = self.options.offset_radius
				if self.options.offset_step*self.options.offset_radius <0 : self.options.offset_step *= -1
				time_ = time.time()
				offsets_count = 0
				for layer in self.selected_paths :
					for path in self.selected_paths[layer] :
										
						offset = self.options.offset_step/2
						while abs(offset) <= abs(self.options.offset_radius) :
							offset_ = csp_offset(cubicsuperpath.parsePath(path.get("d")), offset)				
							offsets_count += 1
							if offset_ != [] : 
								for iii in offset_ :
									draw_csp([iii], color="Green", width=1)		
									#print_(offset_)
							else :
								print_("------------Reached empty offset at radius %s"% offset )
								break
							offset += self.options.offset_step
				print_()
				print_("-----------------------------------------------------------------------------------")				
				print_("-----------------------------------------------------------------------------------")				
				print_("-----------------------------------------------------------------------------------")				
				print_()
				print_("Done in %s"%(time.time()-time_))				
				print_("Total offsets count %s"%offsets_count)				
			elif self.options.active_tab == '"arrangement"': 
				self.arrangement()

			elif self.options.active_tab == '"plasma-prepare-path"': 
				self.plasma_prepare_path()		


		print_("------------------------------------------")
		print_("Done in %f seconds"%(time.time()-start_time))
		print_("End at %s."%time.strftime("%d.%m.%Y %H:%M:%S"))
		
		
#						
gcodetools = Gcodetools()
gcodetools.affect()					

