diff --git a/cura/LayerData.py b/cura/LayerData.py index 1cf13a1798..421b2589cb 100644 --- a/cura/LayerData.py +++ b/cura/LayerData.py @@ -131,7 +131,7 @@ class Layer(): continue if not make_mesh and not (polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType): continue - + poly_color = polygon.getColor() points = numpy.copy(polygon.data) @@ -140,26 +140,7 @@ class Layer(): if polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType: points[:,1] += 0.01 - # Calculate normals for the entire polygon using numpy. - normals = numpy.copy(points) - normals[:,1] = 0.0 # We are only interested in 2D normals - - # Calculate the edges between points. - # The call to numpy.roll shifts the entire array by one so that - # we end up subtracting each next point from the current, wrapping - # around. This gives us the edges from the next point to the current - # point. - normals[:] = normals[:] - numpy.roll(normals, -1, axis = 0) - # Calculate the length of each edge using standard Pythagoras - lengths = numpy.sqrt(normals[:,0] ** 2 + normals[:,2] ** 2) - # The normal of a 2D vector is equal to its x and y coordinates swapped - # and then x inverted. This code does that. - normals[:,[0, 2]] = normals[:,[2, 0]] - normals[:,0] *= -1 - - # Normalize the normals. - normals[:,0] /= lengths - normals[:,2] /= lengths + normals = polygon.getNormals() # Scale all by the line width of the polygon so we can easily offset. normals *= (polygon.lineWidth / 2) @@ -199,16 +180,33 @@ class Polygon(): self._data = data self._line_width = line_width / 1000 + if type == self.Inset0Type: + self._color = Color(1.0, 0.0, 0.0, 1.0) + elif self._type == self.InsetXType: + self._color = Color(0.0, 1.0, 0.0, 1.0) + elif self._type == self.SkinType: + self._color = Color(1.0, 1.0, 0.0, 1.0) + elif self._type == self.SupportType: + self._color = Color(0.0, 1.0, 1.0, 1.0) + elif self._type == self.SkirtType: + self._color = Color(0.0, 1.0, 1.0, 1.0) + elif self._type == self.InfillType: + self._color = Color(1.0, 0.74, 0.0, 1.0) + elif self._type == self.SupportInfillType: + self._color = Color(0.0, 1.0, 1.0, 1.0) + elif self._type == self.MoveCombingType: + self._color = Color(0.0, 0.0, 1.0, 1.0) + elif self._type == self.MoveRetractionType: + self._color = Color(0.5, 0.5, 1.0, 1.0) + else: + self._color = Color(1.0, 1.0, 1.0, 1.0) + def build(self, offset, vertices, colors, indices): self._begin = offset self._end = self._begin + len(self._data) - 1 - color = self.getColor() - color.setValues(color.r * 0.5, color.g * 0.5, color.b * 0.5, color.a) - color = numpy.array([color.r, color.g, color.b, color.a], numpy.float32) - vertices[self._begin:self._end + 1, :] = self._data[:, :] - colors[self._begin:self._end + 1, :] = color + colors[self._begin:self._end + 1, :] = numpy.array([self._color.r * 0.5, self._color.g * 0.5, self._color.b * 0.5, self._color.a], numpy.float32) for i in range(self._begin, self._end): indices[i, 0] = i @@ -218,26 +216,7 @@ class Polygon(): indices[self._end, 1] = self._begin def getColor(self): - if self._type == self.Inset0Type: - return Color(1.0, 0.0, 0.0, 1.0) - elif self._type == self.InsetXType: - return Color(0.0, 1.0, 0.0, 1.0) - elif self._type == self.SkinType: - return Color(1.0, 1.0, 0.0, 1.0) - elif self._type == self.SupportType: - return Color(0.0, 1.0, 1.0, 1.0) - elif self._type == self.SkirtType: - return Color(0.0, 1.0, 1.0, 1.0) - elif self._type == self.InfillType: - return Color(1.0, 0.74, 0.0, 1.0) - elif self._type == self.SupportInfillType: - return Color(0.0, 1.0, 1.0, 1.0) - elif self._type == self.MoveCombingType: - return Color(0.0, 0.0, 1.0, 1.0) - elif self._type == self.MoveRetractionType: - return Color(0.5, 0.5, 1.0, 1.0) - else: - return Color(1.0, 1.0, 1.0, 1.0) + return self._color def vertexCount(self): return len(self._data) @@ -257,3 +236,27 @@ class Polygon(): @property def lineWidth(self): return self._line_width + + # Calculate normals for the entire polygon using numpy. + def getNormals(self): + normals = numpy.copy(self._data) + normals[:,1] = 0.0 # We are only interested in 2D normals + + # Calculate the edges between points. + # The call to numpy.roll shifts the entire array by one so that + # we end up subtracting each next point from the current, wrapping + # around. This gives us the edges from the next point to the current + # point. + normals[:] = normals[:] - numpy.roll(normals, -1, axis = 0) + # Calculate the length of each edge using standard Pythagoras + lengths = numpy.sqrt(normals[:,0] ** 2 + normals[:,2] ** 2) + # The normal of a 2D vector is equal to its x and y coordinates swapped + # and then x inverted. This code does that. + normals[:,[0, 2]] = normals[:,[2, 0]] + normals[:,0] *= -1 + + # Normalize the normals. + normals[:,0] /= lengths + normals[:,2] /= lengths + + return normals diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto new file mode 100644 index 0000000000..39fa3a56ae --- /dev/null +++ b/plugins/CuraEngineBackend/Cura.proto @@ -0,0 +1,104 @@ +syntax = "proto3"; + +package cura.proto; + + +message ObjectList +{ + repeated Object objects = 1; + repeated Setting settings = 2; +} + +// typeid 1 +message Slice +{ + repeated ObjectList object_lists = 1; +} + +message Object +{ + int64 id = 1; + bytes vertices = 2; //An array of 3 floats. + bytes normals = 3; //An array of 3 floats. + bytes indices = 4; //An array of ints. + repeated Setting settings = 5; // Setting override per object, overruling the global settings. +} + +// typeid 3 +message Progress +{ + float amount = 1; +} + +// typeid 2 +message SlicedObjectList +{ + repeated SlicedObject objects = 1; +} + +message SlicedObject +{ + int64 id = 1; + + repeated Layer layers = 2; +} + +message Layer { + int32 id = 1; + + float height = 2; + float thickness = 3; + + repeated Polygon polygons = 4; +} + +message Polygon { + enum Type { + NoneType = 0; + Inset0Type = 1; + InsetXType = 2; + SkinType = 3; + SupportType = 4; + SkirtType = 5; + InfillType = 6; + SupportInfillType = 7; + MoveCombingType = 8; + MoveRetractionType = 9; + } + Type type = 1; + bytes points = 2; + float line_width = 3; +} + +// typeid 4 +message GCodeLayer { + int64 id = 1; + bytes data = 2; +} + +// typeid 5 +message ObjectPrintTime { + int64 id = 1; + float time = 2; + float material_amount = 3; +} + +// typeid 6 +message SettingList { + repeated Setting settings = 1; +} + +message Setting { + string name = 1; + + bytes value = 2; +} + +// typeid 7 +message GCodePrefix { + bytes data = 2; +} + +// typeid 8 +message SlicingFinished { +} diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 847268627c..5bba967428 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -13,6 +13,7 @@ from UM.Qt.Bindings.BackendProxy import BackendState #To determine the state of from UM.Resources import Resources from UM.Settings.SettingOverrideDecorator import SettingOverrideDecorator from UM.Message import Message +from UM.PluginRegistry import PluginRegistry from cura.OneAtATimeIterator import OneAtATimeIterator from . import Cura_pb2 @@ -62,12 +63,12 @@ class CuraEngineBackend(Backend): 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._message_handlers[Cura_pb2.SlicingFinished] = self._onSlicingFinishedMessage + self._message_handlers["cura.proto.SlicedObjectList"] = self._onSlicedObjectListMessage + self._message_handlers["cura.proto.Progress"] = self._onProgressMessage + self._message_handlers["cura.proto.GCodeLayer"] = self._onGCodeLayerMessage + self._message_handlers["cura.proto.GCodePrefix"] = self._onGCodePrefixMessage + self._message_handlers["cura.proto.ObjectPrintTime"] = self._onObjectPrintTimeMessage + self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage self._slicing = False self._restart = False @@ -230,16 +231,7 @@ class CuraEngineBackend(Backend): self.printDurationMessage.emit(message.time, message.material_amount) def _createSocket(self): - super()._createSocket() - - self._socket.registerMessageType(1, Cura_pb2.Slice) - 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) - self._socket.registerMessageType(8, Cura_pb2.SlicingFinished) + super()._createSocket(os.path.abspath(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "Cura.proto"))) ## Manually triggers a reslice def forceSlice(self): @@ -278,7 +270,6 @@ class CuraEngineBackend(Backend): else: self._layer_view_active = False - def _onInstanceChanged(self): self._terminate() self.slicingCancelled.emit() diff --git a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py index 464b605371..3c0b23c595 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py @@ -56,21 +56,27 @@ class ProcessSlicedObjectListJob(Job): layer_data = LayerData.LayerData() layer_count = 0 - for object in self._message.objects: - layer_count += len(object.layers) + for i in range(self._message.repeatedMessageCount("objects")): + layer_count += self._message.getRepeatedMessage("objects", i).repeatedMessageCount("layers") current_layer = 0 - for object in self._message.objects: + for i in range(self._message.repeatedMessageCount("objects")): + object = self._message.getRepeatedMessage("objects", i) try: node = object_id_map[object.id] except KeyError: continue - for layer in object.layers: + for l in range(object.repeatedMessageCount("layers")): + layer = object.getRepeatedMessage("layers", l) + layer_data.addLayer(layer.id) layer_data.setLayerHeight(layer.id, layer.height) layer_data.setLayerThickness(layer.id, layer.thickness) - for polygon in layer.polygons: + + for p in range(layer.repeatedMessageCount("polygons")): + polygon = layer.getRepeatedMessage("polygons", p) + 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) @@ -83,8 +89,6 @@ class ProcessSlicedObjectListJob(Job): layer_data.addPolygon(layer.id, polygon.type, points, polygon.line_width) - Job.yieldThread() - current_layer += 1 progress = (current_layer / layer_count) * 100 # TODO: Rebuild the layer data mesh once the layer has been processed. diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 7fd703f8ca..156a859ade 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -81,20 +81,21 @@ class StartSliceJob(Job): self._sendSettings(self._profile) - slice_message = Cura_pb2.Slice() + slice_message = self._socket.createMessage("cura.proto.Slice"); for group in object_groups: - group_message = slice_message.object_lists.add() + group_message = slice_message.addRepeatedMessage("object_lists"); for object in group: mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation()) - obj = group_message.objects.add() + obj = group_message.addRepeatedMessage("objects"); obj.id = id(object) verts = numpy.array(mesh_data.getVertices()) verts[:,[1,2]] = verts[:,[2,1]] verts[:,1] *= -1 - obj.vertices = verts.tostring() + + obj.vertices = verts self._handlePerObjectSettings(object, obj) @@ -115,13 +116,13 @@ class StartSliceJob(Job): return str(value).encode("utf-8") def _sendSettings(self, profile): - msg = Cura_pb2.SettingList() + msg = self._socket.createMessage("cura.proto.SettingList"); settings = profile.getAllSettingValues(include_machine = True) start_gcode = settings["machine_start_gcode"] settings["material_bed_temp_prepend"] = "{material_bed_temperature}" not in start_gcode settings["material_print_temp_prepend"] = "{material_print_temperature}" not in start_gcode for key, value in settings.items(): - s = msg.settings.add() + s = msg.addRepeatedMessage("settings") s.name = key if key == "machine_start_gcode" or key == "machine_end_gcode": s.value = self._expandGcodeTokens(key, value, settings) @@ -134,7 +135,7 @@ class StartSliceJob(Job): profile = node.callDecoration("getProfile") if profile: for key, value in profile.getAllSettingValues().items(): - setting = message.settings.add() + setting = message.addRepeatedMessage("settings") setting.name = key setting.value = str(value).encode() @@ -145,7 +146,7 @@ class StartSliceJob(Job): return for key, value in object_settings.items(): - setting = message.settings.add() + setting = message.addRepeatedMessage("settings") setting.name = key setting.value = str(value).encode()