gp2ngc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. #!/usr/bin/python
  2. from __future__ import print_function
  3. import os, sys, re, getopt, numpy as np, math, json
  4. from signal import signal, SIGPIPE, SIG_DFL
  5. signal(SIGPIPE,SIG_DFL)
  6. VERSION = "0.1.0"
  7. ctx = {
  8. "units" : "mm", "epsilon" : 1.0/1024.0,
  9. "sort": False,
  10. "explicit_speed": False,
  11. "premul" : 1.0, "premul_x" : 1.0, "premul_y" : 1.0, "premul_z" : 1.0,
  12. "header" : "", "footer" : "",
  13. "g0speed" : "", "g1speed" : "",
  14. "z_active": False,
  15. "z_step" : 7.0, "z_height" : 5.0, "z_plunge" : -21.0, "z_0" : 0.0, "z_slow" : "", "z_rapid" : "",
  16. "tab_n" : 0, "tab_offset" : 0.0, "tab_length" : 50.0, "tab_height" : 5.0, "tab_slide_factor" : 1/8.0,
  17. "tab_default_n" : 4,
  18. "close_polygon": True
  19. }
  20. ctx_laser = {
  21. "units" : "mm", "epsilon" : 1.0/1024.0,
  22. "sort": False,
  23. "explicit_speed": False,
  24. "premul" : 1.0, "premul_x" : 1.0, "premul_y" : 1.0, "premul_z" : 1.0,
  25. "header" : "", "footer" : "",
  26. "g0speed" : "F5000", "g1speed" : "F3000",
  27. "z_active": False,
  28. "z_step" : 0.0, "z_height" : 0.0, "z_plunge" : 0.0, "z_0" : 0.0, "z_slow" : 0.0, "z_rapid" : 0.0,
  29. "tab_n" : 0, "tab_offset" : 0.0, "tab_length" : 2.5, "tab_height" : 0.0, "tab_slide_factor" : 1/8.0,
  30. "tab_default_n" : 4,
  31. "close_polygon": True
  32. }
  33. ctx_maslow = {
  34. "units" : "mm", "epsilon" : 1.0/(1024.0*1024.0),
  35. "sort": False,
  36. "explicit_speed": False,
  37. "premul" : 1.0, "premul_x" : 1.0, "premul_y" : 1.0, "premul_z" : 1.0,
  38. "header" : "", "footer" : "",
  39. #"g0speed" : 800.0, "g1speed" : 800.0,
  40. "g0speed" : "", "g1speed" : "",
  41. "z_active": True,
  42. "z_step" : 7.0, "z_height" : 5.0, "z_plunge" : -21.0, "z_0" : 0.0, "z_slow" : "", "z_rapid" : "",
  43. #"tab_n" : 3, "tab_offset" : 0.0, "tab_length" : 50.0, "tab_height" : 3.0, "tab_slide_factor" : 1/8.0,
  44. "tab_n" : 3, "tab_offset" : 0.0, "tab_length" : 50.0, "tab_height" : 10.0, "tab_slide_factor" : 1/8.0,
  45. "tab_default_n" : 3,
  46. "close_polygon": True
  47. }
  48. def usage():
  49. print("version:", VERSION)
  50. print("usage:")
  51. print("")
  52. print(" gp2ngc [options] <in-gnuplot> <out-ngc>")
  53. print("")
  54. print(" [--preset preset] use preset tool context (options are 'maslow', 'laser')")
  55. print(" [-i infile] input file ('-' for stdin, default)")
  56. print(" [-o outfile] output file ('-' for stdout, default)")
  57. print(" [--header str] string to print before any processing")
  58. print(" [--footer str] string to print after any processing")
  59. print(" [--rapid str] string to append at end of rapid motion (G0)")
  60. print(" [--slow str] string to append at end of slow motion (G1)")
  61. print(" [--z-rapid str] z rapid (default to rapid)")
  62. print(" [--z-slow str] z rapid (default to rapid)")
  63. print(" [--z-step dz] z step down per pass")
  64. print(" [--z-raise Z] z height to raise z to for rapid motion")
  65. print(" [--z-plunge z] z final plunge depth")
  66. print(" [--tab-n n] insert n tabs per contour")
  67. print(" [--tab-length s] tab length")
  68. print(" [--tab-height h] tab height")
  69. print(" [--show-context] show context information")
  70. print(" [--close-polygon] connect first and last point in polygon point list (default)")
  71. print(" [--open-polygon] do not connect first and last point in polygon point list")
  72. print("")
  73. def print_polygon_debug(pgn, ofp=sys.stdout):
  74. print("", file=ofp)
  75. for idx in range(len(pgn)):
  76. print("#", idx, pgn[idx]["t"], ":", pgn[idx]["x"], pgn[idx]["y"], pgn[idx]["s"], "n:", pgn[idx]["n"], file=ofp)
  77. def print_polygon_debug2(pgn, ofp=sys.stdout):
  78. print("", file=ofp)
  79. for idx in range(len(pgn)):
  80. z = 0.0
  81. if pgn[idx]["t"] == "t": z = 1
  82. print(pgn[idx]["x"], pgn[idx]["y"], z, file=ofp)
  83. #if idx>0 and ((pgn[idx-1]["t"] == "." and pgn[idx]["t"] == "t") or (pgn[idx-1]["t"] == "t" and pgn[idx]["t"] == ".")):
  84. # print( "\n", file=ofp)
  85. def cmp_pgn_ele(a,b):
  86. if (len(a) == 0): return -1
  87. if (len(b) == 0): return 1
  88. s0 = math.sqrt(a[0][0]*a[0][0] + a[0][1]*a[0][1])
  89. s1 = math.sqrt(b[0][0]*b[0][0] + b[0][1]*b[0][1])
  90. if s0 < s1: return -1
  91. if s1 > s0: return 1
  92. return 0
  93. def polygons_sort(pgns):
  94. pgns.sort(cmp_pgn_ele)
  95. def print_polygon(pgn, ofp=sys.stdout):
  96. print("", file=ofp)
  97. for idx in range(len(pgn)):
  98. print(pgn[idx]["x"], pgn[idx]["y"], file=ofp)
  99. def crossprodmag(u,v, eps=1.0/1024.0):
  100. ux = u["x"]
  101. uy = u["y"]
  102. us = math.sqrt(ux*ux + uy*uy)
  103. vx = v["x"]
  104. vy = v["y"]
  105. vs = math.sqrt(vx*vx + vy*vy)
  106. if (abs(us) < eps) or (abs(vs) < eps): return 0.0
  107. return ((ux*vy) - (uy*vx))/(us*vs)
  108. def ingest_egest_orig(ctx, ifp = sys.stdin, ofp = sys.stdout):
  109. polygons = []
  110. polygon = []
  111. print(ctx["header"], file=ofp)
  112. line_no=0
  113. #for line in sys.stdin:
  114. for line in ifp:
  115. line = line.strip()
  116. line_no += 1
  117. if (len(line)==0) or (line == ""):
  118. if len(polygon)>0:
  119. polygons.append(polygon)
  120. polygon = []
  121. continue
  122. if line[0]=='#': continue
  123. r = re.split(r'\s+', line)
  124. if len(r)!=2:
  125. print("Error on line " + str(line_no) + ": number of arguments is not 2 (" + line + ")", file=sys.stderr)
  126. sys.exit(1)
  127. try:
  128. x = float(r[0]) * ctx["premul"]
  129. y = float(r[1]) * ctx["premul"]
  130. except Exception, e:
  131. print(e, file=sys.stderr)
  132. sys.exit(1)
  133. polygon.append([x,y])
  134. for p in polygons:
  135. if len(p)==0: continue
  136. x0 = p[0][0]
  137. y0 = p[0][1]
  138. print("G0", "X" + "{:.10f}".format(x0), "Y" + "{:.10f}".format(y0), sfx_rapid, file=ofp)
  139. for xy in p:
  140. x = xy[0]
  141. y = xy[1]
  142. print("G1", "X" + "{:.10f}".format(x), "Y" + "{:.10f}".format(y), ctx["g1speed"], file=ofp)
  143. if ctx["close_polygon"]:
  144. print("G1", "X" + "{:.10f}".format(x0), "Y" + "{:.10f}".format(y0), ctx["g1speed"], file=ofp)
  145. print(file=ofp)
  146. print(sfx, file=ofp)
  147. def ingest_egest(ctx, ifp = sys.stdin, ofp = sys.stdout):
  148. polygons = []
  149. polygon = []
  150. print(ctx["header"], file=ofp)
  151. line_no=0
  152. #for line in sys.stdin:
  153. for line in ifp:
  154. line = line.strip()
  155. line_no += 1
  156. if (len(line)==0) or (line == ""):
  157. if len(polygon)>0:
  158. polygons.append(polygon)
  159. polygon = []
  160. continue
  161. if line[0]=='#': continue
  162. r = re.split(r'\s+', line)
  163. if len(r)!=2:
  164. print("Error on line " + str(line_no) + ": number of arguments is not 2 (" + line + ")", file=sys.stderr)
  165. sys.exit(1)
  166. try:
  167. x = float(r[0]) * ctx["premul"]
  168. y = float(r[1]) * ctx["premul"]
  169. except Exception, e:
  170. print(e, file=sys.stderr)
  171. sys.exit(1)
  172. polygon.append([x,y])
  173. if len(polygon)>0:
  174. polygons.append(polygon)
  175. if ctx["sort"]:
  176. polygons_sort(polygons)
  177. for p in polygons:
  178. if len(p)==0: continue
  179. x0 = p[0][0]
  180. y0 = p[0][1]
  181. print("G0", "X" + "{:.10f}".format(x0), "Y" + "{:.10f}".format(y0), ctx["g0speed"], file=ofp)
  182. nstep=1
  183. if ctx["z_active"]:
  184. print("G0", "Z" + "{:.10f}".format(ctx["z_height"]), ctx["z_rapid"], file=ofp)
  185. nstep = int(math.ceil(( abs(ctx["z_plunge"] - ctx["z_0"]) )/ctx["z_step"]))
  186. for s in range(nstep):
  187. if ctx["z_active"]:
  188. zh = ((ctx["z_plunge"] - ctx["z_0"]) * float(s+1)/float(nstep)) + ctx["z_0"]
  189. if zh < ctx["z_plunge"]:
  190. print(";#(CLAMPING)", file=ofp)
  191. zh = ctx["z_plunge"]
  192. print("G1", "Z" + "{:.10f}".format(zh), ctx["z_slow"], file=ofp)
  193. for xy in p:
  194. x = xy[0]
  195. y = xy[1]
  196. print("G1", "X" + "{:.10f}".format(x), "Y" + "{:.10f}".format(y), ctx["g1speed"], file=ofp)
  197. if ctx["close_polygon"]:
  198. print("G1", "X" + "{:.10f}".format(x0), "Y" + "{:.10f}".format(y0), ctx["g1speed"], file=ofp)
  199. print(file=ofp)
  200. if ctx["z_active"]:
  201. print("G0", "Z" + "{:.10f}".format(ctx["z_height"]), ctx["z_rapid"], file=ofp)
  202. print(ctx["footer"], file=ofp)
  203. ## decorate the pgn array of objects with the cross product normal
  204. ## magnitude value 'n'
  205. ##
  206. def polygon_decorate_n(pgn):
  207. if len(pgn) < 3:
  208. for idx in range(len(pgn)):
  209. pgn[idx]["n"] = 0.0
  210. return
  211. for idx in range(len(pgn)):
  212. prv_idx = (idx + len(pgn) - 1) % len(pgn)
  213. nxt_idx = (idx + 1) % len(pgn)
  214. v0 = { "x": pgn[prv_idx]["x"] - pgn[idx]["x"], "y" : pgn[prv_idx]["y"] - pgn[idx]["y"] }
  215. v1 = { "x": pgn[nxt_idx]["x"] - pgn[idx]["x"], "y" : pgn[nxt_idx]["y"] - pgn[idx]["y"] }
  216. pgn[idx]["n"] = crossprodmag(v0,v1)
  217. ## calculate the 'score' of the tab placed at path s-position 'tab_beg' with
  218. ## tab length 'tab_len'.
  219. ## This is length of each portion the tab falls on the line segment times the
  220. ## magnitude of the normal.
  221. ## Since the left and right segment use the same normal, and the normal is thus
  222. ## multiplied by itself, this score is strictly positive.
  223. ##
  224. def polygon_curve_score(pgn, tab_beg, tab_len):
  225. score = 0.0
  226. if len(pgn) < 3: return score
  227. for idx in range(len(pgn)):
  228. prv_idx = (idx + len(pgn) - 1) % len(pgn)
  229. nxt_idx = (idx + 1) % len(pgn)
  230. if (tab_beg + tab_len) < pgn[idx]["s"]: return score
  231. if tab_beg > pgn[idx]["s"]: continue
  232. s0 = abs(max(pgn[prv_idx]["s"], tab_beg) - pgn[idx]["s"])
  233. s1 = abs(pgn[idx]["s"] - min(tab_beg + tab_len, pgn[nxt_idx]["s"]))
  234. score += s0*s1*pgn[idx]["n"]*pgn[idx]["n"]
  235. return score
  236. ## insert the tab into the 'pgn' array at s-position 'tab_beg' with
  237. ## 'tab_len'.
  238. ## This does not wrap around from the end to the beginning (though this
  239. ## might be upadted in the future).
  240. ## This constructs a new array and returns it.
  241. ##
  242. def polygon_insert_tab_at(pgn, tab_beg, tab_len):
  243. ret_p = []
  244. prv_x = 0.0
  245. prv_y = 0.0
  246. prv_s = 0.0
  247. for pnt_idx in range(len(pgn)):
  248. p = pgn[pnt_idx]
  249. if pnt_idx==0:
  250. prv_x = p["x"]
  251. prv_y = p["y"]
  252. prv_s = p["s"]
  253. # take care of pathological case when line segment is 0 length
  254. #
  255. r = math.sqrt((prv_x - p["x"])*(prv_x - p["x"]) + (prv_y - p["y"])*(prv_y - p["y"]))
  256. if abs(r) < ctx["epsilon"]:
  257. prv_x = p["x"]
  258. prv_y = p["y"]
  259. prv_s = p["s"]
  260. r = 1.0
  261. # If the tab is completel to the 'left' of the current vertex, we can insert it
  262. # completely without needing to split the tab into segements.
  263. # Do so, making sure \to insert an extra 'down' entry so that if we are right
  264. # next to another tab in the list, we'll properly render the outline.
  265. #
  266. if (tab_beg <= p["s"]) and ((tab_beg + tab_len) < p["s"]):
  267. ds = tab_beg - prv_s
  268. x = (ds * (p["x"] - prv_x) / r) + prv_x
  269. y = (ds * (p["y"] - prv_y) / r) + prv_y
  270. ret_p.append( {"x": x, "y": y, "s": tab_beg, "t": "t", "n":0.0 } )
  271. ds = tab_beg + tab_len - prv_s
  272. x = (ds * (p["x"] - prv_x) / r) + prv_x
  273. y = (ds * (p["y"] - prv_y) / r) + prv_y
  274. ret_p.append( {"x": x, "y": y, "s": tab_beg + tab_len, "t": "t", "n":0.0 } )
  275. ret_p.append( {"x": x, "y": y, "s": tab_beg + tab_len, "t": ".", "n":0.0 } )
  276. ret_p += pgn[pnt_idx:]
  277. return ret_p
  278. pnt_type = p["t"]
  279. # The tab is split across the current vertex and entries to the right.
  280. # Shave off the head of the tab, add/modify the entries to create a 'tab'
  281. # entry in the 'pgn' list.
  282. #
  283. if (tab_beg <= p["s"]) and ((tab_beg + tab_len) > p["s"]):
  284. ds = tab_beg - prv_s
  285. x = (ds * (p["x"] - prv_x) / r) + prv_x
  286. y = (ds * (p["y"] - prv_y) / r) + prv_y
  287. ret_p.append( {"x": x, "y": y, "s": tab_beg, "t": "t", "n": 0.0 } )
  288. pnt_type = "t"
  289. tab_len -= (p["s"] - tab_beg)
  290. tab_beg = p["s"]
  291. ret_p.append( { "x" : p["x"], "y": p["y"], "s": p["s"], "t": pnt_type, "n": 0.0 })
  292. prv_x = p["x"]
  293. prv_y = p["y"]
  294. prv_s = p["s"]
  295. return ret_p
  296. ## Process polygon and insert tabs.
  297. ##
  298. def ingest_egest_with_tabs(ctx, ifp = sys.stdin, ofp = sys.stdout):
  299. polygons = []
  300. polygon = []
  301. print(ctx["header"], file=ofp)
  302. firstPoint = True
  303. prev_x = 0.0
  304. prev_y = 0.0
  305. line_no=0
  306. for line in ifp:
  307. line = line.strip()
  308. line_no += 1
  309. if (len(line)==0) or (line == ""):
  310. if len(polygon)>0:
  311. polygons.append(polygon)
  312. polygon = []
  313. firstPoint = True
  314. continue
  315. if line[0]=='#': continue
  316. r = re.split(r'\s+', line)
  317. if len(r)!=2:
  318. print("Error on line " + str(line_no) + ": number of arguments is not 2 (" + line + ")", file=sys.stderr)
  319. sys.exit(1)
  320. try:
  321. x = float(r[0]) * ctx["premul"]
  322. y = float(r[1]) * ctx["premul"]
  323. except Exception, e:
  324. print(e, file=sys.stderr)
  325. sys.exit(1)
  326. # record lenght of outline as we go
  327. #
  328. s = 0.0
  329. if not firstPoint:
  330. ds = math.sqrt( (prev_x - x)*(prev_x - x) + (prev_y - y)*(prev_y - y) )
  331. s = polygon[ len(polygon) - 1 ]["s"] + ds
  332. prev_x = x
  333. prev_y = y
  334. # '.' type ("T" field) represent simple contour whereas 't' type
  335. # represents tabs. Though space iniefficient, it's easier
  336. # to decorate entries with modifiers like this than do it a more
  337. # complicated but efficietn way.
  338. #
  339. polygon.append({ "x":x, "y":y, "s": s, "t": ".", "n":0.0 })
  340. firstPoint = False
  341. if len(polygon)!=0:
  342. polygons.append(polygon)
  343. if ctx["sort"]:
  344. polygons_sort(polygons)
  345. # decorate with 'n' cross product magnitude value for
  346. # score tab positioning heuristic.
  347. #
  348. for pgn_idx in range(len(polygons)):
  349. pgn = polygons[pgn_idx]
  350. if len(pgn)<3:
  351. for idx in range(len(pgn)):
  352. pgn[idx]["n"] = 0.0
  353. continue
  354. for idx in range(len(pgn)):
  355. prv_idx = (idx + len(pgn) - 1) % len(pgn)
  356. nxt_idx = (idx + 1) % len(pgn)
  357. v0 = { "x": pgn[prv_idx]["x"] - pgn[idx]["x"], "y" : pgn[prv_idx]["y"] - pgn[idx]["y"] }
  358. v1 = { "x": pgn[nxt_idx]["x"] - pgn[idx]["x"], "y" : pgn[nxt_idx]["y"] - pgn[idx]["y"] }
  359. pgn[idx]["n"] = crossprodmag( v0, v1 )
  360. _zheight = ctx["z_height"]
  361. _zplunge = ctx["z_plunge"]
  362. _zzero = ctx["z_0"]
  363. _zstep = ctx["z_step"]
  364. _g0speed = ctx["g0speed"]
  365. _g1speed = ctx["g1speed"]
  366. _tabheight = ctx["tab_height"]
  367. _ztabstart = _zplunge + _tabheight
  368. _tablen = ctx["tab_length"]
  369. _ntab = ctx["tab_n"]
  370. # a bit inefficient but construct tabs
  371. #
  372. for pidx in range(len(polygons)):
  373. p = polygons[pidx]
  374. if len(p)==0: continue
  375. polygon_decorate_n(polygons[pidx])
  376. clen = p[ len(p) - 1 ]["s"]
  377. s_window_len = (clen / (float(_ntab))) - _tablen
  378. if _tablen <= 0.0: continue
  379. if clen < (float(_ntab) * _tablen): continue
  380. for tabidx in range(_ntab):
  381. tab_s_offset = float(tabidx) * clen / float(_ntab)
  382. # this heuristic, for simplicity, only uses two scores, one at the original
  383. # offset and another at some distance away that doesn't overlap with the
  384. # other tab, to determine where the tab should be positioned.
  385. #
  386. n_tab_sample = 4
  387. score, min_score, min_idx = [], 0, 0
  388. for idx in range(n_tab_sample):
  389. tab_shift = float(idx) * s_window_len / float(n_tab_sample)
  390. score.append( polygon_curve_score(polygons[pidx], tab_s_offset + tab_shift , _tablen) )
  391. #print("#", tab_s_offset + tab_shift, score[idx])
  392. if idx==0:
  393. min_score = score[0]
  394. min_idx = 0
  395. elif min_score > score[idx]:
  396. min_score = score[idx]
  397. min_idx = idx
  398. #print("## min_score", min_score, ", min_idx", min_idx)
  399. tab_shift = float(min_idx) * s_window_len / float(n_tab_sample)
  400. polygons[pidx] = polygon_insert_tab_at(polygons[pidx], tab_s_offset + tab_shift, _tablen)
  401. #if score0 == min(score0, score1, score2):
  402. # polygons[pidx] = polygon_insert_tab_at(polygons[pidx], tab_s_offset, _tablen)
  403. #elif score1 == min(score0, score1, score2):
  404. # polygons[pidx] = polygon_insert_tab_at(polygons[pidx], tab_s_offset + s_window_len/2, _tablen)
  405. #else:
  406. # polygons[pidx] = polygon_insert_tab_at(polygons[pidx], tab_s_offset + s_window_len, _tablen)
  407. #print_polygon_debug(polygons[pidx])
  408. for p in polygons:
  409. if len(p)==0: continue
  410. x0 = p[0]["x"]
  411. y0 = p[0]["y"]
  412. print("G0", "X" + "{:.10f}".format(x0), "Y" + "{:.10f}".format(y0), _g0speed, file=ofp)
  413. nstep=1
  414. if ctx["z_active"]:
  415. print("G0", "Z" + "{:.10f}".format(_zheight), _g0speed, file=ofp)
  416. nstep = int(abs(math.ceil((_zplunge - _zzero)/_zstep)))
  417. prev_entry_type = "."
  418. for s in range(nstep):
  419. if ctx["z_active"]:
  420. zh = ((_zplunge - _zzero) * float(s+1)/float(nstep)) + _zzero
  421. if zh < _zplunge:
  422. zh = _zplunge
  423. #if (zh < _ztabstart) and (prev_entry_type == ".") and (xy["t"] == "t"):
  424. # #print(";# up!", file=ofp)
  425. # print("G1", "Z" + "{:.10f}".format(_ztabstart), _g1speed, file=ofp)
  426. #else:
  427. # print("G1", "Z" + "{:.10f}".format(zh), _g1speed, file=ofp)
  428. firstIter = True
  429. for xy in p:
  430. if firstIter:
  431. if (zh < _ztabstart) and (prev_entry_type == ".") and (xy["t"] == "t"):
  432. #print(";# up!", file=ofp)
  433. print("G1", "Z" + "{:.10f}".format(_ztabstart), _g1speed, file=ofp)
  434. else:
  435. print("G1", "Z" + "{:.10f}".format(zh), _g1speed, file=ofp)
  436. firstIter = False
  437. if ctx["z_active"] and (zh < _ztabstart):
  438. if (prev_entry_type == "t") and (xy["t"] == "."):
  439. #print(";# down!", file=ofp)
  440. print("G1", "Z" + "{:.10f}".format(zh), _g1speed, file=ofp)
  441. x = xy["x"]
  442. y = xy["y"]
  443. print("G1", "X" + "{:.10f}".format(x), "Y" + "{:.10f}".format(y), _g1speed, file=ofp)
  444. if ctx["z_active"]:
  445. if zh < _ztabstart:
  446. if (prev_entry_type == ".") and (xy["t"] == "t"):
  447. #print(";# up!", file=ofp)
  448. print("G1", "Z" + "{:.10f}".format(_ztabstart), _g1speed, file=ofp)
  449. prev_entry_type = xy["t"]
  450. #if ctx["z_active"]:
  451. if ctx["close_polygon"]:
  452. print("G1", "X" + "{:.10f}".format(x0), "Y" + "{:.10f}".format(y0), _g1speed, file=ofp)
  453. print(file=ofp)
  454. print("G0", "Z" + "{:.10f}".format(_zheight), _g0speed, file=ofp)
  455. print(ctx["footer"], file=ofp)
  456. def main(argv):
  457. global ctx
  458. ifn, ofn = "-", "-"
  459. ifp, ofp = sys.stdin, sys.stdout
  460. long_opt_list = [ "help", "preset=", "show-context", "z", "explicit-speed", "premul=", "epsilon=", "header=", "footer=", "rapid=", "slow=",
  461. "z-rapid=", "z-slow=", "z-step=", "z-raise=", "z-plunge=",
  462. "tab", "tab-n=", "tab-length=", "tab-height=", "notab",
  463. "close-polygon", "open-polygon",
  464. "sort"]
  465. try:
  466. #opts,args = getopt.getopt(argv,"hi:o:",["sfx-final=", "pfx=", "sfx-rapid=","sfx-slow=", "premul=", "z-step", "z-raise", "z-plunge"])
  467. opts,args = getopt.getopt(argv,"hi:o:",long_opt_list)
  468. except getopt.GetoptError:
  469. usage()
  470. sys.exit(2)
  471. if len(args) >= 1:
  472. ifn = args[0]
  473. if len(args) >= 2:
  474. ofn = args[1]
  475. for opt, arg in opts:
  476. if opt == "--preset":
  477. if arg.lower() in ("maslow", "maslowcnc"):
  478. ctx = ctx_maslow
  479. elif arg.lower() in ("laser"):
  480. ctx = ctx_laser
  481. else:
  482. print("WARNING: no preset found, using default", file=sys.stderr)
  483. show_context = False
  484. for opt, arg in opts:
  485. if opt in ("-h", "--help"):
  486. usage()
  487. sys.exit()
  488. elif opt in ("--show-context"): show_context = True
  489. elif opt in ("-i", "--ifile"): ifn = arg
  490. elif opt in ("-o", "--ofile"): ofn = arg
  491. elif opt in ("--premul"): ctx["premul"] = float(arg)
  492. elif opt in ("--rapid"):
  493. ctx["g0speed"] = arg
  494. ctx["explicit_speed"] = True
  495. elif opt in ("--slow"):
  496. ctx["g1speed"] = arg
  497. ctx["explicit_speed"] = True
  498. elif opt in ("--explicit-speed"): ctx["explicit_speed"] = True
  499. elif opt in ("--header"): ctx["header"] = arg.decode('unicode_escape')
  500. elif opt in ("--footer"): ctx["footer"] = arg.decode('unicode_escape')
  501. elif opt in ("--close-polygon"): ctx["close_polygon"] = True
  502. elif opt in ("--open-polygon"): ctx["close_polygon"] = False
  503. elif opt in ("--z"): ctx["z_active"] = True
  504. elif opt in ("--z-slow"): ctx["z_slow"] = float(arg)
  505. elif opt in ("--z-rapid"): ctx["z_rapid"] = float(arg)
  506. elif opt in ("--z-step"): ctx["z_step"] = float(arg)
  507. elif opt in ("--z-raise"): ctx["z_height"] = float(arg)
  508. elif opt in ("--z-plunge"):
  509. ctx["z_active"] = True
  510. ctx["z_plunge"] = float(arg)
  511. elif opt in ("--notab"): ctx["tab_n"] = 0
  512. elif opt in ("--tab"): ctx["tab_n"] = ctx["tab_default_n"]
  513. elif opt in ("--tab-n"): ctx["tab_n"] = int(arg)
  514. elif opt in ("--tab-length"): ctx["tab_length"] = float(arg)
  515. elif opt in ("--tab-height"): ctx["tab_height"] = float(arg)
  516. elif opt in ("--sort"): ctx["sort"] = True
  517. if show_context:
  518. print(json.dumps(ctx, indent=2))
  519. sys.exit(0)
  520. if ifn != "-": ifp = open(ifn,"r")
  521. if ofn != "-": ofp = open(ofn, "w")
  522. if ctx["tab_n"] > 0 : ingest_egest_with_tabs(ctx, ifp, ofp)
  523. else: ingest_egest(ctx, ifp, ofp)
  524. if ifp!=sys.stdin: ifp.close()
  525. if ofp!=sys.stdout: ofp.close()
  526. if __name__ == "__main__":
  527. main(sys.argv[1:])