Skip to content

Commit aecab18

Browse files
authored
Add files via upload
1 parent b295093 commit aecab18

File tree

2 files changed

+189
-0
lines changed

2 files changed

+189
-0
lines changed

Blender Laser NC Export.blend

106 KB
Binary file not shown.

Blender to NC.py

+189
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import bpy
2+
from bpy import context as C
3+
from pathlib import Path
4+
from math import sqrt
5+
6+
7+
JOGSPD = 1200
8+
LSRSPD = 700
9+
LSRPOW = 255
10+
11+
VERBOSE = False
12+
13+
ob = C.active_object
14+
obname = ob.name
15+
bpy.ops.object.duplicate()
16+
bpy.ops.object.convert()
17+
dta = C.active_object.data
18+
bpy.ops.object.delete()
19+
C.view_layer.objects.active = ob
20+
ob.select_set(True)
21+
22+
savefile = f'//{obname}_bl.nc'
23+
24+
# store the edges as vert index pair tuples
25+
edges = [(e.vertices[0], e.vertices[1]) for e in dta.edges]
26+
# store the vertices as x,y tuples
27+
verts = [(v.co[0], v.co[1]) for v in dta.vertices]
28+
29+
# build an array for finding any "corner" verticies.
30+
# These are verticies which have anything other than 2 edges connected to them.
31+
32+
vertedgecounts = {}
33+
def vertfound(i, d):
34+
if i in d: d[i] += 1
35+
else: d[i] = 1
36+
vertconnections = {}
37+
def vertedge(i, c, d):
38+
if i in d: d[i].append(c)
39+
else: d[i] = [c]
40+
for e in edges:
41+
for id in (0,1): vertfound(e[id], vertedgecounts)
42+
for id in (0,1):
43+
connec = e[abs(id-1)]
44+
vertedge(e[id], connec, vertconnections)
45+
46+
cornerverts = set()
47+
for vt in vertedgecounts:
48+
ct = vertedgecounts[vt]
49+
if ct != 2: cornerverts.add(vt)
50+
51+
# build segments
52+
segments = []
53+
for corner in cornerverts:
54+
edgecounts = vertedgecounts[corner]
55+
while (corner in vertconnections) and (len(vertconnections[corner]) > 0):
56+
newsegment = [corner]
57+
thisvert = corner
58+
while True:
59+
nextvert = vertconnections[thisvert].pop()
60+
if len(vertconnections[thisvert]) == 0:
61+
del(vertconnections[thisvert])
62+
vertconnections[nextvert].remove(thisvert)
63+
newsegment.append(nextvert)
64+
thisvert = nextvert
65+
if thisvert in cornerverts:
66+
if len(vertconnections[thisvert]) == 0:
67+
del(vertconnections[thisvert])
68+
break
69+
if len(vertconnections[thisvert]) == 0:
70+
print("corner counting went wrong")
71+
raise
72+
segments.append(newsegment)
73+
74+
if VERBOSE: print(obname)
75+
#print(edges)
76+
if VERBOSE: print(len(edges))
77+
#print(verts)
78+
if VERBOSE: print(len(verts))
79+
#print(cornerverts)
80+
#print(vertconnections)
81+
#print(segments)
82+
83+
loops = []
84+
# all of the verts should have exactly 2 connections now
85+
while len(vertconnections):
86+
loopvert, connections = vertconnections.popitem()
87+
vertconnections[loopvert] = connections
88+
newloop = [loopvert]
89+
thisvert = loopvert
90+
while True:
91+
# print(thisvert)
92+
nextvert = vertconnections[thisvert].pop()
93+
del(vertconnections[thisvert])
94+
if nextvert == loopvert:
95+
break
96+
vertconnections[nextvert].remove(thisvert)
97+
newloop.append(nextvert)
98+
thisvert = nextvert
99+
loops.append(newloop)
100+
101+
#print(vertconnections)
102+
#print(loops)
103+
104+
# generate the point search list
105+
pointstosearch = []
106+
def grabpointdata(pid, group = 0):
107+
pointdata = {}
108+
pointdata['P'] = pid
109+
coords = verts[pid]
110+
pointdata['X'] = coords[0]
111+
pointdata['Y'] = coords[1]
112+
if group: pointdata['G'] = group
113+
return pointdata
114+
for seg in segments:
115+
for i in (0,-1): pointstosearch.append(grabpointdata(seg[i], seg))
116+
for loop in loops:
117+
for i in range(len(loop)//2): pointstosearch.append(grabpointdata(loop[i*2], loop))
118+
119+
# Generate the header, and initialize the machine
120+
OutString = "(Gcode generated by Tryop Blender Exporter)\n"
121+
# G20 is inches, G21 is mm
122+
OutString += "G21\n"
123+
# G90 is absolute, G91 is incremental
124+
OutString += "G90\n"
125+
# M05 is spindle off
126+
# M03 is spindle on, s is the spindle speed (or laser power in this case), from 0 to 255
127+
OutString += "M03 S0\n"
128+
# G00 is rapid move
129+
OutString += f"G00 X0 Y0 F{JOGSPD}\n"
130+
131+
def dist(p1,p2):
132+
return sqrt((p1["X"]-p2["X"])**2 + (p1["Y"]-p2["Y"])**2)
133+
134+
X = 0.
135+
Y = 0.
136+
curpos = {}
137+
curpos['X'] = X
138+
curpos['Y'] = Y
139+
140+
def close(p1): return dist(p1, curpos)
141+
142+
while len(pointstosearch):
143+
pointstosearch.sort(key=close)
144+
# if VERBOSE: print(pointstosearch)
145+
curpos = pointstosearch[0]
146+
pidx = curpos['P']
147+
grp = curpos['G']
148+
to_remove = []
149+
for pnt in pointstosearch:
150+
if pnt['G'] == grp: to_remove.append(pnt)
151+
for pnt in to_remove:
152+
pointstosearch.remove(pnt)
153+
# re-order the group
154+
if grp in loops:
155+
grpidx = grp.index(pidx)
156+
grp = grp[grpidx:] + grp[:grpidx]
157+
grp.append(grp[0])
158+
else:
159+
if grp[-1] == pidx:
160+
grp.reverse()
161+
if VERBOSE: print(grp)
162+
X = curpos['X']
163+
Y = curpos['Y']
164+
# for each segment or loop, jog to the start, turn on the laser, complete the path, and turn off again.
165+
OutString += f"G00 X{X:.2f} Y{Y:.2f} F{JOGSPD}\n"
166+
# M03 is spindle on, s is the spindle speed (or laser power in this case), from 0 to 255
167+
OutString += f"M03 S{LSRPOW}\n"
168+
for curidx in grp[1:]:
169+
curpos = grabpointdata(curidx)
170+
X = curpos['X']
171+
Y = curpos['Y']
172+
# G01 is linear motion
173+
OutString += f"G01 X{X:.2f} Y{Y:.2f} F{LSRSPD}\n"
174+
# when done, turn the laser back off
175+
OutString += "M03 S0\n"
176+
177+
# when all the engraving is done, turn the laser off and jog back to the origin
178+
OutString += "M05\n"
179+
OutString += f"G00 X0 Y0 F{JOGSPD}\n"
180+
181+
182+
fp = Path(bpy.path.abspath(savefile))
183+
try:
184+
f = open(fp, 'w')
185+
f.write(OutString)
186+
f.close()
187+
if VERBOSE: print('File Saved')
188+
except:
189+
if VERBOSE: print('exception found while saving file')

0 commit comments

Comments
 (0)