Layer data is now processed layer by layer.

This is done to prevent the very large messages that would be sent otherwise.
Protobuf can't do anything with messages above 512MB. As we no longer send a
"collection" message, this should no longer occur.
CURA-1210
This commit is contained in:
Jaime van Kessel 2016-03-24 12:14:31 +01:00
parent 834e2148d2
commit 7045d67bde
3 changed files with 51 additions and 82 deletions

View file

@ -2,14 +2,12 @@ syntax = "proto3";
package cura.proto;
message ObjectList
{
repeated Object objects = 1;
repeated Setting settings = 2;
}
// typeid 1
message Slice
{
repeated ObjectList object_lists = 1;
@ -24,28 +22,13 @@ message Object
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;
@ -70,20 +53,16 @@ message Polygon {
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;
}
@ -94,11 +73,9 @@ message Setting {
bytes value = 2;
}
// typeid 7
message GCodePrefix {
bytes data = 2;
}
// typeid 8
message SlicingFinished {
}
}

View file

@ -16,7 +16,7 @@ from UM.Message import Message
from UM.PluginRegistry import PluginRegistry
from cura.OneAtATimeIterator import OneAtATimeIterator
from . import ProcessSlicedObjectListJob
from . import ProcessSlicedLayersJob
from . import ProcessGCodeJob
from . import StartSliceJob
@ -51,7 +51,7 @@ class CuraEngineBackend(Backend):
self._layer_view_active = False
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
self._onActiveViewChanged()
self._stored_layer_data = None
self._stored_layer_data = []
Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onChanged)
@ -64,7 +64,8 @@ class CuraEngineBackend(Backend):
self._change_timer.setSingleShot(True)
self._change_timer.timeout.connect(self.slice)
self._message_handlers["cura.proto.SlicedObjectList"] = self._onSlicedObjectListMessage
#self._message_handlers["cura.proto.SlicedObjectList"] = self._onSlicedObjectListMessage
self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
self._message_handlers["cura.proto.Progress"] = self._onProgressMessage
self._message_handlers["cura.proto.GCodeLayer"] = self._onGCodeLayerMessage
self._message_handlers["cura.proto.GCodePrefix"] = self._onGCodePrefixMessage
@ -155,6 +156,7 @@ class CuraEngineBackend(Backend):
def _terminate(self):
self._slicing = False
self._restart = True
self._stored_layer_data = []
self.slicingCancelled.emit()
Logger.log("d", "Attempting to kill the engine process")
if self._process is not None:
@ -209,12 +211,9 @@ class CuraEngineBackend(Backend):
def _onSettingChanged(self, setting):
self._onChanged()
def _onSlicedObjectListMessage(self, message):
if self._layer_view_active:
self._process_layers_job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
self._process_layers_job.start()
else :
self._stored_layer_data = message
def _onLayerMessage(self, message):
self._stored_layer_data.append(message)
def _onProgressMessage(self, message):
if self._message:
@ -234,6 +233,11 @@ class CuraEngineBackend(Backend):
self._message.hide()
self._message = None
if self._layer_view_active and (self._process_layers_job is None or not self._process_layers_job.isRunning()):
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_layer_data)
self._process_layers_job.start()
self._stored_layer_data = []
def _onGCodeLayerMessage(self, message):
self._scene.gcode_list.append(message.data.decode("utf-8", "replace"))
@ -275,10 +279,10 @@ class CuraEngineBackend(Backend):
self._layer_view_active = True
# There is data and we're not slicing at the moment
# if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
if self._stored_layer_data and not self._slicing:
self._process_layers_job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(self._stored_layer_data)
if len(self._stored_layer_data) and not self._slicing:
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_layer_data)
self._process_layers_job.start()
self._stored_layer_data = None
self._stored_layer_data = []
else:
self._layer_view_active = False

View file

@ -1,4 +1,4 @@
# Copyright (c) 2015 Ultimaker B.V.
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from UM.Job import Job
@ -20,10 +20,10 @@ import struct
catalog = i18nCatalog("cura")
class ProcessSlicedObjectListJob(Job):
def __init__(self, message):
class ProcessSlicedLayersJob(Job):
def __init__(self, layers):
super().__init__()
self._message = message
self._layers = layers
self._scene = Application.getInstance().getController().getScene()
self._progress = None
self._abort_requested = False
@ -51,13 +51,12 @@ class ProcessSlicedObjectListJob(Job):
object_id_map = {}
new_node = SceneNode()
## Put all nodes in a dict identified by ID
## Remove old layer data (if any)
for node in DepthFirstIterator(self._scene.getRoot()):
if type(node) is SceneNode and node.getMeshData():
if node.callDecoration("getLayerData"):
self._scene.getRoot().removeChild(node)
else:
object_id_map[id(node)] = node
Job.yieldThread()
if self._abort_requested:
if self._progress:
@ -68,56 +67,45 @@ class ProcessSlicedObjectListJob(Job):
mesh = MeshData()
layer_data = LayerData.LayerData()
layer_count = 0
for i in range(self._message.repeatedMessageCount("objects")):
layer_count += self._message.getRepeatedMessage("objects", i).repeatedMessageCount("layers")
layer_count = len(self._layers)
current_layer = 0
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 l in range(object.repeatedMessageCount("layers")):
layer = object.getRepeatedMessage("layers", l)
for layer in self._layers:
layer_data.addLayer(layer.id)
layer_data.setLayerHeight(layer.id, layer.height)
layer_data.setLayerThickness(layer.id, layer.thickness)
layer_data.addLayer(layer.id)
layer_data.setLayerHeight(layer.id, layer.height)
layer_data.setLayerThickness(layer.id, layer.thickness)
for p in range(layer.repeatedMessageCount("polygons")):
polygon = layer.getRepeatedMessage("polygons", p)
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.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.
# Create a new 3D-array, copy the 2D points over and insert the right height.
# This uses manual array creation + copy rather than numpy.insert since this is
# faster.
new_points = numpy.empty((len(points), 3), numpy.float32)
new_points[:,0] = points[:,0]
new_points[:,1] = layer.height
new_points[:,2] = -points[:,1]
# Create a new 3D-array, copy the 2D points over and insert the right height.
# This uses manual array creation + copy rather than numpy.insert since this is
# faster.
new_points = numpy.empty((len(points), 3), numpy.float32)
new_points[:,0] = points[:,0]
new_points[:,1] = layer.height
new_points[:,2] = -points[:,1]
new_points /= 1000
new_points /= 1000
layer_data.addPolygon(layer.id, polygon.type, new_points, polygon.line_width)
Job.yieldThread()
layer_data.addPolygon(layer.id, polygon.type, new_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.
# This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.
Job.yieldThread()
current_layer += 1
progress = (current_layer / layer_count) * 100
# TODO: Rebuild the layer data mesh once the layer has been processed.
# This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.
if self._abort_requested:
if self._progress:
self._progress.hide()
return
if self._abort_requested:
if self._progress:
self._progress.setProgress(progress)
self._progress.hide()
return
if self._progress:
self._progress.setProgress(progress)
# We are done processing all the layers we got from the engine, now create a mesh out of the data
layer_data.build()