diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py new file mode 100644 index 0000000000..aed68e2c3b --- /dev/null +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -0,0 +1,231 @@ +from UM.Backend.Backend import Backend +from UM.Application import Application +from UM.Scene.SceneNode import SceneNode +from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator +from UM.Preferences import Preferences +from UM.Math.Vector import Vector +from UM.Signal import Signal +from UM.Logger import Logger +from UM.Resources import Resources + +from . import Cura_pb2 +from . import ProcessSlicedObjectListJob +from . import ProcessGCodeJob + +import os +import sys +import numpy + +from PyQt5.QtCore import QTimer + +class CuraEngineBackend(Backend): + def __init__(self): + super().__init__() + + # Find out where the engine is located, and how it is called. This depends on how Cura is packaged and which OS we are running on. + default_engine_location = '../PinkUnicornEngine/CuraEngine' + if hasattr(sys, 'frozen'): + default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), 'CuraEngine') + if sys.platform == 'win32': + default_engine_location += '.exe' + default_engine_location = os.path.abspath(default_engine_location) + Preferences.getInstance().addPreference('backend/location', default_engine_location) + + self._scene = Application.getInstance().getController().getScene() + self._scene.sceneChanged.connect(self._onSceneChanged) + + self._settings = None + Application.getInstance().activeMachineChanged.connect(self._onActiveMachineChanged) + self._onActiveMachineChanged() + + self._change_timer = QTimer() + self._change_timer.setInterval(500) + self._change_timer.setSingleShot(True) + self._change_timer.timeout.connect(self.slice) + + self._message_handlers[Cura_pb2.SlicedObjectList] = self._onSlicedObjectListMessage + self._message_handlers[Cura_pb2.Progress] = self._onProgressMessage + self._message_handlers[Cura_pb2.GCodeLayer] = self._onGCodeLayerMessage + self._message_handlers[Cura_pb2.GCodePrefix] = self._onGCodePrefixMessage + self._message_handlers[Cura_pb2.ObjectPrintTime] = self._onObjectPrintTimeMessage + + self._slicing = False + self._restart = False + + self._save_gcode = True + self._save_polygons = True + self._report_progress = True + + self.backendConnected.connect(self._onBackendConnected) + + def getEngineCommand(self): + return [Preferences.getInstance().getValue("backend/location"), '-j', Resources.getPath(Resources.SettingsLocation, 'fdmprinter.json'), '-vv', '--connect', "127.0.0.1:{0}".format(self._port)] + + ## Emitted when we get a message containing print duration and material amount. This also implies the slicing has finished. + # \param time The amount of time the print will take. + # \param material_amount The amount of material the print will use. + printDurationMessage = Signal() + + ## Emitted when the slicing process starts. + slicingStarted = Signal() + + ## Emitted whne the slicing process is aborted forcefully. + slicingCancelled = Signal() + + ## Perform a slice of the scene with the given set of settings. + # + # \param kwargs Keyword arguments. + # Valid values are: + # - settings: The settings to use for the slice. The default is the active machine. + # - save_gcode: True if the generated gcode should be saved, False if not. True by default. + # - save_polygons: True if the generated polygon data should be saved, False if not. True by default. + # - force_restart: True if the slicing process should be forcefully restarted if it is already slicing. + # If False, this method will do nothing when already slicing. True by default. + # - report_progress: True if the slicing progress should be reported, False if not. Default is True. + def slice(self, **kwargs): + if self._slicing: + if not kwargs.get('force_restart', True): + return + + self._slicing = False + self._restart = True + if self._process is not None: + Logger.log('d', "Killing engine process") + try: + self._process.terminate() + except: # terminating a process that is already terminating causes an exception, silently ignore this. + pass + self.slicingCancelled.emit() + return + + objects = [] + for node in DepthFirstIterator(self._scene.getRoot()): + if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: + if not getattr(node, '_outside_buildarea', False): + objects.append(node) + + if not objects: + return #No point in slicing an empty build plate + + self._slicing = True + self.slicingStarted.emit() + + self._report_progress = kwargs.get('report_progress', True) + if self._report_progress: + self.processingProgress.emit(0.0) + + self._sendSettings(kwargs.get('settings', self._settings)) + + self._scene.acquireLock() + + # Set the gcode as an empty list. This will be filled with strings by GCodeLayer messages. + # This is done so the gcode can be fragmented in memory and does not need a continues memory space. + # (AKA. This prevents MemoryErrors) + self._save_gcode = kwargs.get('save_gcode', True) + if self._save_gcode: + setattr(self._scene, 'gcode_list', []) + + self._save_polygons = kwargs.get('save_polygons', True) + + msg = Cura_pb2.ObjectList() + + #TODO: All at once/one at a time mode + center = Vector() + for object in objects: + center += object.getPosition() + + mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation()) + + obj = msg.objects.add() + obj.id = id(object) + + verts = numpy.array(mesh_data.getVertices(), copy=True) + verts[:,[1,2]] = verts[:,[2,1]] + verts[:,1] *= -1 + obj.vertices = verts.tostring() + + #if meshData.hasNormals(): + #obj.normals = meshData.getNormalsAsByteArray() + + #if meshData.hasIndices(): + #obj.indices = meshData.getIndicesAsByteArray() + + self._scene.releaseLock() + + self._socket.sendMessage(msg) + + def _onSceneChanged(self, source): + if (type(source) is not SceneNode) or (source is self._scene.getRoot()) or (source.getMeshData() is None): + return + + if(source.getMeshData().getVertices() is None): + return + + self._onChanged() + + def _onActiveMachineChanged(self): + if self._settings: + self._settings.settingChanged.disconnect(self._onSettingChanged) + + self._settings = Application.getInstance().getActiveMachine() + if self._settings: + self._settings.settingChanged.connect(self._onSettingChanged) + self._onChanged() + + def _onSettingChanged(self, setting): + self._onChanged() + + def _onSlicedObjectListMessage(self, message): + if self._save_polygons: + job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message) + job.start() + + def _onProgressMessage(self, message): + if message.amount >= 0.99: + self._slicing = False + + if self._report_progress: + self.processingProgress.emit(message.amount) + + def _onGCodeLayerMessage(self, message): + if self._save_gcode: + job = ProcessGCodeJob.ProcessGCodeLayerJob(message) + job.start() + + def _onGCodePrefixMessage(self, message): + if self._save_gcode: + self._scene.gcode_list.insert(0, message.data.decode('utf-8', 'replace')) + + def _onObjectPrintTimeMessage(self, message): + self.printDurationMessage.emit(message.time, message.material_amount) + + def _createSocket(self): + super()._createSocket() + + self._socket.registerMessageType(1, Cura_pb2.ObjectList) + self._socket.registerMessageType(2, Cura_pb2.SlicedObjectList) + self._socket.registerMessageType(3, Cura_pb2.Progress) + self._socket.registerMessageType(4, Cura_pb2.GCodeLayer) + self._socket.registerMessageType(5, Cura_pb2.ObjectPrintTime) + self._socket.registerMessageType(6, Cura_pb2.SettingList) + self._socket.registerMessageType(7, Cura_pb2.GCodePrefix) + + def _onChanged(self): + if not self._settings: + return + + self._change_timer.start() + + def _sendSettings(self, settings): + msg = Cura_pb2.SettingList() + for setting in settings.getAllSettings(include_machine=True): + s = msg.settings.add() + s.name = setting.getKey() + s.value = str(setting.getValue()).encode('utf-8') + + self._socket.sendMessage(msg) + + def _onBackendConnected(self): + if self._restart: + self._onChanged() + self._restart = False diff --git a/plugins/CuraEngineBackend/Cura_pb2.py b/plugins/CuraEngineBackend/Cura_pb2.py new file mode 100644 index 0000000000..c577ef0f71 --- /dev/null +++ b/plugins/CuraEngineBackend/Cura_pb2.py @@ -0,0 +1,609 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: Cura.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='Cura.proto', + package='Cura', + serialized_pb=_b('\n\nCura.proto\x12\x04\x43ura\"+\n\nObjectList\x12\x1d\n\x07objects\x18\x01 \x03(\x0b\x32\x0c.Cura.Object\"i\n\x06Object\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x10\n\x08vertices\x18\x02 \x01(\x0c\x12\x0f\n\x07normals\x18\x03 \x01(\x0c\x12\x0f\n\x07indices\x18\x04 \x01(\x0c\x12\x1f\n\x08settings\x18\x05 \x03(\x0b\x32\r.Cura.Setting\"\x1a\n\x08Progress\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x02\"7\n\x10SlicedObjectList\x12#\n\x07objects\x18\x01 \x03(\x0b\x32\x12.Cura.SlicedObject\"7\n\x0cSlicedObject\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x1b\n\x06layers\x18\x02 \x03(\x0b\x32\x0b.Cura.Layer\"4\n\x05Layer\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x1f\n\x08polygons\x18\x02 \x03(\x0b\x32\r.Cura.Polygon\"\x9f\x01\n\x07Polygon\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.Cura.Polygon.Type\x12\x0e\n\x06points\x18\x02 \x01(\x0c\"b\n\x04Type\x12\x0c\n\x08NoneType\x10\x00\x12\x0e\n\nInset0Type\x10\x01\x12\x0e\n\nInsetXType\x10\x02\x12\x0c\n\x08SkinType\x10\x03\x12\x0f\n\x0bSupportType\x10\x04\x12\r\n\tSkirtType\x10\x05\"&\n\nGCodeLayer\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"D\n\x0fObjectPrintTime\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04time\x18\x02 \x01(\x02\x12\x17\n\x0fmaterial_amount\x18\x03 \x01(\x02\".\n\x0bSettingList\x12\x1f\n\x08settings\x18\x01 \x03(\x0b\x32\r.Cura.Setting\"&\n\x07Setting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\"\x1b\n\x0bGCodePrefix\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x62\x06proto3') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + +_POLYGON_TYPE = _descriptor.EnumDescriptor( + name='Type', + full_name='Cura.Polygon.Type', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='NoneType', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='Inset0Type', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='InsetXType', index=2, number=2, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='SkinType', index=3, number=3, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='SupportType', index=4, number=4, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='SkirtType', index=5, number=5, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=430, + serialized_end=528, +) +_sym_db.RegisterEnumDescriptor(_POLYGON_TYPE) + + +_OBJECTLIST = _descriptor.Descriptor( + name='ObjectList', + full_name='Cura.ObjectList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='objects', full_name='Cura.ObjectList.objects', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=20, + serialized_end=63, +) + + +_OBJECT = _descriptor.Descriptor( + name='Object', + full_name='Cura.Object', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='Cura.Object.id', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='vertices', full_name='Cura.Object.vertices', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='normals', full_name='Cura.Object.normals', index=2, + number=3, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='indices', full_name='Cura.Object.indices', index=3, + number=4, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='settings', full_name='Cura.Object.settings', index=4, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=65, + serialized_end=170, +) + + +_PROGRESS = _descriptor.Descriptor( + name='Progress', + full_name='Cura.Progress', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='amount', full_name='Cura.Progress.amount', index=0, + number=1, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=172, + serialized_end=198, +) + + +_SLICEDOBJECTLIST = _descriptor.Descriptor( + name='SlicedObjectList', + full_name='Cura.SlicedObjectList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='objects', full_name='Cura.SlicedObjectList.objects', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=200, + serialized_end=255, +) + + +_SLICEDOBJECT = _descriptor.Descriptor( + name='SlicedObject', + full_name='Cura.SlicedObject', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='Cura.SlicedObject.id', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='layers', full_name='Cura.SlicedObject.layers', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=257, + serialized_end=312, +) + + +_LAYER = _descriptor.Descriptor( + name='Layer', + full_name='Cura.Layer', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='Cura.Layer.id', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='polygons', full_name='Cura.Layer.polygons', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=314, + serialized_end=366, +) + + +_POLYGON = _descriptor.Descriptor( + name='Polygon', + full_name='Cura.Polygon', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='type', full_name='Cura.Polygon.type', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='points', full_name='Cura.Polygon.points', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _POLYGON_TYPE, + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=369, + serialized_end=528, +) + + +_GCODELAYER = _descriptor.Descriptor( + name='GCodeLayer', + full_name='Cura.GCodeLayer', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='Cura.GCodeLayer.id', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='data', full_name='Cura.GCodeLayer.data', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=530, + serialized_end=568, +) + + +_OBJECTPRINTTIME = _descriptor.Descriptor( + name='ObjectPrintTime', + full_name='Cura.ObjectPrintTime', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='Cura.ObjectPrintTime.id', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='time', full_name='Cura.ObjectPrintTime.time', index=1, + number=2, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='material_amount', full_name='Cura.ObjectPrintTime.material_amount', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=570, + serialized_end=638, +) + + +_SETTINGLIST = _descriptor.Descriptor( + name='SettingList', + full_name='Cura.SettingList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='settings', full_name='Cura.SettingList.settings', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=640, + serialized_end=686, +) + + +_SETTING = _descriptor.Descriptor( + name='Setting', + full_name='Cura.Setting', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='Cura.Setting.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='value', full_name='Cura.Setting.value', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=688, + serialized_end=726, +) + + +_GCODEPREFIX = _descriptor.Descriptor( + name='GCodePrefix', + full_name='Cura.GCodePrefix', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='data', full_name='Cura.GCodePrefix.data', index=0, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=728, + serialized_end=755, +) + +_OBJECTLIST.fields_by_name['objects'].message_type = _OBJECT +_OBJECT.fields_by_name['settings'].message_type = _SETTING +_SLICEDOBJECTLIST.fields_by_name['objects'].message_type = _SLICEDOBJECT +_SLICEDOBJECT.fields_by_name['layers'].message_type = _LAYER +_LAYER.fields_by_name['polygons'].message_type = _POLYGON +_POLYGON.fields_by_name['type'].enum_type = _POLYGON_TYPE +_POLYGON_TYPE.containing_type = _POLYGON +_SETTINGLIST.fields_by_name['settings'].message_type = _SETTING +DESCRIPTOR.message_types_by_name['ObjectList'] = _OBJECTLIST +DESCRIPTOR.message_types_by_name['Object'] = _OBJECT +DESCRIPTOR.message_types_by_name['Progress'] = _PROGRESS +DESCRIPTOR.message_types_by_name['SlicedObjectList'] = _SLICEDOBJECTLIST +DESCRIPTOR.message_types_by_name['SlicedObject'] = _SLICEDOBJECT +DESCRIPTOR.message_types_by_name['Layer'] = _LAYER +DESCRIPTOR.message_types_by_name['Polygon'] = _POLYGON +DESCRIPTOR.message_types_by_name['GCodeLayer'] = _GCODELAYER +DESCRIPTOR.message_types_by_name['ObjectPrintTime'] = _OBJECTPRINTTIME +DESCRIPTOR.message_types_by_name['SettingList'] = _SETTINGLIST +DESCRIPTOR.message_types_by_name['Setting'] = _SETTING +DESCRIPTOR.message_types_by_name['GCodePrefix'] = _GCODEPREFIX + +ObjectList = _reflection.GeneratedProtocolMessageType('ObjectList', (_message.Message,), dict( + DESCRIPTOR = _OBJECTLIST, + __module__ = 'Cura_pb2' + # @@protoc_insertion_point(class_scope:Cura.ObjectList) + )) +_sym_db.RegisterMessage(ObjectList) + +Object = _reflection.GeneratedProtocolMessageType('Object', (_message.Message,), dict( + DESCRIPTOR = _OBJECT, + __module__ = 'Cura_pb2' + # @@protoc_insertion_point(class_scope:Cura.Object) + )) +_sym_db.RegisterMessage(Object) + +Progress = _reflection.GeneratedProtocolMessageType('Progress', (_message.Message,), dict( + DESCRIPTOR = _PROGRESS, + __module__ = 'Cura_pb2' + # @@protoc_insertion_point(class_scope:Cura.Progress) + )) +_sym_db.RegisterMessage(Progress) + +SlicedObjectList = _reflection.GeneratedProtocolMessageType('SlicedObjectList', (_message.Message,), dict( + DESCRIPTOR = _SLICEDOBJECTLIST, + __module__ = 'Cura_pb2' + # @@protoc_insertion_point(class_scope:Cura.SlicedObjectList) + )) +_sym_db.RegisterMessage(SlicedObjectList) + +SlicedObject = _reflection.GeneratedProtocolMessageType('SlicedObject', (_message.Message,), dict( + DESCRIPTOR = _SLICEDOBJECT, + __module__ = 'Cura_pb2' + # @@protoc_insertion_point(class_scope:Cura.SlicedObject) + )) +_sym_db.RegisterMessage(SlicedObject) + +Layer = _reflection.GeneratedProtocolMessageType('Layer', (_message.Message,), dict( + DESCRIPTOR = _LAYER, + __module__ = 'Cura_pb2' + # @@protoc_insertion_point(class_scope:Cura.Layer) + )) +_sym_db.RegisterMessage(Layer) + +Polygon = _reflection.GeneratedProtocolMessageType('Polygon', (_message.Message,), dict( + DESCRIPTOR = _POLYGON, + __module__ = 'Cura_pb2' + # @@protoc_insertion_point(class_scope:Cura.Polygon) + )) +_sym_db.RegisterMessage(Polygon) + +GCodeLayer = _reflection.GeneratedProtocolMessageType('GCodeLayer', (_message.Message,), dict( + DESCRIPTOR = _GCODELAYER, + __module__ = 'Cura_pb2' + # @@protoc_insertion_point(class_scope:Cura.GCodeLayer) + )) +_sym_db.RegisterMessage(GCodeLayer) + +ObjectPrintTime = _reflection.GeneratedProtocolMessageType('ObjectPrintTime', (_message.Message,), dict( + DESCRIPTOR = _OBJECTPRINTTIME, + __module__ = 'Cura_pb2' + # @@protoc_insertion_point(class_scope:Cura.ObjectPrintTime) + )) +_sym_db.RegisterMessage(ObjectPrintTime) + +SettingList = _reflection.GeneratedProtocolMessageType('SettingList', (_message.Message,), dict( + DESCRIPTOR = _SETTINGLIST, + __module__ = 'Cura_pb2' + # @@protoc_insertion_point(class_scope:Cura.SettingList) + )) +_sym_db.RegisterMessage(SettingList) + +Setting = _reflection.GeneratedProtocolMessageType('Setting', (_message.Message,), dict( + DESCRIPTOR = _SETTING, + __module__ = 'Cura_pb2' + # @@protoc_insertion_point(class_scope:Cura.Setting) + )) +_sym_db.RegisterMessage(Setting) + +GCodePrefix = _reflection.GeneratedProtocolMessageType('GCodePrefix', (_message.Message,), dict( + DESCRIPTOR = _GCODEPREFIX, + __module__ = 'Cura_pb2' + # @@protoc_insertion_point(class_scope:Cura.GCodePrefix) + )) +_sym_db.RegisterMessage(GCodePrefix) + + +# @@protoc_insertion_point(module_scope) diff --git a/plugins/CuraEngineBackend/LayerData.py b/plugins/CuraEngineBackend/LayerData.py new file mode 100644 index 0000000000..33ec83cd81 --- /dev/null +++ b/plugins/CuraEngineBackend/LayerData.py @@ -0,0 +1,90 @@ +from UM.Mesh.MeshData import MeshData +from UM.Math.Color import Color + +import numpy +import math + +class LayerData(MeshData): + def __init__(self): + super().__init__() + self._layers = {} + self._element_counts = {} + + def addPolygon(self, layer, type, data): + if layer not in self._layers: + self._layers[layer] = [] + + p = Polygon(self, type, data) + self._layers[layer].append(p) + + def getLayers(self): + return self._layers + + def getElementCounts(self): + return self._element_counts + + def build(self): + for layer, data in self._layers.items(): + if layer not in self._element_counts: + self._element_counts[layer] = [] + + for polygon in data: + polygon.build() + self._element_counts[layer].append(polygon.elementCount) + +class Polygon(): + NoneType = 0 + Inset0Type = 1 + InsetXType = 2 + SkinType = 3 + SupportType = 4 + SkirtType = 5 + + def __init__(self, mesh, type, data): + super().__init__() + self._mesh = mesh + self._type = type + self._data = data + + def build(self): + self._begin = self._mesh._vertex_count + self._mesh.addVertices(self._data) + self._end = self._begin + len(self._data) - 1 + + color = None + if self._type == self.Inset0Type: + color = [1, 0, 0, 1] + elif self._type == self.InsetXType: + color = [0, 1, 0, 1] + elif self._type == self.SkinType: + color = [1, 1, 0, 1] + elif self._type == self.SupportType: + color = [0, 1, 1, 1] + elif self._type == self.SkirtType: + color = [0, 1, 1, 1] + else: + color = [1, 1, 1, 1] + + colors = [color for i in range(len(self._data))] + self._mesh.addColors(numpy.array(colors, dtype=numpy.float32)) + + indices = [] + for i in range(self._begin, self._end): + indices.append(i) + indices.append(i + 1) + + indices.append(self._end) + indices.append(self._begin) + self._mesh.addIndices(numpy.array(indices, dtype=numpy.int32)) + + @property + def type(self): + return self._type + + @property + def data(self): + return self._data + + @property + def elementCount(self): + return (self._end - self._begin) * 2 #The range of vertices multiplied by 2 since each vertex is used twice diff --git a/plugins/CuraEngineBackend/ProcessGCodeJob.py b/plugins/CuraEngineBackend/ProcessGCodeJob.py new file mode 100644 index 0000000000..4da385a58d --- /dev/null +++ b/plugins/CuraEngineBackend/ProcessGCodeJob.py @@ -0,0 +1,13 @@ +from UM.Job import Job +from UM.Application import Application + + +class ProcessGCodeLayerJob(Job): + def __init__(self, message): + super().__init__() + + self._scene = Application.getInstance().getController().getScene() + self._message = message + + def run(self): + self._scene.gcode_list.append(self._message.data.decode('utf-8', 'replace')) diff --git a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py new file mode 100644 index 0000000000..2938dde0a9 --- /dev/null +++ b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py @@ -0,0 +1,67 @@ +from UM.Job import Job +from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator +from UM.Scene.SceneNode import SceneNode +from UM.Application import Application +from UM.Mesh.MeshData import MeshData + +from . import LayerData + +import numpy +import struct + +class ProcessSlicedObjectListJob(Job): + def __init__(self, message): + super().__init__(description = 'Processing sliced object') + self._message = message + self._scene = Application.getInstance().getController().getScene() + + def run(self): + objectIdMap = {} + new_node = SceneNode() + ## Put all nodes in a dict identified by ID + for node in DepthFirstIterator(self._scene.getRoot()): + if type(node) is SceneNode and node.getMeshData(): + if hasattr(node.getMeshData(), "layerData"): + self._scene.getRoot().removeChild(node) + else: + objectIdMap[id(node)] = node + + settings = Application.getInstance().getActiveMachine() + layerHeight = settings.getSettingValueByKey('layer_height') + + for object in self._message.objects: + try: + node = objectIdMap[object.id] + except KeyError: + continue + + mesh = MeshData() + + layerData = LayerData.LayerData() + for layer in object.layers: + for polygon in layer.polygons: + points = numpy.fromstring(polygon.points, dtype='i8') # Convert bytearray to numpy array + points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly. + points = numpy.asarray(points, dtype=numpy.float32) + points /= 1000 + points = numpy.insert(points, 1, layer.id * layerHeight, axis = 1) + + points[:,2] *= -1 + + if not settings.getSettingValueByKey('machine_center_is_zero'): + center = [settings.getSettingValueByKey('machine_width') / 2, 0.0, -settings.getSettingValueByKey('machine_depth') / 2] + points -= numpy.array(center) + + #points = numpy.pad(points, ((0,0), (0,1)), 'constant', constant_values=(0.0, 1.0)) + #inverse = node.getWorldTransformation().getInverse().getData() + #points = points.dot(inverse) + #points = points[:,0:3] + + layerData.addPolygon(layer.id, polygon.type, points) + + # We are done processing all the layers we got from the engine, now create a mesh out of the data + layerData.build() + mesh.layerData = layerData + + new_node.setMeshData(mesh) + new_node.setParent(self._scene.getRoot()) diff --git a/plugins/CuraEngineBackend/__init__.py b/plugins/CuraEngineBackend/__init__.py new file mode 100644 index 0000000000..675cde091c --- /dev/null +++ b/plugins/CuraEngineBackend/__init__.py @@ -0,0 +1,14 @@ +#Shoopdawoop +from . import CuraEngineBackend + +def getMetaData(): + return { + 'type': 'backend', + 'plugin': { + 'name': "CuraEngine Backend" + } + } + +def register(app): + return {"backend":CuraEngineBackend.CuraEngineBackend()} +