"""
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 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.
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)
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
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
###
# standard library
import os
import math
import bezmisc
import re
import copy
import sys
import time
import cmath
import numpy
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.
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))
# VARIABLE NAME SHOULD BE A STRING! Like isset("foobar")
################################################################################
###
### Styles and additional parameters
###
################################################################################
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)
%"""
}
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' }),
}
################################################################################
### Gcode additional functions
################################################################################
if replace_new_line :
res = ""
if s[-1] == "\n" : s = s[:-1]
for a in s.split("\n") :
if a != "" :
else :
res += "\n"
return res
################################################################################
### Cubic Super Path additional functions
################################################################################
res = []
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 :
else :
return res
# 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
#we've got a special case here
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:
if t == 0 or t == 1 :
#we've got another special case here
if y1==y :
# the point is on the path
return on_the_path
# if t == 0 we sould 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
if ax1==0 and bx1==0 and cx1==0 and dx1==x : continue # this segment parallel to the ray, so skip it
break
else :
if y1==y :
# the point is on the path
return on_the_path
else :
else:
return csp
for p in sp:
min_dist = 1e100
max_dist = 0
for i in range(4) :
for j in range(4) :
# 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 :
return min_dist
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) :
i = 0
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 :
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 += [0.]
dist = d+[1.]
sample_points -= 2
# try to find closes points using Newtons method
for k in range(sample_points) :
for j in range(sample_points) :
i = 0
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)
#draw_pointer(csp_at_t(sp1,sp2,t1))
if F2!=None :
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
return dist
return dist
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])
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)
return dist
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")
# Finds minx,miny,maxx,maxy of the csp and return their (x,y,i,j,t)
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]))
############################################################################
### 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.
############################################################################
i = 0
if det!=0 :
else: break
i += 1
if a==b :
return
elif depth_a>0 :
elif depth_b>0 :
else : # Both segments have been subdevided enougth. Let's get some intersections :).
if intersection :
if intersection == "Overlap" :
return intersections
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) )
) :
res += [intersection]
return res
# returns a list containning [t1,t2,t3,...,tn], 0<=ti<=1...
tolerance = .0000000001
res = []
for k in range(sample_points) :
i, F = 0, 1e100
try : # some numerical calculation could exceed the limits
t2 = t*t
#slopes...
F1 = (
)
t -= F/F1
except:
break
i += 1
for i in res :
if abs(t-i)<=0.001 :
break
if not abs(t-i)<=0.001 :
return res
tolerance = .0001
F = 0.
i = 0
t = .5
if d != 0 :
Flast = F
F1 = (
)
i+=1
if F1!=0:
t -= F/F1
else:
break
else: break
return t
#curvature = (x'y''-y'x'') / (x'^2+y'^2)^1.5
if d != 0 :
else :
# Use the Lapitals rule to solve 0/0 problem for 2 times...
if depth>0 :
# little hack ;^) hope it wont influence anything...
return 1e100
if c == 0 : return 1e100
else: return 1/c
# special points = curvature == 0
res = []
for i in roots :
i = i.real
return res
# Remove all zerro length segments
s = 0
#subpath = subpath[:]
for p in sp1 :
pl = p
return s<0
return [x,y]
total = 0
lengths = []
total += l
seg += [ l ]
if l>0 :
return seg,l
# rebuild_csp() adds to csp control points making it's segments looks like segs
if s==None : s, l = csp_segments(csp)
d = None
del segs[d[1]]
if segs[i]<s[j] : break
if s[j]-s[j-1] != 0 :
s = s[:j] + [ s[j-1]*(1-t)+s[j]*t ] + s[j:]
return csp, s
if aa:
coef2=1
else:
coef1=1
roots = cubic_solver(a,b,c,d)
retval = []
for i in roots :
i = i.real
return retval
elif t1 <= 1e-10:
else:
# points is float=t or list [t1, t2, ..., tn]
last_t = 0
for t in points:
if 1e-10<t<1.-1e-10 :
last_t = t
return res
# points are [[i,t]...] where i-segment's number
parts = []
continue
continue
continue
else :
else :
return parts
def arc_from_s_r_n_l(s,r,n,l) :
def arc_from_c_s_l(c,s,l) :
r = point_to_point_d(c,s)
if r == 0 : return []
alpha = l/r
n = [c[0]-s[0],c[1]-s[1]]
return csp_from_arc(s, e, c, r, slope)
# 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
return []
result = []
return result
### Distance calculattion from point to arc
dist = None
p = P(p)
if r>0 :
i = c + (p-c).unit()*r
if a*alpha<0:
return (p-i).mag(), [i.x, i.y]
else :
else :
n, i = 10, 0
i += 1
for j in range(n+1):
t = float(j)/n
n=n*2
return d1[0]
x,y = p
c = 0
#CLT added test of x in range
xmin=1e100
xmax=-1e100
for i in range(4):
c +=1
return 0.
min_dist = 1e100
return min_dist
if x==0 : # Lines are parallel
else:
else: return False
else :
return (
if x==0 : # Lines are parallel
else:
res = []
return res
else: return []
else :
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):
# p1 - point, p2,p3 - line segment
#draw_pointer(p1)
if c1 <= 0 :
return min(
min_dist = 1e100
max_dist = 0.
for i in range(4) :
min_dist = 0.
break
for i in range(4) :
for j in range(4) :
n = []
for j in csp[i] :
n = [ [j[2][:],j[1][:],j[0][:]] ] + n
csp[i] = n[:]
return csp
if t == 0 :
else :
elif t == 1 :
else :
else :
return [1.,0.]
def csp_concat_subpaths(*s):
else :
if len(s) == 0 : return []
result = s[0]
for s1 in s[1:]:
return result
result = []
s = csp[i]
intersections = []
for s in splitted_s[:] :
for p in csp_true_bounds([s]) :
break
if clip :
splitted_s.remove(s)
result += splitted_s
return result
# Appends subpath with line or polyline.
if not prepend :
for p in points :
subpath += [ [p[:],p[:],p[:]] ]
else:
else :
for p in points :
else:
return subpath
joined_result = []
while done_smf :
del(result[-1])
j = 0
while j<len(joined_result) :
break
break
j += 1
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])
abc = triangle_cross(a,b,c)
abd = triangle_cross(a,b,d)
bcd = triangle_cross(b,c,d)
cad = triangle_cross(c,a,d)
raise ValueError, "csp_segment_convex_hull happend something that shouldnot happen!"
################################################################################
### Bezier additional functions
################################################################################
return [
]
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]) )
# returns [d^2,t]
################################################################################
### Some vector functions
################################################################################
def normalize((x,y)) :
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):
################################################################################
### 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 :
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]
)
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 ]
]
if det==0: return None
return [
]
def small(a) :
global small_tolerance
return abs(a)<small_tolerance
else :
value = None
for k in node :
return value
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;"
'y': str(y),
'style' : style
}
if gcodetools_tag!=None :
if group == None:
for s in text :
{
'x': str(x),
'y': str(y),
})
y += font_size
def draw_csp(csp, stroke = "#f00", fill = "none", comment = "", width = 0.354, group = None, style = None, gcodetools_tag = None) :
if style == None :
'style' : style
}
if comment != '':
if group == None :
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) :
if pointer_type == None:
pointer_type = "Pointer"
if group == None:
if text != None :
group = inkex.etree.SubElement( group, inkex.addNS('g','svg'), {"gcodetools": pointer_type+" group"} )
if figure == "line" :
s = ""
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)})
elif figure == "arrow" :
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)})
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)})
def straight_segments_intersection(a,b, true_intersection = True) : # (True intersection means check ta and tb are in [0,1])
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 :
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 = []
else :
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
if n>=0 :
else :
elif b!=0:
if det>0 :
elif d == 0 :
return [-c/(b*b)]
else :
elif c!=0 :
return [-d/c]
else : return []
################################################################################
### print_ prints any arguments into specified log file
################################################################################
for s in arg :
f.write( s )
f.write("\n")
f.close()
################################################################################
### Point (x,y) operations
################################################################################
class P:
if not y==None:
else:
if isinstance(other, P):
if h: return self / h
else: return P(0,0)
class Arc():
self.c = P(c)
if self.a>0 :
r += self.r
else :
r = self.r - r
if self.r != 0 :
self.r = r
a = self.a * reverse_angle
r = (st-c)
r = r.mag()
if a<0:
else:
attr = {
'style': style,
"gcodetools": "Preview",
}
if transform != [] :
return []
class Line():
if self.l != 0 :
else:
"gcodetools": "Preview",
}
if transform != [] :
if x == 0 :
# lines are parallel
res = []
# lines are the same
if v1.x != 0 :
else :
return res
else :
else : return []
else: return []
class Biarc:
if items == None :
else:
def l(self) :
# offset each element
i = a.intersect(b)
for p in i :
draw_pointer(p.to_list())
pass
global gcodetools
for i in [0,1]:
if group==None:
gcodetools.preview_groups = { layer: inkex.etree.SubElement( gcodetools.layers[min(1,len(gcodetools.layers)-1)], inkex.addNS('g','svg'), {"gcodetools": "Preview group"} ) }
gcodetools.preview_groups[layer] = inkex.etree.SubElement( gcodetools.layers[min(1,len(gcodetools.layers)-1)], inkex.addNS('g','svg'), {"gcodetools": "Preview group"} )
if 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)
else : reverse_angle = 1
num = 0
num += 1
#if num>1 : break
#Crve defenitnion [start point, type = {'arc','line','move','end'}, arc center, arc angle, end point, [zstart, zend]]
################################################################################
###
### Offset function
###
### This function offsets given cubic super path.
### It's based on src/livarot/PathOutline.cpp from Inkscape's source code.
###
###
################################################################################
offset_tolerance = 0.05
print_("Offset radius %s"% r)
result = []
t.sort()
if t[0]>.00000001 : t = [0.]+t
if (c>1/r and r<0 or c<1/r and r>0) :
else : # This part will be clipped for sure... TODO Optimize it...
if result==[] :
else:
else:
if intersection != [] :
else :
pass # ???
#raise ValueError, "Offset curvature clipping error"
#draw_csp([result])
return result
# See Gernot Hoffmann "Bezier Curves" p.34 -> 7.1 Bezier Offset Curves
if intersection != [] :
break
if _break:break
if _break :
else :
return []
if intersection != [] :
# Offsets do not intersect... will add an arc...
if arc == [] :
else:
# Clip prev by arc
if intersection != [] :
#else : raise ValueError, "Offset curvature clipping error"
# Clip next by arc
if next == [] :
if intersection != [] :
#else : raise ValueError, "Offset curvature clipping error"
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],
)
#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)
else :
t = .5
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")
############################################################################
# Some small definitions
############################################################################
############################################################################
# Prepare the path
############################################################################
# Remove all small segments (segment length < 0.001)
# 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.
############################################################################
# Offset
############################################################################
# Create offsets for all segments in the path. And join them together inside each subpath.
subpath_offset = []
last_offset_len = 0
if subpath_offset == [] :
else :
#draw_csp([prev],"Blue")
#draw_csp([arc],"Magenta")
# 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)
#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;"} )
#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
# If subpath_i==subpath_j we are looking for self intersections, so
# we'll need search intersections only for xrange(i,len(subpath1))
# Find self intersections of a segment
summ +=1
for t in intersections :
summ1 += 1
else :
summ +=1
for t in intersections :
summ1 += 1
#TODO tolerance dependence to cpsp_length(t)
#draw_pointer(csp_at_t(subpath[i-1],subpath[i],t[0]),"#f00")
#print_(t)
#print_(i,j)
########################################################################
# Split unclipped offset by intersection points into splitted_offset
########################################################################
splitted_offset = []
subpath = unclipped_offset[i]
# Close parts list to close path (The first and the last parts are joined together)
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])
########################################################################
# Clipping
########################################################################
result = []
if (P(s1[0][1])-P(s2[-1][1])).l2()<0.0001 and ( (subpath_i+1) % len(splitted_offset) != subpath_j ):
break
if (P(s2[0][1])-P(s1[-1][1])).l2()<0.0001 and ( (subpath_j+1) % len(splitted_offset) != subpath_i ):
break
if not clip :
elif options.offset_draw_clippend_path :
(P(csp_at_t(s2[-2],s2[-1],1.))+ P(csp_normalized_normal(s2[-2],s2[-1],1.))*10).to_list(),"Green", "line" )
# Now join all together and check closure and orientation of 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
else :
# Remove small parts
########################################################################
# Now to the Dummy cliping: remove parts from splitted offset if their
# centers are closer to the original path than offset radius.
########################################################################
for s in joined_result[:]:
dist = csp_to_point_distance(original_csp, s[int(len(s)/2)][1], dist_bounds = [r1,r2], tolerance = .000001)
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_()
return joined_result
################################################################################
###
### Biarc function
###
### Calculates biarc approximation of cubic super path segment
### splits segment if needed or approximates it with straight line
###
################################################################################
# Both tangents are zerro - line straight
else:
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
# 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
if v.mag()==0:
elif not asmall:
discr = b*b-4*a*c
return None, None
else :
return R, alpha
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]] ]
if d > options.biarc_tolerance and depth<options.biarc_max_split_depth : return biarc_split(sp1, sp2, z1, z2, depth)
else:
# arc should be straight otherwise it could be threated as full circle
else :
# arc should be straight otherwise it could be threated as full circle
else :
else:
return 0
# get first subcurve and ceck it's length
break
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
while lc<l :
if reverse :
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?
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?
if ls != 0 :
else :
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
return res
class Postprocessor():
}
for s in command:
s = s.strip()
if s!="" :
if not r:
else :
self.error("Unrecognized function '%s' while postprocessing.\n(Command: '%s')"%(function,command), "error")
try :
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")
# remap parameters should be like "x->y,y->x"
for s in parameters:
if not r :
if case_sensitive :
else :
gcode = ""
warned = []
plane = "g17"
# get plane selection:
if r :
# Raise warning if scale factors are not the game for G02 and G03
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")
# Transform
for a in axis[i] :
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 :
try:
except:
pass
gcode += s + "\n"
if flip :
planes = []
feeds = {}
coords = []
gcode = ""
# get Planes
if r :
# get Feeds
if r :
#Coordinates
for c in "xyzijka" :
if r :
if c not in coords :
coords += [c]
# Add offset parametrization
for c in coords:
# Add scale parametrization
gcode += "#10 = 1 (Scale factor)\n"
else :
if "g17" in planes :
if "g18" in planes :
if "g19" in planes :
# Add a scale
if "a" in coords:
gcode += "#12 = 1 (A axis scale)\n"
# Add feed parametrization
for f in feeds :
# Parameterize Gcode
#feed replace :
#Coords XYZA replace
for c in "xyza" :
#Coords IJKR replace
for c in "ijkr" :
gcode += s + "\n"
try:
except :
self.error("Bad parameters for round. Round should be an integer! \n(Parameters: '%s')"%(parameters), "error")
gcode = ""
for a in "xyzijkaf" :
if r :
r"(?i)("+a+r")\s*(-?)\s*(\d*\.?\d*)",
s)
gcode += s + "\n"
try :
self.error("Bad parameters for scale. Scale should not be 0 at any axis! \n(Parameters: '%s')"%(parameters), "error")
except :
try :
except :
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")
################################################################################
### Polygon class
################################################################################
class Polygon:
for p in poly :
return b[2]-b[0]
[
]
]
# Polygon is a list of simple polygons
# Surface is a polygon + line y = 0
# Direction is [dx,dy]
centroids = []
sa = 0
a *= 3.
if abs(a)>0 :
cx /= a
cy /= a
for c in centroids :
# Polygon is a list of simple polygons
# Surface is a polygon + line y = 0
# Down means min y (0,-1)
# Get surface top point
# Get polygon bottom point
# 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
# and vice versa just change the sign because vertex now under the edge
#print_(dist, top, bottom)
#self.draw()
else :
if st[0]>end[0] : st, end = end, st # This will be needed to check that edge if open only at rigth end
#print_(c)
if c<0 :
return True
return inside
# Add vertices at all self intersection points.
hull = []
poly_ = []
poly_ += [s]
# Check self intersections
for p in int_ :
poly_ += [p]
# Check self intersections with other polys
for p in int_ :
poly_ += [p]
# Create the dictionary containing all edges in both directions
edges = {}
if (point_to_point_d2(e,s)<0.000001) : continue
for p in edges :
if point_to_point_d2(p,s)<0.000001 :
s = p
if point_to_point_d2(p,e)<0.000001 :
e = p
l = point_to_point_d(s,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 :
break
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 :
break
edges[s] += [ [s,e, l] ]
#draw_pointer(s+e,"red","line")
else :
edges[s] = [ [s,e,l] ]
#draw_pointer(s+e,"green","line")
# 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
# 0 = 2*pi is the largest angle
return False
return True
# Last edge is normalized vector of the last edge.
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))
#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)
next = p
#print_("min_angle=",min_angle)
return next
# Join edges together into new polygon cutting the vertexes inside new polygon
loops = 0
poly = []
loops+=1
# Find left most vertex.
loops1 = 0
loops1 += 1
#draw_pointer(next[0]+next[1],"Green","line", comment=i, width= 1)
#print_(next[0],"-",next[1])
# Remove all edges that are intersects new poly (any vertex inside new poly)
class Arangement_Genetic:
# gene = [fittness, order, rotation, xposition]
# spieces = [gene]*shapes count
# population = [spieces]
self.population = []
specimen = []
for j in order:
# retun distance, each component is normalized
s = 0
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
# Define similarity as a simple distance between two points in len(gene)*len(spiece) -th dimentions
# for sp2 in top_spieces sum(|sp1-sp2|)/top_count
sim = 0
t = []
for j in range(20) :
t.sort()
#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]]
genes_order = []
# OMG it's a incest :O!!!
# Damn you bastards!
else :
pass
# if random.random()<.01 : print_(self.species_distance2(parent1, parent2))
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 :
#rotation_mutate_param = random.random()/100
#xposition_mutate_param = random.random()/100
specimen[i] = [parent1[i][0], parent1[i][1]*tr+parent2[i][1]*(1-tr),parent1[i][2]*tp+parent2[i][2]*(1-tp)]
j = i
specimen[i] = [parent2[j][0], parent1[i][1]*tr+parent2[i][1]*(1-tr),parent1[i][2]*tp+parent2[i][2]*(1-tp)]
for i in range(random.randint(self.mutation_genes_count[0],self.mutation_genes_count[0]*self.incest_mutation_count_multiplyer )) :
for p in spiece :
return surface
for p in spiece[1:] :
return surface
###
### Fast test function using weave's from scipy inline function
###
try :
converters is None
except :
try:
except:
options.self.error("For this function Scipy is needed. See http://www.cnc-club.ru/gcodetools for details.","error")
# Prepare vars
for subpoly in p :
test_ = []
population_ = []
population_ += sp
f.close()
f.close()
s = ''
code,
['points_','subpoly_','poly_', 'lp_', 'ls_', 'l_', 'lt_','test_', 'population_'],
compiler='gcc',
)
#surface.draw()
################################################################################
###
### Gcodetools class
###
################################################################################
if not no_headers :
f.close()
################################################################################
### TODO move it to the bottom
################################################################################
if not end :
else:
# r is needed only for be compatible with add_arc
if not end :
else:
# r is needed only for be compatible with add_arc
if not end :
else:
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.
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 :
if self.selected_paths == {}:
res = []
# Find closes point to in-out reference point
# If subpath is open skip this step
# split and reverse path for further add in-out points
d = [1e100,1,1,1.]
for p in self.in_out_reference_points :
d = d1[:]
p_ = p
# 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
del subpath[-1]
max_cross = [-1e100, None]
# return back last point
# there's an angle near the point
j = max_cross[1]
if j<0 : j -= 1
if j!=0 :
else :
# have to cut path's segment
d,i,j,t = d
# prepare corners
# find corners and add some nodes
# got a corner to process
res_ += [
]
else:
# finally add let's add in-out paths...
)
else:
################################################################################
### Arrangement: arranges paths by givven params
### TODO move it to the bottom
################################################################################
polygons = []
original_paths = []
#print_("Redused edges count from", sum([len(poly) for poly in polygon.polygon ]) )
original_paths += [path]
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) ) )
# material_width = self.options.arrangement_material_width
# population = Arangement_Genetic(polygons, material_width)
# population.add_random_species(1)
# population.test_population_centroid()
## return
#population.test(population.test_spiece_centroid)
last_champ = -1
champions_count = 0
for i in range(population_count):
"""
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)
"""
else:
print_()
print_()
colors = ["blue"]
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
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 reduse 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="Distanse from original path where engraving is not needed (usualy 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",
}
'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",
]
c = []
if len(p)==0 :
return []
### Sort to reduce Rapid distance
keys = [0]
while len(k)>0:
dist = None
del k[dist[1]]
for k in keys:
subpath = p[k]
# 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]) )
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 :
if style!=None :
else :
for i in [0,1]:
if group==None:
self.preview_groups = { layer: inkex.etree.SubElement( self.layers[min(1,len(self.layers)-1)], inkex.addNS('g','svg'), {"gcodetools": "Preview group"} ) }
self.preview_groups[layer] = inkex.etree.SubElement( self.layers[min(1,len(self.layers)-1)], inkex.addNS('g','svg'), {"gcodetools": "Preview group"} )
if 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)
else : reverse_angle = -1
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':
"gcodetools": "Preview",
}
if transform != [] :
elif s[1] == 'arc':
arcn += 1
sp = s[0]
c = s[2]
if s[3]*a<0:
if a>0:
else:
attr = {
'style': st,
"gcodetools": "Preview",
}
if transform != [] :
s = si
else :
f.close()
else:
f.close()
else:
else:
self.error(_("Directory does not exist! Please specify existing directory at Preferences tab!"),"error")
return False
else:
ext = ""
max_n = 0
for s in dir_list :
if r :
else :
try:
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]]
###
################################################################################
g = ""
def c(c):
if c[5] == 0 : c[5]=None
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
return min(
try :
self.last_used_tool == None
except :
self.last_used_tool = None
print_("working on curve")
g += ( "(Change tool to %s)\n" % re.sub("\"'\(\)\\\\"," ",tool["name"]) ) + tool["tool change gcode"] + "\n"
current_a = 0
# Creating Gcode for curve between s=curve[i-1] and si=curve[i] start at s[0] end at s[4]=si[0]
if s[1] == 'move':
lg = 'G00'
elif s[1] == 'end':
lg = 'G00'
elif s[1] == 'line':
a = calculate_angle(a, current_a)
current_a = a
lg = 'G01'
elif s[1] == 'arc':
r = [(s[2][0]-s[0][0]), (s[2][1]-s[0][1])]
if s[3]<0 : # CW
else: #CCW
current_a = a
else : axis4 = ""
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:
lg = 'G02'
else:
a = calculate_angle(a, current_a)
current_a = a
lg = 'G01'
return g
trans = []
while (g!=root):
if 'transform' in g.keys():
t = g.get('transform')
t = simpletransform.parseTransform(t)
g=g.getparent()
return trans
return trans
else :
return transform
if trans != []:
if not reverse :
else :
return csp
break
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")
else :
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 += [ [ [(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]] ] ]
# 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])]
])
[[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()
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.Zauto_scale[layer] = math.sqrt( (self.transform_matrix[layer][0][0]**2 + self.transform_matrix[layer][1][1]**2)/2 )
### Zautoscale is absolete
if not reverse :
else :
return [t[0][0]*x+t[0][1]*y+t[0][2], t[1][0]*x+t[1][1]*y+t[1][2]]
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_)) ]
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 adn errors could be assigned to space or comma or dot
### sepparated strings (case is ignoreg).
################################################################################
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)
print_(s)
print_(s)
print_(s)
else :
print_(s)
################################################################################
### Set markers
################################################################################
# Add marker to defs if it doesnot exists
marker = inkex.etree.SubElement( defs, inkex.addNS("marker","svg"), {"id":"CheckToolsAndOPMarker","orient":"auto","refX":"-4","refY":"-1.687441","style":"overflow:visible"})
{ "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;" }
)
marker = inkex.etree.SubElement( defs, inkex.addNS("marker","svg"), {"id":"DrawCurveMarker","orient":"auto","refX":"-4","refY":"-1.687441","style":"overflow:visible"})
{ "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;" }
)
marker = inkex.etree.SubElement( defs, inkex.addNS("marker","svg"), {"id":"DrawCurveMarker_r","orient":"auto","refX":"4","refY":"-1.687441","style":"overflow:visible"})
{ "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;" }
)
marker = inkex.etree.SubElement( defs, inkex.addNS("marker","svg"), {"id":"InOutPathMarker","orient":"auto","refX":"-4","refY":"-1.687441","style":"overflow:visible"})
{ "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 recursive(g) :
for i in g:
for j in i:
recursive(i)
################################################################################
###
### Get Gcodetools info from the svg
###
################################################################################
self.selected_paths = {}
self.orientation_points = {}
self.Zcoordinates = {}
self.transform_matrix = {}
self.Zauto_scale = {}
items = g.getchildren()
for i in items:
if selected:
else :
recursive_search(i,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" :
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")
if "gcodetools" not in i.keys() :
self.selected_paths[layer] = self.selected_paths[layer] + [i] if layer in self.selected_paths else [i]
items_ = i.getchildren()
for j in items_ :
self.in_out_reference_points.append( self.apply_transforms(j,cubicsuperpath.parsePath(j.get("d")))[0][0][1] )
# 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")
self.error(_("Document has no layers! Add at least one layer using layers panel (Ctrl+Shift+L)"),"Error")
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" )
else :
else :
items = g.getchildren()
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 p==None : return None
points = []
for i in p :
point = [[],[]]
for node in i :
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))
else : return None
point = [[], '']
for node in g :
else : return []
tool["self_group"] = g
for i in g:
# Get parameters
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":
if j.get("gcodetools") == "Gcodetools tool definition field value" or j.get("gcodetools") == "Gcodetools tool defention field value":
#print_("Found tool parameter '%s':'%s'" % (key,value))
try :
except :
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 :
self.error(_("Warning! Tool has parameter that default tool has not ( '%s': '%s' ).") % (key, value), "tools_warning" )
return tool
# print_(("index(layer)=",self.layers.index(layer),"set_tool():layer=",layer,"self.tools=",self.tools))
# for l in self.layers:
# print_(("l=",l))
# print_(("processing layer",i))
break
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")
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
###
################################################################################
out=[[],[],[],[]]
for p in points:
if minx==p[0]:
out[0]+=[p]
minx=p[0]
out[0]=[p]
if miny==p[1]:
out[1]+=[p]
miny=p[1]
out[1]=[p]
if maxx==p[0]:
out[2]+=[p]
maxx=p[0]
out[2]=[p]
if maxy==p[1]:
out[3]+=[p]
maxy=p[1]
out[3]=[p]
return out
i=0
out=[]
for p in points:
if p!=[None,None]: out+=[p]
i+=1
return(out)
l=0
return l
# 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:
cw=[]
# print_(("tpoints=",tpoints))
# print_(p)
cw+=p
return minimal_way
keys = [0]
del lines[0]
del lines[i]
return keys
lines = []
return sort_lines(lines)
gcode=""
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 = {}
for i in node.getchildren():
return res
else :
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') )
colors = {}
# transform simple path to get all var about orientation
curves = []
dxfpoints = []
try :
except:
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
else:
return "None"
comment = re.sub("\[([A-Za-z_\-\:]+)\]", partial(set_comment, path=path), self.options.comment_gcode)
else:
comment = ""
colors[id_] = simplestyle.parseColor(style['stroke'] if "stroke" in style and style['stroke']!='none' else "#000")
print_("got dxfpoint (scaled) at (%f,%f)" % (x,y))
dxfpoints += [[x,y]]
else:
curves += [
[
]
]
# for c in curves :
# print_(c)
curves_ = []
else :
else: # pass by pass
curves_ = []
gcode += "\n(Pass at depth %s)\n"%z
else :
################################################################################
###
### dxfpoints
###
################################################################################
if self.selected_paths == {}:
self.error(_("Nothing is selected. Please select something to convert to drill point (dxfpoint) or clear point sign."),"warning")
# print_(("processing path",path.get('d')))
# print_("trying to set as dxfpoint")
if r!=None:
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))
# for id, node in self.selected.iteritems():
# print_((id,node,node.attrib))
################################################################################
###
### Artefacts
###
################################################################################
else :
# paths[layer].reverse() # Reverse list of paths to leave their order
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
remove = []
if (bounds[2]-bounds[0])**2+(bounds[3]-bounds[1])**2 < self.options.area_find_artefacts_diameter**2:
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]) )
{
'gcodetools': 'area artefact arrow',
})
inkex.etree.SubElement(parent, inkex.addNS('path','svg'), {'d': cubicsuperpath.formatPath(csp[i]), 'style': styles["area artefact"]})
for i in remove :
del csp[i]
else :
return
################################################################################
###
### Calculate area curves
###
################################################################################
return
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")
print_(d)
if d==None:
print_("omitting non-path")
continue
# Path is not offset. Preparation will be needed.
# Finding top most point in path (min y value)
# Reverse path if needed.
# Move outline subpath to the begining of csp
j = min_j
# Split by the topmost point and join again
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:
subp = [ [ sp2[1], sp2[1],sp2[2] ] ] + [sp3] + subp[j+1:] + subp[:j-1] + [sp1] + [[ sp2[0], sp2[1],sp2[1] ]]
# reverse path if needed
n = []
for j in csp[i]:
n = [ [j[2][:],j[1][:],j[0][:]] ] + n
csp[i] = n[:]
print_(("original d=",d))
print_(("formatted d=",d))
# scale = sqrt(Xscale**2 + Yscale**2) / sqrt(1**2 + 1**2)
# avoiding infinite loops
radius = -r
{
})
if radius == -r : break
################################################################################
###
### Polyline to biarc
###
### Converts Polyline to Biarc
################################################################################
# Both tangents are zerro - line straight
else:
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
# 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
if v.mag()==0:
elif not asmall:
discr = b*b-4*a*c
return None, None
else :
return R, alpha
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]] ]
if d > options.biarc_tolerance and depth<options.biarc_max_split_depth : return biarc_split(sp1, sp2, z1, z2, depth)
else:
# arc should be straight otherwise it could be threated as full circle
else :
# arc should be straight otherwise it could be threated as full circle
else :
if d==None:
print_("omitting non-path")
continue
# lets pretend that csp is a polyline
# lets create biarcs
# lets split polyline into different smooth parths.
# normalize p1p2 and p2p3 to get angle
#it's an angle
else:
################################################################################
###
### Area fill
###
### Fills area with lines
################################################################################
# convert degrees into rad
return
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")
lines = []
if d==None:
print_("omitting non-path")
continue
#maxx = max([x,y,i,j,root],maxx)
# rotate the path to get bounds in defined direction.
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 ]
# 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):
# Zig-zag
if r<=0 :
return
lines += [ [] ]
while (i<b[2] or last_one) :
if lines[-1] == [] :
if top :
else :
i += r
else :
lines[-1] += [ [x,y] ]
stage = 0
while w>0 and h>0 :
if stage == 0 :
y -= h
h -= r
elif stage == 1:
x += w
if not start:
w -= r
elif stage == 2 :
y += h
h -= r
elif stage == 3:
x -= w
w -=r
lines[-1] += [ [x,y] ]
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
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
intersections = {}
ints = []
for t in roots :
else :
if p in intersections :
intersections[p] += [ [i,j,t] ]
else :
intersections[p] = [ [i,j,t] ]
#p = self.transform(p,layer,True)
#draw_pointer(p)
for i in ints:
i = 0
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.
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
# now let's try connect splitted lines
#while len(splitted_line)>0 :
#TODO
# and apply back transrormations to draw them
# 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()
################################################################################
#global x1,y1,rx,ry
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
"""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
#We can get correct sign of the sin, assuming cos is positive
else :
#We can correct signs by noting that the dot product
# of bisector and either normal must be >0
#end bisect
"""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
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
#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
#radius and centre are:
#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:
return max_dist
return max_dist
#end of get_radius_to_line
"""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
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
#if x2,y2 not in front of the normal...
#print_("Straight",x1,y1,nx,ny,x2,y2)
#It is a corner bisector, so..
if discriminator < 0 :
return max_dist #this part irrelevant
#print_("Corner",x1,y1,nx,ny,x1+x2,y1+y2,discriminator,r)
#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.
"""
#LT This is the only limit we get from the user currently
return [[a,b,c,d]]
#end of bez_divide
"""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
"""
jjmin = -1
iimin = -1
r = max_dist
# set limits within which to look for lines
if jj==j : #except this one
#print_("Try pt i,ii,t1,x1,y1",i,ii,t1,x1,y1)
else: #doing a line
if jj==j : #except this one
#see if line in range
#print_("Try line i,ii,t1,x1,y1",i,ii,t1,x1,y1)
if 0<=t1<r :
r = t1
#next ii
#next jj
#end of get_biggest
"""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
"""
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()
"""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.
"""
#get the xy distance of point 1 from the line 0-2
#print_("pop",j,i,xy1)
wl+=[w]
#end of save_point
"""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
{"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:
{"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
# convert to 2 hex digits as a shade of red
"gcodetools": "Gcode G1R"
})
"gcodetools": "Gcode G1L"
})
#end of draw_point
#end of subfunction definitions. engraving() starts here:
gcode = ''
cspe =[]
we = []
return
#Find what units the user uses
unit=" mm"
unit=" inches"
#LT See if we can use this parameter for line and Bezier subdivision:
#Calculate scale in pixels per user unit (mm or inch)
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
#max dist from path to engrave in user's units
engraving_group = inkex.etree.SubElement( self.selected_paths[layer][0].getparent(), inkex.addNS('g','svg') )
self.my3Dlayer=inkex.etree.SubElement(self.document.getroot(), 'g') #Create a generic element at root level
#Create groups for left and right eyes
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"})
#LT: Create my own list. n1LT[j] is for subpath j
nlLT = []
# Remove zero length segments, assume closed path
i = 0 #LT was from i=1
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:
del cspi[j][i]
else:
i += 1
#Create copies in 3D layer
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
#n = []
#Copy it to 3D layer objects
spl=[]
spr=[]
#LT find angle between this and previous segment
#I don't trust this function, so test result
#record x,y,normal,ifCorner, sin(angle-turned/2)
#LT now do the line
else : #Bezier. First, recursively cut it up:
if l1<engraving_tolerance :
continue
if l1<engraving_tolerance :
continue
#bisect
#LT for each segment - ends here.
#Copy complete paths to 3D layer
#print_("cspl",cspl)
'style': "stroke:#808080; stroke-opacity:1; stroke-width:0.6; fill:none",
"gcodetools": "G1L outline"
})
'style': "stroke:#808080; stroke-opacity:1; stroke-width:0.6; fill:none",
"gcodetools": "G1L outline"
})
{ "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"
})
{ "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
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
#LT Note: Python enables wrapping of array indices
# backwards to -1, -2, but not forwards. Hence:
#if n1[2] == True and n1[3]==0 : # A straight angle
#continue
bits=1
bit0=0
#lastr=r #Remember r from last line
lastw=w #Remember w from last line
w = max_dist
else:
#set initial r value, not to be exceeded
else: #line. Cut it up if long.
else : bit0=0.0
#split excess evenly at both ends
#print_("j,i,r,bit0,bits",j,i,w,bit0,bits)
#w = min(r, toolr)
if reflex : #just after a reflex corner
if w<lastw : #need to adjust it
if w>lastw :
elif b>0 and n2[3]>0 and not self.options.engraving_draw_calculation_paths : #acute corner coming up
#LT end of for each bit of this line
lastw = w #remember this w
#LT next i
#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.
"gcodetools": "Engraving calculation paths",
})
{"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'})
for w in wl :
wluu+=[ w / orientation_scale ]
#LT previously, we was in pixels so gave wrong depth
#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!=[]:
#LT3 for layers loop ends here
if gcode!='' :
################################################################################
###
### Orientation
###
################################################################################
if layer == None :
if transform != [] :
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
if transform != [] :
{
'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")
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")
self.error(_("Active layer already has orientation points! Remove them or select another layer!"),"active_layer_already_has_orientation_points")
if transform != [] :
doc_height = 1052.3622047
orientation_scale = 3.5433070660
orientation_scale = 90
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})
{
'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' % (si[0], -si[1]+doc_height),
'gcodetools': "Gcodetools orientation point arrow"
})
draw_text("(%s; %s; %s)" % (i[0],i[1],i[2]), (si[0]+10), (-si[1]-10+doc_height), group = g, gcodetools_tag = "Gcodetools orientation point text")
################################################################################
###
### Tools library
###
################################################################################
# Add a tool to the drawing
if layer == None :
self.error(_("Active layer already has a tool! Remove it or select another layer!"),"active_layer_already_has_tool")
tool = {
"name": "Cylindrical cutter",
"id": "Cylindrical cutter 0001",
"diameter":10,
"penetration angle":90,
"feed":"400",
"penetration feed":"100",
"depth step":"1",
"tool change gcode":" "
}
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":" "
}
tool = {
"name": "Cone cutter",
"id": "Cone cutter 0001",
"diameter":10,
"shape":"w",
"feed":"400",
"penetration feed":"100",
"depth step":"1",
"tool change gcode":" "
}
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":" "
}
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",
}
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 :
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"})
{'style': "fill:#%s;fill-opacity:0.5;stroke:#444444; stroke-width:1px;"%colors[tool_num%len(colors)], "gcodetools":"Gcodetools tool background"})
y = 0
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)
draw_text(param, 150, y, group = g, gcodetools_tag = "Gcodetools tool definition field value", font_size = 10 if key!='name' else 20)
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
###
################################################################################
self.error(_("Selection is empty! Will compute whole drawing."),"selection_is_empty_will_comupe_drawing")
else :
# 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') )
tools_bounds = {}
tools_bounds[layer] = tools_bounds[layer] if layer in tools_bounds else [float("inf"),float("-inf")]
style = "fill:%s; fill-opacity:%s; stroke:#000044; stroke-width:1; marker-mid:url(#CheckToolsAndOPMarker);" % (
trans = simpletransform.composeTransform( trans_, trans if trans != [] else [[1.,0.,0.],[0.,1.,0.]])
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])]
if layer in tools_bounds :
################################################################################
### TODO Launch browser on help tab
################################################################################
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
################################################################################
#draw_csp(self.transform_csp([subpath],layer,True), color = "Orange", width = .1)
c[i-1][4] = c[i][0][:]
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':
elif s[1] == 'arc':
r = [(s[2][0]-s[0][0]), (s[2][1]-s[0][1])]
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:
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
if x not in ["X", "Y", "Z", "x", "y", "z"] or z not in ["X", "Y", "Z", "x", "y", "z"] :
return
return
gcode = ""
self.tool["passing feed"] = float(self.tool["passing feed"] if "passing feed" in self.tool else 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"
# Offset the path if fine cut is defined.
else :
# Close the path to make offset correct
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] ] ])
offsetted_subpath = csp_offset([offsetted_subpath], r if not csp_subpath_ccw(offsetted_subpath) else -r )
#draw_csp(self.transform_csp(offsetted_subpath,layer,True), color = "Green", width = 1)
# Join offsetted_subpath together
# Hope there wont be any cicles
# Create solid object from path and lathe_width
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 %s %f F %f \n" % (x, top_start[0], z, top_start[1], self.tool["passing feed"]) )
intersections = []
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)]
if y > current_width+step :
else :
# full step cut
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
if width == 0 :
else :
else :
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] ] ])
offsetted_subpath = csp_offset([offsetted_subpath], width if not csp_subpath_ccw(offsetted_subpath) else -width )
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 += ("G01 %s %f %s %f F %f \n" % (x, top_start[0], z, top_start[1], self.tool["passing feed"]) )
################################################################################
###
### Lathe modify path
### Modifies path to fit current cutter. As for now straight rect cutter.
###
################################################################################
else :
#self.set_tool(layer)
new_csp = []
last_n = None
last_o = 0
new_subpath = []
# Split segment at x' and y' == 0
new_subpath = []
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)] ]
[
]
)
# Add last horisontal straigth line if needed
new_csp += [new_subpath]
#
# 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.
################################################################################
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") :
if r :
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 :
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
################################################################################
# Get reference points.
gcode = ''
pos = []
pos += [c]
def graffiti_preview_transform(x,y):
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]
# Emit = dots per second
x,y = graffiti_preview_transform(x,y)
# Get closest possible centers of arcs, also we define that arcs are both ccw or both not.
(
)
(
)
)
if dc == 0 :
# can be joined by one arc
#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))
else :
gcode = """(Header)
(Generated by gcodetools from Inkscape.)
(Using graffiti extension.)
(Header end.)"""
# Get all reference points and path's bounds to make preview
# Set reference points
reference_points = None
break
if reference_points == None :
# Transform reference points
self.graffiti_reference_points[layer][i][0] = self.transform(self.graffiti_reference_points[layer][i][0], 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])
minx,miny,maxx,maxy = min(minx,bounds[0]), min(miny,bounds[1]), max(maxx,bounds[2]), max(maxy,bounds[3])
self.graffiti_preview = list([ [255]*(4*self.options.graffiti_preview_size) for i in range(self.options.graffiti_preview_size)])
if r :
else :
# 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 = []
# Rebuild the paths to polyline.
polylines = []
del subpaths[i]
polylines += [
['connector', create_connector(
last_sp2[1],
)]
]
polyline = []
spl = None
# remove zerro length segments
i = 0
del subpath[i+1]
else :
i += 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.
polylines += [
['connector', create_connector(
sp1[1],
sp1[1],
)]
]
polyline = []
# max_segment_length
# 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.
polyline = []
l = 0
if l>0.00000001 :
if l>0.00000001 :
print_(i)
t = 0
last_state = None
# Draw linearization
t += 1
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.
if l!=0:
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)
try :
# Draw reference points
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.close()
except :
################################################################################
###
### Effect
###
### Main function of Gcodetools class
###
################################################################################
global options
# define print_ function
global print_
try :
f.write("Gcodetools log file.\nStarted at %s.\n%s\n" % (time.strftime("%d.%m.%Y %H:%M:%S"),options.log_filename))
f.close()
except :
print_ = lambda *x : None
else : print_ = lambda *x : None
return
return
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.
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.error(_("Cutting tool has not been defined! A default tool has been automatically added."),"warning")
else :
csp1, csp2 = cubicsuperpath.parsePath(self.selected_paths[layer][0].get("d")), cubicsuperpath.parsePath(self.selected_paths[layer][1].get("d"))
+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
offsets_count = 0
offsets_count += 1
if offset_ != [] :
#print_(offset_)
else :
break
print_()
print_("-----------------------------------------------------------------------------------")
print_("-----------------------------------------------------------------------------------")
print_("-----------------------------------------------------------------------------------")
print_()
print_("------------------------------------------")
#