GCodeReader.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. # Copyright (c) 2016 Aleph Objects, Inc.
  2. # Cura is released under the terms of the AGPLv3 or higher.
  3. from UM.Mesh.MeshReader import MeshReader
  4. import os
  5. from UM.Scene.SceneNode import SceneNode
  6. from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
  7. from UM.Math.Vector import Vector
  8. from UM.Math.AxisAlignedBox import AxisAlignedBox
  9. from UM.Application import Application
  10. from UM.Message import Message
  11. from UM.Logger import Logger
  12. from UM.Backend.Backend import BackendState
  13. from UM.i18n import i18nCatalog
  14. catalog = i18nCatalog("cura")
  15. from cura import LayerDataBuilder
  16. from cura import LayerDataDecorator
  17. from cura.LayerPolygon import LayerPolygon
  18. import numpy
  19. import math
  20. class GCodeReader(MeshReader):
  21. def __init__(self):
  22. super(GCodeReader, self).__init__()
  23. self._supported_extensions = [".gcode", ".g"]
  24. Application.getInstance().hideMessageSignal.connect(self._onHideMessage)
  25. self._cancelled = False
  26. self._message = None
  27. self._scene_node = None
  28. @staticmethod
  29. def _getValue(line, code):
  30. n = line.find(code) + len(code)
  31. if n < 1:
  32. return None
  33. m = line.find(' ', n)
  34. try:
  35. if m < 0:
  36. return line[n:]
  37. return line[n:m]
  38. except:
  39. return None
  40. def _getInt(self, line, code):
  41. value = self._getValue(line, code)
  42. try:
  43. return int(value)
  44. except:
  45. return None
  46. def _getFloat(self, line, code):
  47. value = self._getValue(line, code)
  48. try:
  49. return float(value)
  50. except:
  51. return None
  52. def _onHideMessage(self, message):
  53. if message == self._message:
  54. self._cancelled = True
  55. def _onParentChanged(self, node):
  56. backend = Application.getInstance().getBackend()
  57. if self._scene_node is not None and self._scene_node.getParent() is None:
  58. self._scene_node = None
  59. backend.backendStateChange.emit(BackendState.NotStarted)
  60. Application.getInstance().setHideSettings(False)
  61. Application.getInstance().getPrintInformation().setPreSliced(False)
  62. # else:
  63. # backend.backendStateChange.emit(BackendState.SlicingDisabled)
  64. # Application.getInstance().getPrintInformation().setPreSliced(True)
  65. # Application.getInstance().setHideSettings(True)
  66. @staticmethod
  67. def _getNullBoundingBox():
  68. return AxisAlignedBox(minimum=Vector(0, 0, 0), maximum=Vector(10, 10, 10))
  69. @staticmethod
  70. def _createPolygon(layer_data, path, layer_id, extruder):
  71. countvalid = 0
  72. for point in path:
  73. if point[3] > 0:
  74. countvalid += 1
  75. if countvalid < 2:
  76. return False
  77. try:
  78. layer_data.addLayer(layer_id)
  79. layer_data.setLayerHeight(layer_id, path[0][1])
  80. layer_data.setLayerThickness(layer_id, 0.25)
  81. this_layer = layer_data.getLayer(layer_id)
  82. except ValueError:
  83. return False
  84. count = len(path)
  85. line_types = numpy.empty((count - 1, 1), numpy.int32)
  86. line_widths = numpy.empty((count - 1, 1), numpy.float32)
  87. line_widths[:, 0] = 0.5
  88. points = numpy.empty((count, 3), numpy.float32)
  89. i = 0
  90. for point in path:
  91. points[i, 0] = point[0]
  92. points[i, 1] = point[2]
  93. points[i, 2] = -point[1]
  94. if i > 0:
  95. if point[3] == LayerPolygon.Inset0Type:
  96. line_types[i - 1] = extruder + 1
  97. else:
  98. line_types[i - 1] = point[3]
  99. i += 1
  100. this_poly = LayerPolygon(layer_data, extruder, line_types, points, line_widths)
  101. this_poly.buildCache()
  102. this_layer.polygons.append(this_poly)
  103. return True
  104. def read(self, file_name):
  105. Logger.log("d", "Preparing to load %s" % file_name)
  106. self._cancelled = False
  107. self._scene_node = SceneNode()
  108. self._scene_node.getBoundingBox = self._getNullBoundingBox # Manually set bounding box, because mesh doesn't have mesh data
  109. self._scene_node.gcode = True
  110. self._scene_node.parentChanged.connect(self._onParentChanged)
  111. backend = Application.getInstance().getBackend()
  112. backend.close()
  113. backend.backendStateChange.emit(BackendState.SlicingDisabled)
  114. glist = []
  115. Application.getInstance().getController().getScene().gcode_list = glist
  116. Logger.log("d", "Opening file %s" % file_name)
  117. layer_data_builder = LayerDataBuilder.LayerDataBuilder()
  118. with open(file_name, "r") as file:
  119. file_lines = 0
  120. current_line = 0
  121. for line in file:
  122. file_lines += 1
  123. file.seek(0)
  124. file_step = max(math.floor(file_lines / 100), 1)
  125. current_extruder = 0
  126. current_path = []
  127. current_x = 0
  128. current_y = 0
  129. current_z = 0
  130. current_e = 0
  131. current_layer = 0
  132. self._message = Message(catalog.i18nc("@info:status", "Parsing GCODE"), lifetime=0)
  133. self._message.setProgress(0)
  134. self._message.show()
  135. Logger.log("d", "Parsing %s" % file_name)
  136. for line in file:
  137. if self._cancelled:
  138. Logger.log("w", "Parsing %s cancelled" % file_name)
  139. return None
  140. current_line += 1
  141. if current_line % file_step == 0:
  142. self._message.setProgress(math.floor(current_line / file_lines * 100))
  143. if len(line) == 0:
  144. continue
  145. if line[0] == ";":
  146. continue
  147. G = self._getInt(line, "G")
  148. if G is not None:
  149. if G == 0 or G == 1:
  150. x = self._getFloat(line, "X")
  151. y = self._getFloat(line, "Y")
  152. z = self._getFloat(line, "Z")
  153. e = self._getFloat(line, "E")
  154. z_changed = False
  155. if x is not None:
  156. current_x = x
  157. if y is not None:
  158. current_y = y
  159. if z is not None:
  160. if not current_z == z:
  161. z_changed = True
  162. current_z = z
  163. if e is not None:
  164. if e > current_e:
  165. current_path.append([current_x, current_y, current_z, LayerPolygon.Inset0Type]) # extrusion
  166. else:
  167. current_path.append([current_x, current_y, current_z, LayerPolygon.MoveRetractionType]) # retraction
  168. current_e = e
  169. else:
  170. current_path.append([current_x, current_y, current_z, LayerPolygon.NoneType])
  171. if z_changed:
  172. if len(current_path) > 1 and current_z > 0:
  173. if self._createPolygon(layer_data_builder, current_path, current_layer, current_extruder):
  174. current_layer += 1
  175. current_path.clear()
  176. else:
  177. current_path.clear()
  178. elif G == 28:
  179. x = self._getFloat(line, "X")
  180. y = self._getFloat(line, "Y")
  181. if x is not None:
  182. current_x = x
  183. if y is not None:
  184. current_y = y
  185. current_z = 0
  186. elif G == 92:
  187. x = self._getFloat(line, "X")
  188. y = self._getFloat(line, "Y")
  189. z = self._getFloat(line, "Z")
  190. e = self._getFloat(line, "E")
  191. if x is not None:
  192. current_x = x
  193. if y is not None:
  194. current_y = y
  195. if z is not None:
  196. current_z = z
  197. if e is not None:
  198. current_e = e
  199. T = self._getInt(line, "T")
  200. if T is not None:
  201. current_extruder = T
  202. if len(current_path) > 1 and current_z > 0:
  203. if self._createPolygon(layer_data_builder, current_path, current_layer, current_extruder):
  204. current_layer += 1
  205. current_path.clear()
  206. else:
  207. current_path.clear()
  208. if len(current_path) > 1 and current_z > 0:
  209. if self._createPolygon(layer_data_builder, current_path, current_layer, current_extruder):
  210. current_layer += 1
  211. current_path.clear()
  212. layer_mesh = layer_data_builder.build()
  213. decorator = LayerDataDecorator.LayerDataDecorator()
  214. decorator.setLayerData(layer_mesh)
  215. self._scene_node.removeDecorator("LayerDataDecorator")
  216. self._scene_node.addDecorator(decorator)
  217. Logger.log("d", "Finished parsing %s" % file_name)
  218. self._message.hide()
  219. if current_layer == 0:
  220. Logger.log("w", "File %s doesn't contain any valid layers" % file_name)
  221. Application.getInstance().getPrintInformation().setPreSliced(True)
  222. Application.getInstance().setHideSettings(True)
  223. settings = Application.getInstance().getGlobalContainerStack()
  224. machine_width = settings.getProperty("machine_width", "value")
  225. machine_depth = settings.getProperty("machine_depth", "value")
  226. self._scene_node.setPosition(Vector(-machine_width / 2, 0, machine_depth / 2))
  227. Logger.log("d", "Loaded %s" % file_name)
  228. return self._scene_node