mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-13 19:35:56 +08:00
Merge pull request #1267 from alephobjects/GCODEReader_upstream
Add a Gcode reader plugin
This commit is contained in:
commit
47be957f38
9
cura/BlockSlicingDecorator.py
Normal file
9
cura/BlockSlicingDecorator.py
Normal file
@ -0,0 +1,9 @@
|
||||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
|
||||
|
||||
class BlockSlicingDecorator(SceneNodeDecorator):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def isBlockSlicing(self):
|
||||
return True
|
@ -19,6 +19,8 @@ from UM.SaveFile import SaveFile
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Scene.GroupDecorator import GroupDecorator
|
||||
from UM.Settings.Validator import Validator
|
||||
from UM.Message import Message
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
|
||||
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
||||
@ -26,13 +28,13 @@ from UM.Operations.GroupedOperation import GroupedOperation
|
||||
from UM.Operations.SetTransformOperation import SetTransformOperation
|
||||
from UM.Operations.TranslateOperation import TranslateOperation
|
||||
from cura.SetParentOperation import SetParentOperation
|
||||
from cura.SliceableObjectDecorator import SliceableObjectDecorator
|
||||
from cura.BlockSlicingDecorator import BlockSlicingDecorator
|
||||
|
||||
from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.SettingFunction import SettingFunction
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
from . import PlatformPhysics
|
||||
from . import BuildVolume
|
||||
from . import CameraAnimation
|
||||
@ -135,6 +137,9 @@ class CuraApplication(QtApplication):
|
||||
}
|
||||
)
|
||||
|
||||
self._currently_loading_files = []
|
||||
self._non_sliceable_extensions = []
|
||||
|
||||
self._machine_action_manager = MachineActionManager.MachineActionManager()
|
||||
self._machine_manager = None # This is initialized on demand.
|
||||
self._setting_inheritance_manager = None
|
||||
@ -580,9 +585,12 @@ class CuraApplication(QtApplication):
|
||||
def updatePlatformActivity(self, node = None):
|
||||
count = 0
|
||||
scene_bounding_box = None
|
||||
is_block_slicing_node = False
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if type(node) is not SceneNode or not node.getMeshData():
|
||||
if type(node) is not SceneNode or (not node.getMeshData() and not node.callDecoration("getLayerData")):
|
||||
continue
|
||||
if node.callDecoration("isBlockSlicing"):
|
||||
is_block_slicing_node = True
|
||||
|
||||
count += 1
|
||||
if not scene_bounding_box:
|
||||
@ -592,6 +600,10 @@ class CuraApplication(QtApplication):
|
||||
if other_bb is not None:
|
||||
scene_bounding_box = scene_bounding_box + node.getBoundingBox()
|
||||
|
||||
print_information = self.getPrintInformation()
|
||||
if print_information:
|
||||
print_information.setPreSliced(is_block_slicing_node)
|
||||
|
||||
if not scene_bounding_box:
|
||||
scene_bounding_box = AxisAlignedBox.Null
|
||||
|
||||
@ -712,7 +724,7 @@ class CuraApplication(QtApplication):
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if type(node) is not SceneNode:
|
||||
continue
|
||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||
if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
|
||||
continue # Node that doesnt have a mesh and is not a group.
|
||||
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
||||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||
@ -1007,3 +1019,78 @@ class CuraApplication(QtApplication):
|
||||
@pyqtSlot(str)
|
||||
def log(self, msg):
|
||||
Logger.log("d", msg)
|
||||
|
||||
@pyqtSlot(QUrl)
|
||||
def readLocalFile(self, file):
|
||||
if not file.isValid():
|
||||
return
|
||||
|
||||
scene = self.getController().getScene()
|
||||
|
||||
for node in DepthFirstIterator(scene.getRoot()):
|
||||
if node.callDecoration("isBlockSlicing"):
|
||||
self.deleteAll()
|
||||
break
|
||||
|
||||
f = file.toLocalFile()
|
||||
extension = os.path.splitext(f)[1]
|
||||
filename = os.path.basename(f)
|
||||
if len(self._currently_loading_files) > 0:
|
||||
# If a non-slicable file is already being loaded, we prevent loading of any further non-slicable files
|
||||
if extension.lower() in self._non_sliceable_extensions:
|
||||
message = Message(
|
||||
self._i18n_catalog.i18nc("@info:status",
|
||||
"Only one G-code file can be loaded at a time. Skipped importing {0}",
|
||||
filename))
|
||||
message.show()
|
||||
return
|
||||
# If file being loaded is non-slicable file, then prevent loading of any other files
|
||||
extension = os.path.splitext(self._currently_loading_files[0])[1]
|
||||
if extension.lower() in self._non_sliceable_extensions:
|
||||
message = Message(
|
||||
self._i18n_catalog.i18nc("@info:status",
|
||||
"Can't open any other file if G-code is loading. Skipped importing {0}",
|
||||
filename))
|
||||
message.show()
|
||||
return
|
||||
|
||||
self._currently_loading_files.append(f)
|
||||
if extension in self._non_sliceable_extensions:
|
||||
self.deleteAll()
|
||||
|
||||
job = ReadMeshJob(f)
|
||||
job.finished.connect(self._readMeshFinished)
|
||||
job.start()
|
||||
|
||||
def _readMeshFinished(self, job):
|
||||
nodes = job.getResult()
|
||||
filename = job.getFileName()
|
||||
self._currently_loading_files.remove(filename)
|
||||
|
||||
for node in nodes:
|
||||
node.setSelectable(True)
|
||||
node.setName(os.path.basename(filename))
|
||||
|
||||
extension = os.path.splitext(filename)[1]
|
||||
if extension.lower() in self._non_sliceable_extensions:
|
||||
self.getController().setActiveView("LayerView")
|
||||
view = self.getController().getActiveView()
|
||||
view.resetLayerData()
|
||||
view.setLayer(9999999)
|
||||
view.calculateMaxLayers()
|
||||
|
||||
block_slicing_decorator = BlockSlicingDecorator()
|
||||
node.addDecorator(block_slicing_decorator)
|
||||
else:
|
||||
sliceable_decorator = SliceableObjectDecorator()
|
||||
node.addDecorator(sliceable_decorator)
|
||||
|
||||
scene = self.getController().getScene()
|
||||
|
||||
op = AddSceneNodeOperation(node, scene.getRoot())
|
||||
op.push()
|
||||
|
||||
scene.sceneChanged.emit(node)
|
||||
|
||||
def addNonSliceableExtension(self, extension):
|
||||
self._non_sliceable_extensions.append(extension)
|
||||
|
13
cura/GCodeListDecorator.py
Normal file
13
cura/GCodeListDecorator.py
Normal file
@ -0,0 +1,13 @@
|
||||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
|
||||
|
||||
class GCodeListDecorator(SceneNodeDecorator):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._gcode_list = []
|
||||
|
||||
def getGCodeList(self):
|
||||
return self._gcode_list
|
||||
|
||||
def setGCodeList(self, list):
|
||||
self._gcode_list = list
|
@ -13,6 +13,9 @@ import math
|
||||
import os.path
|
||||
import unicodedata
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
## A class for processing and calculating minimum, current and maximum print time as well as managing the job name
|
||||
#
|
||||
# This class contains all the logic relating to calculation and slicing for the
|
||||
@ -49,6 +52,8 @@ class PrintInformation(QObject):
|
||||
self._material_lengths = []
|
||||
self._material_weights = []
|
||||
|
||||
self._pre_sliced = False
|
||||
|
||||
self._backend = Application.getInstance().getBackend()
|
||||
if self._backend:
|
||||
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
|
||||
@ -61,6 +66,16 @@ class PrintInformation(QObject):
|
||||
|
||||
currentPrintTimeChanged = pyqtSignal()
|
||||
|
||||
preSlicedChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify=preSlicedChanged)
|
||||
def preSliced(self):
|
||||
return self._pre_sliced
|
||||
|
||||
def setPreSliced(self, pre_sliced):
|
||||
self._pre_sliced = pre_sliced
|
||||
self.preSlicedChanged.emit()
|
||||
|
||||
@pyqtProperty(Duration, notify = currentPrintTimeChanged)
|
||||
def currentPrintTime(self):
|
||||
return self._current_print_time
|
||||
@ -122,7 +137,9 @@ class PrintInformation(QObject):
|
||||
def createJobName(self, base_name):
|
||||
base_name = self._stripAccents(base_name)
|
||||
self._setAbbreviatedMachineName()
|
||||
if Preferences.getInstance().getValue("cura/jobname_prefix"):
|
||||
if self._pre_sliced:
|
||||
return catalog.i18nc("@label", "Pre-sliced file {0}", base_name)
|
||||
elif Preferences.getInstance().getValue("cura/jobname_prefix"):
|
||||
return self._abbr_machine + "_" + base_name
|
||||
else:
|
||||
return base_name
|
||||
@ -150,4 +167,4 @@ class PrintInformation(QObject):
|
||||
|
||||
## Utility method that strips accents from characters (eg: â -> a)
|
||||
def _stripAccents(self, str):
|
||||
return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn')
|
||||
return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn')
|
||||
|
12
cura/SliceableObjectDecorator.py
Normal file
12
cura/SliceableObjectDecorator.py
Normal file
@ -0,0 +1,12 @@
|
||||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
|
||||
|
||||
class SliceableObjectDecorator(SceneNodeDecorator):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def isSliceable(self):
|
||||
return True
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
return type(self)()
|
@ -1,22 +1,22 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
import os.path
|
||||
import zipfile
|
||||
|
||||
from UM.Job import Job
|
||||
from UM.Logger import Logger
|
||||
from UM.Math.Matrix import Matrix
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Scene.GroupDecorator import GroupDecorator
|
||||
import UM.Application
|
||||
from UM.Job import Job
|
||||
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
from UM.Application import Application
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from cura.QualityManager import QualityManager
|
||||
|
||||
import os.path
|
||||
import zipfile
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as ET
|
||||
@ -263,4 +263,4 @@ class ThreeMFReader(MeshReader):
|
||||
Logger.log("w", "Unrecognised unit %s used. Assuming mm instead", unit)
|
||||
scale = 1
|
||||
|
||||
return Vector(scale, scale, scale)
|
||||
return Vector(scale, scale, scale)
|
||||
|
@ -12,6 +12,8 @@ from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Resources import Resources
|
||||
from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then.
|
||||
from UM.Platform import Platform
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
|
||||
|
||||
import cura.Settings
|
||||
|
||||
@ -69,6 +71,8 @@ class CuraEngineBackend(Backend):
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
self._scene.sceneChanged.connect(self._onSceneChanged)
|
||||
|
||||
self._pause_slicing = False
|
||||
|
||||
# Workaround to disable layer view processing if layer view is not active.
|
||||
self._layer_view_active = False
|
||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
@ -150,6 +154,8 @@ class CuraEngineBackend(Backend):
|
||||
## Perform a slice of the scene.
|
||||
def slice(self):
|
||||
Logger.log("d", "Starting slice job...")
|
||||
if self._pause_slicing:
|
||||
return
|
||||
self._slice_start_time = time()
|
||||
if not self._enabled or not self._global_container_stack: # We shouldn't be slicing.
|
||||
# try again in a short time
|
||||
@ -183,6 +189,17 @@ class CuraEngineBackend(Backend):
|
||||
self._start_slice_job.start()
|
||||
self._start_slice_job.finished.connect(self._onStartSliceCompleted)
|
||||
|
||||
|
||||
def pauseSlicing(self):
|
||||
self.close()
|
||||
self._pause_slicing = True
|
||||
self.backendStateChange.emit(BackendState.Disabled)
|
||||
|
||||
def continueSlicing(self):
|
||||
if self._pause_slicing:
|
||||
self._pause_slicing = False
|
||||
self.backendStateChange.emit(BackendState.NotStarted)
|
||||
|
||||
## Terminate the engine process.
|
||||
def _terminate(self):
|
||||
self._slicing = False
|
||||
@ -298,6 +315,19 @@ class CuraEngineBackend(Backend):
|
||||
if source is self._scene.getRoot():
|
||||
return
|
||||
|
||||
should_pause = False
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if node.callDecoration("isBlockSlicing"):
|
||||
should_pause = True
|
||||
gcode_list = node.callDecoration("getGCodeList")
|
||||
if gcode_list is not None:
|
||||
self._scene.gcode_list = gcode_list
|
||||
|
||||
if should_pause:
|
||||
self.pauseSlicing()
|
||||
else:
|
||||
self.continueSlicing()
|
||||
|
||||
if source.getMeshData() is None:
|
||||
return
|
||||
|
||||
|
303
plugins/GCodeReader/GCodeReader.py
Normal file
303
plugins/GCodeReader/GCodeReader.py
Normal file
@ -0,0 +1,303 @@
|
||||
# Copyright (c) 2016 Aleph Objects, Inc.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Message import Message
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
from cura import LayerDataBuilder
|
||||
from cura import LayerDataDecorator
|
||||
from cura.LayerPolygon import LayerPolygon
|
||||
from cura.GCodeListDecorator import GCodeListDecorator
|
||||
|
||||
import numpy
|
||||
import math
|
||||
import re
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
# Class for loading and parsing G-code files
|
||||
class GCodeReader(MeshReader):
|
||||
def __init__(self):
|
||||
super(GCodeReader, self).__init__()
|
||||
self._supported_extensions = [".gcode", ".g"]
|
||||
Application.getInstance().hideMessageSignal.connect(self._onHideMessage)
|
||||
self._cancelled = False
|
||||
self._message = None
|
||||
self._clearValues()
|
||||
self._scene_node = None
|
||||
self._position = namedtuple('Position', ['x', 'y', 'z', 'e'])
|
||||
|
||||
def _clearValues(self):
|
||||
self._extruder = 0
|
||||
self._layer_type = LayerPolygon.Inset0Type
|
||||
self._layer = 0
|
||||
self._previous_z = 0
|
||||
self._layer_data_builder = LayerDataBuilder.LayerDataBuilder()
|
||||
self._center_is_zero = False
|
||||
|
||||
@staticmethod
|
||||
def _getValue(line, code):
|
||||
n = line.find(code)
|
||||
if n < 0:
|
||||
return None
|
||||
n += len(code)
|
||||
pattern = re.compile("[;\s]")
|
||||
match = pattern.search(line, n)
|
||||
m = match.start() if match is not None else -1
|
||||
try:
|
||||
if m < 0:
|
||||
return line[n:]
|
||||
return line[n:m]
|
||||
except:
|
||||
return None
|
||||
|
||||
def _getInt(self, line, code):
|
||||
value = self._getValue(line, code)
|
||||
try:
|
||||
return int(value)
|
||||
except:
|
||||
return None
|
||||
|
||||
def _getFloat(self, line, code):
|
||||
value = self._getValue(line, code)
|
||||
try:
|
||||
return float(value)
|
||||
except:
|
||||
return None
|
||||
|
||||
def _onHideMessage(self, message):
|
||||
if message == self._message:
|
||||
self._cancelled = True
|
||||
|
||||
@staticmethod
|
||||
def _getNullBoundingBox():
|
||||
return AxisAlignedBox(minimum=Vector(0, 0, 0), maximum=Vector(10, 10, 10))
|
||||
|
||||
def _createPolygon(self, current_z, path):
|
||||
countvalid = 0
|
||||
for point in path:
|
||||
if point[3] > 0:
|
||||
countvalid += 1
|
||||
if countvalid < 2:
|
||||
return False
|
||||
try:
|
||||
self._layer_data_builder.addLayer(self._layer)
|
||||
self._layer_data_builder.setLayerHeight(self._layer, path[0][2])
|
||||
self._layer_data_builder.setLayerThickness(self._layer, math.fabs(current_z - self._previous_z))
|
||||
this_layer = self._layer_data_builder.getLayer(self._layer)
|
||||
except ValueError:
|
||||
return False
|
||||
count = len(path)
|
||||
line_types = numpy.empty((count - 1, 1), numpy.int32)
|
||||
line_widths = numpy.empty((count - 1, 1), numpy.float32)
|
||||
# TODO: need to calculate actual line width based on E values
|
||||
line_widths[:, 0] = 0.4
|
||||
points = numpy.empty((count, 3), numpy.float32)
|
||||
i = 0
|
||||
for point in path:
|
||||
points[i, 0] = point[0]
|
||||
points[i, 1] = point[2]
|
||||
points[i, 2] = -point[1]
|
||||
if i > 0:
|
||||
line_types[i - 1] = point[3]
|
||||
if point[3] in [LayerPolygon.MoveCombingType, LayerPolygon.MoveRetractionType]:
|
||||
line_widths[i - 1] = 0.2
|
||||
i += 1
|
||||
|
||||
this_poly = LayerPolygon(self._layer_data_builder, self._extruder, line_types, points, line_widths)
|
||||
this_poly.buildCache()
|
||||
|
||||
this_layer.polygons.append(this_poly)
|
||||
return True
|
||||
|
||||
def _gCode0(self, position, params, path):
|
||||
x, y, z, e = position
|
||||
x = params.x if params.x is not None else x
|
||||
y = params.y if params.y is not None else y
|
||||
z_changed = False
|
||||
if params.z is not None:
|
||||
if z != params.z:
|
||||
z_changed = True
|
||||
self._previous_z = z
|
||||
z = params.z
|
||||
if params.e is not None:
|
||||
if params.e > e[self._extruder]:
|
||||
path.append([x, y, z, self._layer_type]) # extrusion
|
||||
else:
|
||||
path.append([x, y, z, LayerPolygon.MoveRetractionType]) # retraction
|
||||
e[self._extruder] = params.e
|
||||
else:
|
||||
path.append([x, y, z, LayerPolygon.MoveCombingType])
|
||||
if z_changed:
|
||||
if not self._is_layers_in_file:
|
||||
if len(path) > 1 and z > 0:
|
||||
if self._createPolygon(z, path):
|
||||
self._layer += 1
|
||||
path.clear()
|
||||
else:
|
||||
path.clear()
|
||||
return self._position(x, y, z, e)
|
||||
|
||||
def _gCode28(self, position, params, path):
|
||||
return self._position(
|
||||
params.x if params.x is not None else position.x,
|
||||
params.y if params.y is not None else position.y,
|
||||
0,
|
||||
position.e)
|
||||
|
||||
def _gCode92(self, position, params, path):
|
||||
if params.e is not None:
|
||||
position.e[self._extruder] = params.e
|
||||
return self._position(
|
||||
params.x if params.x is not None else position.x,
|
||||
params.y if params.y is not None else position.y,
|
||||
params.z if params.z is not None else position.z,
|
||||
position.e)
|
||||
|
||||
_gCode1 = _gCode0
|
||||
|
||||
def _processGCode(self, G, line, position, path):
|
||||
func = getattr(self, "_gCode%s" % G, None)
|
||||
x = self._getFloat(line, "X")
|
||||
y = self._getFloat(line, "Y")
|
||||
z = self._getFloat(line, "Z")
|
||||
e = self._getFloat(line, "E")
|
||||
if func is not None:
|
||||
if (x is not None and x < 0) or (y is not None and y < 0):
|
||||
self._center_is_zero = True
|
||||
params = self._position(x, y, z, e)
|
||||
return func(position, params, path)
|
||||
return position
|
||||
|
||||
def _processTCode(self, T, line, position, path):
|
||||
self._extruder = T
|
||||
if self._extruder + 1 > len(position.e):
|
||||
position.e.extend([0] * (self._extruder - len(position.e) + 1))
|
||||
if not self._is_layers_in_file:
|
||||
if len(path) > 1 and position[2] > 0:
|
||||
if self._createPolygon(position[2], path):
|
||||
self._layer += 1
|
||||
path.clear()
|
||||
else:
|
||||
path.clear()
|
||||
return position
|
||||
|
||||
_type_keyword = ";TYPE:"
|
||||
_layer_keyword = ";LAYER:"
|
||||
|
||||
def read(self, file_name):
|
||||
Logger.log("d", "Preparing to load %s" % file_name)
|
||||
self._cancelled = False
|
||||
|
||||
scene_node = SceneNode()
|
||||
scene_node.getBoundingBox = self._getNullBoundingBox # Manually set bounding box, because mesh doesn't have mesh data
|
||||
|
||||
glist = []
|
||||
self._is_layers_in_file = False
|
||||
|
||||
|
||||
Logger.log("d", "Opening file %s" % file_name)
|
||||
|
||||
with open(file_name, "r") as file:
|
||||
file_lines = 0
|
||||
current_line = 0
|
||||
for line in file:
|
||||
file_lines += 1
|
||||
glist.append(line)
|
||||
if not self._is_layers_in_file and line[:len(self._layer_keyword)] == self._layer_keyword:
|
||||
self._is_layers_in_file = True
|
||||
file.seek(0)
|
||||
|
||||
file_step = max(math.floor(file_lines / 100), 1)
|
||||
|
||||
self._clearValues()
|
||||
|
||||
self._message = Message(catalog.i18nc("@info:status", "Parsing G-code"), lifetime=0)
|
||||
self._message.setProgress(0)
|
||||
self._message.show()
|
||||
|
||||
Logger.log("d", "Parsing %s" % file_name)
|
||||
|
||||
current_position = self._position(0, 0, 0, [0])
|
||||
current_path = []
|
||||
|
||||
for line in file:
|
||||
if self._cancelled:
|
||||
Logger.log("d", "Parsing %s cancelled" % file_name)
|
||||
return None
|
||||
current_line += 1
|
||||
if current_line % file_step == 0:
|
||||
self._message.setProgress(math.floor(current_line / file_lines * 100))
|
||||
if len(line) == 0:
|
||||
continue
|
||||
if line.find(self._type_keyword) == 0:
|
||||
type = line[len(self._type_keyword):].strip()
|
||||
if type == "WALL-INNER":
|
||||
self._layer_type = LayerPolygon.InsetXType
|
||||
elif type == "WALL-OUTER":
|
||||
self._layer_type = LayerPolygon.Inset0Type
|
||||
elif type == "SKIN":
|
||||
self._layer_type = LayerPolygon.SkinType
|
||||
elif type == "SKIRT":
|
||||
self._layer_type = LayerPolygon.SkirtType
|
||||
elif type == "SUPPORT":
|
||||
self._layer_type = LayerPolygon.SupportType
|
||||
elif type == "FILL":
|
||||
self._layer_type = LayerPolygon.InfillType
|
||||
if self._is_layers_in_file and line[:len(self._layer_keyword)] == self._layer_keyword:
|
||||
try:
|
||||
layer_number = int(line[len(self._layer_keyword):])
|
||||
self._createPolygon(current_position[2], current_path)
|
||||
current_path.clear()
|
||||
self._layer = layer_number
|
||||
except:
|
||||
pass
|
||||
if line[0] == ";":
|
||||
continue
|
||||
|
||||
G = self._getInt(line, "G")
|
||||
if G is not None:
|
||||
current_position = self._processGCode(G, line, current_position, current_path)
|
||||
T = self._getInt(line, "T")
|
||||
if T is not None:
|
||||
current_position = self._processTCode(T, line, current_position, current_path)
|
||||
|
||||
if not self._is_layers_in_file and len(current_path) > 1 and current_position[2] > 0:
|
||||
if self._createPolygon(current_position[2], current_path):
|
||||
self._layer += 1
|
||||
current_path.clear()
|
||||
|
||||
layer_mesh = self._layer_data_builder.build()
|
||||
decorator = LayerDataDecorator.LayerDataDecorator()
|
||||
decorator.setLayerData(layer_mesh)
|
||||
scene_node.addDecorator(decorator)
|
||||
|
||||
gcode_list_decorator = GCodeListDecorator()
|
||||
gcode_list_decorator.setGCodeList(glist)
|
||||
scene_node.addDecorator(gcode_list_decorator)
|
||||
|
||||
Logger.log("d", "Finished parsing %s" % file_name)
|
||||
self._message.hide()
|
||||
|
||||
if self._layer == 0:
|
||||
Logger.log("w", "File %s doesn't contain any valid layers" % file_name)
|
||||
|
||||
settings = Application.getInstance().getGlobalContainerStack()
|
||||
machine_width = settings.getProperty("machine_width", "value")
|
||||
machine_depth = settings.getProperty("machine_depth", "value")
|
||||
|
||||
if not self._center_is_zero:
|
||||
scene_node.setPosition(Vector(-machine_width / 2, 0, machine_depth / 2))
|
||||
|
||||
Logger.log("d", "Loaded %s" % file_name)
|
||||
|
||||
return scene_node
|
33
plugins/GCodeReader/__init__.py
Normal file
33
plugins/GCodeReader/__init__.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright (c) 2016 Aleph Objects, Inc.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import GCodeReader
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": i18n_catalog.i18nc("@label", "G-code Reader"),
|
||||
"author": "Victor Larchenko",
|
||||
"version": "1.0",
|
||||
"description": i18n_catalog.i18nc("@info:whatsthis", "Allows loading and displaying G-code files."),
|
||||
"api": 3
|
||||
},
|
||||
"mesh_reader": [
|
||||
{
|
||||
"extension": "gcode",
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "G-code File")
|
||||
},
|
||||
{
|
||||
"extension": "g",
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "G File")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def register(app):
|
||||
app.addNonSliceableExtension(".gcode")
|
||||
app.addNonSliceableExtension(".g")
|
||||
return { "mesh_reader": GCodeReader.GCodeReader() }
|
@ -45,10 +45,11 @@ class LayerPass(RenderPass):
|
||||
tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay)
|
||||
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
|
||||
if isinstance(node, ToolHandle):
|
||||
tool_handle_batch.addItem(node.getWorldTransformation(), mesh = node.getSolidMesh())
|
||||
|
||||
elif isinstance(node, SceneNode) and node.getMeshData() and node.isVisible():
|
||||
elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
|
||||
layer_data = node.callDecoration("getLayerData")
|
||||
if not layer_data:
|
||||
continue
|
||||
|
@ -119,7 +119,7 @@ class LayerView(View):
|
||||
continue
|
||||
|
||||
if not node.render(renderer):
|
||||
if node.getMeshData() and node.isVisible():
|
||||
if (node.getMeshData()) and node.isVisible():
|
||||
renderer.queueNode(node, transparent = True, shader = self._ghost_shader)
|
||||
|
||||
def setLayer(self, value):
|
||||
|
@ -1,15 +1,17 @@
|
||||
# Contributed by Seva Alekseyev <sevaa@nih.gov> with National Institutes of Health, 2016
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from math import pi, sin, cos, sqrt
|
||||
|
||||
import numpy
|
||||
|
||||
from UM.Job import Job
|
||||
from UM.Logger import Logger
|
||||
from UM.Math.Matrix import Matrix
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Job import Job
|
||||
from math import pi, sin, cos, sqrt
|
||||
import numpy
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as ET
|
||||
|
@ -269,16 +269,16 @@ UM.MainWindow
|
||||
if(drop.urls.length > 0)
|
||||
{
|
||||
// Import models
|
||||
var imported_model = -1;
|
||||
for(var i in drop.urls)
|
||||
{
|
||||
// There is no endsWith in this version of JS...
|
||||
if ((drop.urls[i].length <= 12) || (drop.urls[i].substring(drop.urls[i].length-12) !== ".curaprofile")) {
|
||||
// Drop an object
|
||||
UM.MeshFileHandler.readLocalFile(drop.urls[i]);
|
||||
if (i == drop.urls.length - 1)
|
||||
Printer.readLocalFile(drop.urls[i]);
|
||||
if (imported_model == -1)
|
||||
{
|
||||
var meshName = backgroundItem.getMeshName(drop.urls[i].toString());
|
||||
backgroundItem.hasMesh(decodeURIComponent(meshName));
|
||||
imported_model = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -297,6 +297,11 @@ UM.MainWindow
|
||||
}
|
||||
messageDialog.open()
|
||||
}
|
||||
if (imported_model != -1)
|
||||
{
|
||||
var meshName = backgroundItem.getMeshName(drop.urls[imported_model].toString())
|
||||
backgroundItem.hasMesh(decodeURIComponent(meshName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -406,7 +411,8 @@ UM.MainWindow
|
||||
iconSource: UM.Theme.getIcon("viewmode");
|
||||
|
||||
style: UM.Theme.styles.tool_button;
|
||||
tooltip: '';
|
||||
tooltip: "";
|
||||
enabled: !PrintInformation.preSliced
|
||||
menu: ViewMenu { }
|
||||
}
|
||||
|
||||
@ -731,14 +737,11 @@ UM.MainWindow
|
||||
|
||||
for(var i in fileUrls)
|
||||
{
|
||||
UM.MeshFileHandler.readLocalFile(fileUrls[i])
|
||||
|
||||
if (i == fileUrls.length - 1)
|
||||
{
|
||||
var meshName = backgroundItem.getMeshName(fileUrls.toString())
|
||||
backgroundItem.hasMesh(decodeURIComponent(meshName))
|
||||
}
|
||||
Printer.readLocalFile(fileUrls[i])
|
||||
}
|
||||
|
||||
var meshName = backgroundItem.getMeshName(fileUrls[0].toString())
|
||||
backgroundItem.hasMesh(decodeURIComponent(meshName))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ Menu
|
||||
return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1);
|
||||
}
|
||||
onTriggered: {
|
||||
UM.MeshFileHandler.readLocalFile(modelData);
|
||||
Printer.readLocalFile(modelData);
|
||||
var meshName = backgroundItem.getMeshName(modelData.toString())
|
||||
backgroundItem.hasMesh(decodeURIComponent(meshName))
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ Menu
|
||||
{
|
||||
title: catalog.i18nc("@title:menu menubar:toplevel", "&View");
|
||||
id: menu
|
||||
enabled: !PrintInformation.preSliced
|
||||
Instantiator
|
||||
{
|
||||
model: UM.ViewModel { }
|
||||
|
@ -34,6 +34,8 @@ Rectangle {
|
||||
return catalog.i18nc("@label:PrintjobStatus %1 is target operation","Ready to %1").arg(UM.OutputDeviceManager.activeDeviceShortDescription);
|
||||
case 4:
|
||||
return catalog.i18nc("@label:PrintjobStatus", "Unable to Slice");
|
||||
case 5:
|
||||
return catalog.i18nc("@label:PrintjobStatus", "Slicing unavailable");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
@ -104,7 +106,7 @@ Rectangle {
|
||||
id: saveToButton
|
||||
|
||||
tooltip: UM.OutputDeviceManager.activeDeviceDescription;
|
||||
enabled: base.backendState == 3 && base.activity == true
|
||||
enabled: (base.backendState == 3 || base.backendState == 5) && base.activity == true
|
||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||
|
||||
anchors.top: parent.top
|
||||
@ -179,7 +181,7 @@ Rectangle {
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
width: UM.Theme.getSize("save_button_save_to_button").height
|
||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||
enabled: base.backendState == 3 && base.activity == true
|
||||
enabled: (base.backendState == 3 || base.backendState == 5) && base.activity == true
|
||||
visible: devicesModel.deviceCount > 1
|
||||
|
||||
|
||||
|
@ -16,6 +16,7 @@ Rectangle
|
||||
|
||||
property int currentModeIndex;
|
||||
property bool monitoringPrint: false
|
||||
property bool hideSettings: PrintInformation.preSliced
|
||||
Connections
|
||||
{
|
||||
target: Printer
|
||||
@ -288,7 +289,7 @@ Rectangle
|
||||
|
||||
Label {
|
||||
id: settingsModeLabel
|
||||
text: catalog.i18nc("@label:listbox", "Print Setup");
|
||||
text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified");
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width;
|
||||
anchors.top: headerSeparator.bottom
|
||||
@ -308,7 +309,7 @@ Rectangle
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.top: headerSeparator.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
visible: !monitoringPrint
|
||||
visible: !monitoringPrint && !hideSettings
|
||||
Component{
|
||||
id: wizardDelegate
|
||||
Button {
|
||||
@ -384,7 +385,7 @@ Rectangle
|
||||
height: settingsModeSelection.height
|
||||
width: visible ? height : 0
|
||||
|
||||
visible: !monitoringPrint && modesListModel.get(base.currentModeIndex) != undefined && modesListModel.get(base.currentModeIndex).showFilterButton
|
||||
visible: !monitoringPrint && !hideSettings && modesListModel.get(base.currentModeIndex) != undefined && modesListModel.get(base.currentModeIndex).showFilterButton
|
||||
opacity: visible ? 1 : 0
|
||||
|
||||
onClicked: sidebarContents.currentItem.toggleFilterField()
|
||||
@ -432,7 +433,7 @@ Rectangle
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: base.left
|
||||
anchors.right: base.right
|
||||
visible: !monitoringPrint
|
||||
visible: !monitoringPrint && !hideSettings
|
||||
|
||||
delegate: StackViewDelegate
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user