From 7045d67bde646868ed692235fd3ea960897c6508 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 24 Mar 2016 12:14:31 +0100 Subject: [PATCH 01/12] 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 --- plugins/CuraEngineBackend/Cura.proto | 25 +----- .../CuraEngineBackend/CuraEngineBackend.py | 28 ++++--- ...ctListJob.py => ProcessSlicedLayersJob.py} | 80 ++++++++----------- 3 files changed, 51 insertions(+), 82 deletions(-) rename plugins/CuraEngineBackend/{ProcessSlicedObjectListJob.py => ProcessSlicedLayersJob.py} (60%) diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index 39fa3a56ae..0d4975aca4 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -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 { -} +} \ No newline at end of file diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index ed73f0ad93..0ec1099c43 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -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 diff --git a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py similarity index 60% rename from plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py rename to plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index df2e5966e9..d3053f9175 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -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() From c91a87cbf86d5c506a59eeece8a2d169005735fb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 30 Mar 2016 09:24:50 +0200 Subject: [PATCH 02/12] Minor codestyle changes CURA-1210 --- plugins/CuraEngineBackend/CuraEngineBackend.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 0ec1099c43..bd4279083c 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -64,7 +64,6 @@ 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.Layer"] = self._onLayerMessage self._message_handlers["cura.proto.Progress"] = self._onProgressMessage self._message_handlers["cura.proto.GCodeLayer"] = self._onGCodeLayerMessage @@ -279,7 +278,7 @@ 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 len(self._stored_layer_data) and not self._slicing: + if 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 = [] From bfba09565587e1a87dea57ef896f27021e033f08 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 4 Apr 2016 14:13:18 +0200 Subject: [PATCH 03/12] Updated disallowed areas for um2 & um2+ CURA-1166 --- resources/machines/ultimaker2.json | 8 ++++---- resources/machines/ultimaker2plus.json | 8 +++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/resources/machines/ultimaker2.json b/resources/machines/ultimaker2.json index 214dbba1e0..d25368ac4a 100644 --- a/resources/machines/ultimaker2.json +++ b/resources/machines/ultimaker2.json @@ -70,10 +70,10 @@ "machine_use_extruder_offset_to_offset_coords": { "default": true }, "machine_gcode_flavor": { "default": "UltiGCode" }, "machine_disallowed_areas": { "default": [ - [[-115.0, 112.5], [ -82.0, 112.5], [ -84.0, 104.5], [-115.0, 104.5]], - [[ 115.0, 112.5], [ 115.0, 104.5], [ 110.0, 104.5], [ 108.0, 112.5]], - [[-115.0, -112.5], [-115.0, -104.5], [ -84.0, -104.5], [ -82.0, -112.5]], - [[ 115.0, -112.5], [ 108.0, -112.5], [ 110.0, -104.5], [ 115.0, -104.5]] + [[-115.0, 112.5], [ -82.0, 112.5], [ -84.0, 102.5], [-115.0, 102.5]], + [[ 115.0, 112.5], [ 115.0, 102.5], [ 110.0, 102.5], [ 108.0, 112.5]], + [[-115.0, -112.5], [-115.0, -104.5], [ -84.0, -104.5], [ -82.0, -112.5]], + [[ 115.0, -112.5], [ 108.0, -112.5], [ 110.0, -104.5], [ 115.0, -104.5]] ]}, "machine_platform_offset": { "default": [9.0, 0.0, 0.0] }, diff --git a/resources/machines/ultimaker2plus.json b/resources/machines/ultimaker2plus.json index 2eb5ecc78c..7e1e2a87a6 100644 --- a/resources/machines/ultimaker2plus.json +++ b/resources/machines/ultimaker2plus.json @@ -38,6 +38,12 @@ ] ] }, - "retraction_amount": { "default": 6.0 } + "retraction_amount": { "default": 6.0 }, + "machine_disallowed_areas": { "default": [ + [[-115.0, 112.5], [ -78.0, 112.5], [ -80.0, 102.5], [-115.0, 102.5]], + [[ 115.0, 112.5], [ 115.0, 102.5], [ 106.0, 102.5], [ 104.0, 112.5]], + [[-115.0, -112.5], [-115.0, -104.5], [ -84.0, -104.5], [ -82.0, -112.5]], + [[ 115.0, -112.5], [ 108.0, -112.5], [ 110.0, -104.5], [ 115.0, -104.5]] + ]} } } From 78038e90b8c6550821507a6064e56604f87ce751 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 5 Apr 2016 09:52:33 +0200 Subject: [PATCH 04/12] Removed Olsson block upgrade option from UM2Extended --- resources/machines/ultimaker2_extended.json | 4 - .../machines/ultimaker2_extended_olsson.json | 17 ---- .../ultimaker2_extended_olsson_025.json | 18 ----- .../ultimaker2_extended_olsson_040.json | 19 ----- .../ultimaker2_extended_olsson_060.json | 19 ----- .../ultimaker2_extended_olsson_080.json | 19 ----- .../WizardPages/SelectUpgradedPartsUM2.qml | 80 ------------------- 7 files changed, 176 deletions(-) delete mode 100644 resources/machines/ultimaker2_extended_olsson.json delete mode 100644 resources/machines/ultimaker2_extended_olsson_025.json delete mode 100644 resources/machines/ultimaker2_extended_olsson_040.json delete mode 100644 resources/machines/ultimaker2_extended_olsson_060.json delete mode 100644 resources/machines/ultimaker2_extended_olsson_080.json delete mode 100644 resources/qml/WizardPages/SelectUpgradedPartsUM2.qml diff --git a/resources/machines/ultimaker2_extended.json b/resources/machines/ultimaker2_extended.json index 72b2ad53e6..0cd5ff0002 100644 --- a/resources/machines/ultimaker2_extended.json +++ b/resources/machines/ultimaker2_extended.json @@ -10,10 +10,6 @@ "file_formats": "text/x-gcode", "inherits": "ultimaker2.json", - "pages": [ - "SelectUpgradedPartsUM2" - ], - "machine_settings": { "machine_height": { "default": 315 } } diff --git a/resources/machines/ultimaker2_extended_olsson.json b/resources/machines/ultimaker2_extended_olsson.json deleted file mode 100644 index d144a97014..0000000000 --- a/resources/machines/ultimaker2_extended_olsson.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "id": "ultimaker2_extended_olsson_base", - "version": 1, - "name": "Ultimaker 2 Extended with Olsson Block", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2backplate.png", - "visible": false, - "file_formats": "text/x-gcode", - "inherits": "ultimaker2_olsson.json", - - "machine_settings": { - "machine_height": { "default": 313 }, - "machine_show_variants": { "default": true } - } -} diff --git a/resources/machines/ultimaker2_extended_olsson_025.json b/resources/machines/ultimaker2_extended_olsson_025.json deleted file mode 100644 index ea09020363..0000000000 --- a/resources/machines/ultimaker2_extended_olsson_025.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id": "ultimaker2_extended_olsson", - "version": 1, - "name": "Ultimaker 2 Extended with Olsson Block", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2backplate.png", - "visible": false, - "file_formats": "text/x-gcode", - "inherits": "ultimaker2_extended_olsson.json", - "variant": "0.25 mm", - "profiles_machine": "ultimaker2_olsson", - "machine_settings": { - "machine_nozzle_size": { "default": 0.25 }, - "machine_nozzle_tip_outer_diameter": { "default": 0.8 } - } -} diff --git a/resources/machines/ultimaker2_extended_olsson_040.json b/resources/machines/ultimaker2_extended_olsson_040.json deleted file mode 100644 index dfbd4a94d9..0000000000 --- a/resources/machines/ultimaker2_extended_olsson_040.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "ultimaker2_extended_olsson", - "version": 1, - "name": "Ultimaker 2 Extended with Olsson Block", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2backplate.png", - "visible": false, - "file_formats": "text/x-gcode", - "inherits": "ultimaker2_extended_olsson.json", - - "variant": "0.4 mm", - "profiles_machine": "ultimaker2_olsson", - "machine_settings": { - "machine_nozzle_size": { "default": 0.40 }, - "machine_nozzle_tip_outer_diameter": { "default": 1.05 } - } -} diff --git a/resources/machines/ultimaker2_extended_olsson_060.json b/resources/machines/ultimaker2_extended_olsson_060.json deleted file mode 100644 index 30d1cc082c..0000000000 --- a/resources/machines/ultimaker2_extended_olsson_060.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "ultimaker2_extended_olsson", - "version": 1, - "name": "Ultimaker 2 Extended with Olsson Block", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2backplate.png", - "visible": false, - "file_formats": "text/x-gcode", - "inherits": "ultimaker2_extended_olsson.json", - - "variant": "0.6 mm", - "profiles_machine": "ultimaker2_olsson", - "machine_settings": { - "machine_nozzle_size": { "default": 0.60 }, - "machine_nozzle_tip_outer_diameter": { "default": 1.25 } - } -} diff --git a/resources/machines/ultimaker2_extended_olsson_080.json b/resources/machines/ultimaker2_extended_olsson_080.json deleted file mode 100644 index 04fe80337c..0000000000 --- a/resources/machines/ultimaker2_extended_olsson_080.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "ultimaker2_extended_olsson", - "version": 1, - "name": "Ultimaker 2 Extended with Olsson Block", - "manufacturer": "Ultimaker", - "author": "Ultimaker", - "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2backplate.png", - "file_formats": "text/x-gcode", - "visible": false, - "inherits": "ultimaker2_extended_olsson.json", - - "variant": "0.8 mm", - "profiles_machine": "ultimaker2_olsson", - "machine_settings": { - "machine_nozzle_size": { "default": 0.80 }, - "machine_nozzle_tip_outer_diameter": { "default": 1.35 } - } -} diff --git a/resources/qml/WizardPages/SelectUpgradedPartsUM2.qml b/resources/qml/WizardPages/SelectUpgradedPartsUM2.qml deleted file mode 100644 index 79404492f0..0000000000 --- a/resources/qml/WizardPages/SelectUpgradedPartsUM2.qml +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2015 Ultimaker B.V. -// Cura is released under the terms of the AGPLv3 or higher. - -import QtQuick 2.2 -import QtQuick.Controls 1.1 -import QtQuick.Window 2.1 - -import UM 1.1 as UM - -Item -{ - id: wizardPage - property string title - - SystemPalette{id: palette} - UM.I18nCatalog { id: catalog; name:"cura"} - - property variant wizard: null; - - Connections - { - target: wizardPage.wizard - onNextClicked: //You can add functions here that get triggered when the final button is clicked in the wizard-element - { - if(wizardPage.wizard.lastPage == true){ - wizardPage.wizard.visible = false - } - } - } - - Component.onDestruction: - { - if (hotendCheckBox.checked == true){ - switch(UM.MachineManager.getMachineDefinitionType()) { - case "ultimaker2": - UM.MachineManager.setMachineDefinitionType("ultimaker2_olsson") - break; - case "ultimaker2_extended": - UM.MachineManager.setMachineDefinitionType("ultimaker2_extended_olsson") - break; - } - } - } - Label - { - id: pageTitle - width: parent.width - text: catalog.i18nc("@title", "Select Upgraded Parts") - wrapMode: Text.WordWrap - font.pointSize: 18 - } - Label - { - id: pageDescription - anchors.top: pageTitle.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - width: parent.width - wrapMode: Text.WordWrap - text: catalog.i18nc("@label","To assist you in having better default settings for your Ultimaker. Cura would like to know which upgrades you have in your machine:") - } - - Item - { - id: pageCheckboxes - height: childrenRect.height - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.top: pageDescription.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - width: parent.width - UM.Theme.getSize("default_margin").width - CheckBox - { - id: hotendCheckBox - text: catalog.i18nc("@option:check","Olsson Block") - checked: false - } - } - - ExclusiveGroup { id: printerGroup; } -} From 2cedfbfbb7c8f05a0c70c1dca5e10a727e428e9a Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 5 Apr 2016 22:28:52 +0200 Subject: [PATCH 05/12] Change splash-screen so it does not say "developer version", pending a release "real soon now" CURA-1341 --- resources/images/cura.png | Bin 20982 -> 18834 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/images/cura.png b/resources/images/cura.png index 625668ea2578413ad52adc7adfd1c9e42e5dd56f..56908f3719821c1f8d04cc89e5282f17c45a746a 100644 GIT binary patch literal 18834 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 RrVaoA002ovPDHLkV1f&wSyuo6 literal 20982 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 ydg&9B#jfF8(_Z;#-t6g2k5K0zT Date: Wed, 6 Apr 2016 10:51:45 +0200 Subject: [PATCH 06/12] Fixed changelog plugin so it shows correctly again CURA-1326 --- plugins/ChangeLogPlugin/ChangeLog.py | 20 ++++++++++++++------ plugins/ChangeLogPlugin/__init__.py | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/plugins/ChangeLogPlugin/ChangeLog.py b/plugins/ChangeLogPlugin/ChangeLog.py index 7586268aa6..7c8c81f2c6 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.py +++ b/plugins/ChangeLogPlugin/ChangeLog.py @@ -28,9 +28,10 @@ class ChangeLog(Extension, QObject,): self._version = Version(version_string) else: self._version = None + self._change_logs = None Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) - Preferences.getInstance().addPreference("general/latest_version_changelog_shown", "15.05.90") #First version of CURA with uranium + Preferences.getInstance().addPreference("general/latest_version_changelog_shown", "2.0.0") #First version of CURA with uranium self.addMenuItem(catalog.i18nc("@item:inmenu", "Show Changelog"), self.showChangelog) #self.showChangelog() @@ -42,7 +43,6 @@ class ChangeLog(Extension, QObject,): @pyqtSlot(result = str) def getChangeLogString(self): logs = self.getChangeLogs() - latest_version = Version(Preferences.getInstance().getValue("general/latest_version_changelog_shown")) result = "" for version in logs: result += "

" + str(version) + "


" @@ -58,7 +58,7 @@ class ChangeLog(Extension, QObject,): def loadChangeLogs(self): self._change_logs = collections.OrderedDict() - with open(os.path.join(PluginRegistry.getInstance().getPluginPath("ChangeLogPlugin"), "ChangeLog.txt"), "r",-1, "utf-8") as f: + with open(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "ChangeLog.txt"), "r",-1, "utf-8") as f: open_version = None open_header = None for line in f: @@ -78,12 +78,19 @@ class ChangeLog(Extension, QObject,): def _onEngineCreated(self): if not self._version: return #We're on dev branch. - #if self._version > Preferences.getInstance().getValue("general/latest_version_changelog_shown"): - #self.showChangelog() + + if Preferences.getInstance().getValue("general/latest_version_changelog_shown") == "master": + latest_version_shown = Version("0.0.0") + else: + latest_version_shown = Version(Preferences.getInstance().getValue("general/latest_version_changelog_shown")) + + if self._version > latest_version_shown: + self.showChangelog() def showChangelog(self): if not self._changelog_window: self.createChangelogWindow() + self._changelog_window.show() Preferences.getInstance().setValue("general/latest_version_changelog_shown", Application.getInstance().getVersion()) @@ -92,7 +99,8 @@ class ChangeLog(Extension, QObject,): self._changelog_window.hide() def createChangelogWindow(self): - path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("ChangeLogPlugin"), "ChangeLog.qml")) + path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "ChangeLog.qml")) + component = QQmlComponent(Application.getInstance()._engine, path) self._changelog_context = QQmlContext(Application.getInstance()._engine.rootContext()) self._changelog_context.setContextProperty("manager", self) diff --git a/plugins/ChangeLogPlugin/__init__.py b/plugins/ChangeLogPlugin/__init__.py index 42683d91dc..88ac4e08a6 100644 --- a/plugins/ChangeLogPlugin/__init__.py +++ b/plugins/ChangeLogPlugin/__init__.py @@ -12,7 +12,7 @@ def getMetaData(): "name": catalog.i18nc("@label", "Changelog"), "author": "Ultimaker", "version": "1.0", - "description": catalog.i18nc("@info:whatsthis", "Shows changes since latest checked version."), + "description": catalog.i18nc("@info:whatsthis", "Shows changes since latest checked version."), "api": 2 } } From 34b0d051fbd19c4423b495cb1a526636769963fe Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 6 Apr 2016 15:02:37 +0200 Subject: [PATCH 07/12] Updated changelog for 2.1 release CURA-1326 --- plugins/ChangeLogPlugin/ChangeLog.txt | 90 +++++++++++++-------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index 9950fb3c4e..ee4b256a58 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -1,55 +1,53 @@ -[2.0.0] +[2.1.0] -*Naming changes -Infill prints after perimeters → Infill Before Walls -Initial layer thickness → Initial Layer Height -Structure type → Pattern -Cool head lift → Lift Head -Combine everything (Type-A) → Union Overlapping Volumes -Combine everything (Type-B) → Remove All Holes -Keep open faces Keep Disconnected → Faces -Only follow mesh surface → Surface Mode +*2.1 Beta release +Cura has been completely reengineered from the ground up for an even more seamless integration between hardware, software and materials. Together with its intuitive new user interface, it’s now also ready for any future developments. For the beginner Cura makes 3D printing incredibly easy, and for more advanced users, there are over 140 new customisable settings. -*All at Once/One at a Time -Cura’s default mode is set to All At Once. You can print multiple objects faster with the option print objects One At A Time. This can be changed in Advanced Settings. Please note that in One At A Time mode, grouped objects will still be printed as a single object. - -*Setting Profiles -Now you can create preferred setting favourites and share them with others. - -*Post-Processing Plugin -This plugin supports post-processing on the GCode generated by the engine – allowing for custom scripts. For example, Pause At Height and Tweak At Z. - -*Support for Bed Levelling and other wizards -We have restored the Bed Levelling function and several other wizards that were previously available for the Ultimaker Original. Additionally, these are ready to be used with machines from other vendors (BQ, Rep Rap neo). - -*Third-Party Printer Profiles -We received printer profiles for third-party vendors (BQ, Rep Rap neo) from the community (thanks guys!). These have been included in this release. - -*3MF File Loading Support (New) -We’re happy to report we now support loading 3MF files. This is a new file format similar to AMF, but freely available. - -*Output Device API for Developers (New) -The Storage Device API has now been replaced with the Output Device API for saving files. It’s designed to make it easier for anyone that wants to write a plugin giving them some form of output device, whether it’s a printer or a web service. - -*Improved Cut-Off Object Bottom (New) -We’ve added a feature than allows you to move objects below the build plate. You can either correct a model with a rough bottom, or print only a part of an object. Please note that the implementation greatly differs from the old one where it was a setting. - -*Improved File Saving (new) -We’re happy to report that the way file saving is handled has received a huge overhaul. Now the default action is to save everything on the build plate to a file. - -*Select Multiple Objects (New) +*Select Multiple Objects You now have the freedom to select and manipulate multiple objects at the same time. -*Grouping (New) +*Grouping You can now group objects together to make it easier to manipulate multiple objects. -*Per-Object Settings (New) -You can now select different profiles for different objects and in advance mode override individual settings. +*Undo/Redo +You can now undo and redo your actions, like moving an object or scaling. + +*Setting Profiles +The new GUI allows custom profiles to load easily and intuitively, directly from +Cura. + +*3MF File Loading Support +We’re happy to report we now support loading 3MF files. This is a new file format similar to AMF, but freely available. + +*Intuitive Cut-Off Object Bottom +We’ve added a feature that allows you to move objects below the build plate. You can either correct a model with a rough bottom, or print only a part of an object. Please note that the implementation differs greatly from the old one when it was just a setting. + +*64-bit Windows Builds +An optimized 64-bit Windows Cura version is now available. This allows you to load larger model files. + +*Automatic calculations +Cura allows you to set a number of lines/layers instead of millimeters. The engine automatically calculates the right settings. + +*Per-Object Settings +You can now select different profiles for different objects and when in advanced mode, override individual settings. + +*Fuzzy Skin +Prints the outer walls with a jittering motion to give your object a diffused finish. + +*Wire Printing +The object is printed with a mid-air / net-like structure, following the mesh surface. The build plate will move up and down during diagonal segments. Though not visible in layer view, you can view the result in other software, such as Repetier Host or http://chilipeppr.com/tinyg. + +* Conical Support +An experimental filament cost-reduction feature for support. + +*Support Towers +Specialized support for tiny overhang areas. + +*ZigZag infill +A new, infill type that’s easily breakable, introduced specially for support. + +* Avoid Printed Parts +While combing, the print head moves around printed parts, avoiding collisions with the nozzle and a part that’s already printed. -*64-bit Windows Builds (New) -Cura now allows 64-bit Windows builds in addition to the 32-bit builds. For users running the 64-bit version of Windows, you can now load models in more detail. -*Fuzzy skin mode (New) -A new engine feature that enables objects to be printed as if they have a fuzzy skin. -*Z-seam alignment (New) From 3a478dac1266afda861d56bc0c39a21e6e7b17b7 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 6 Apr 2016 16:47:21 +0200 Subject: [PATCH 08/12] Fix inaccuracy in change log You can't actually set a profile per object. You can set settings per object. Contributes to issue CURA-1326. --- plugins/ChangeLogPlugin/ChangeLog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index ee4b256a58..c90633da27 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -29,7 +29,7 @@ An optimized 64-bit Windows Cura version is now available. This allows you to lo Cura allows you to set a number of lines/layers instead of millimeters. The engine automatically calculates the right settings. *Per-Object Settings -You can now select different profiles for different objects and when in advanced mode, override individual settings. +You can now override individual settings for different objects in advanced mode. *Fuzzy Skin Prints the outer walls with a jittering motion to give your object a diffused finish. From c176c608d7482be72c1f5107b40e2db236af9e38 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 6 Apr 2016 17:23:11 +0200 Subject: [PATCH 09/12] Added extra mm to UM2+ lower right disallowed area CURA-1166 --- resources/machines/ultimaker2plus.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/machines/ultimaker2plus.json b/resources/machines/ultimaker2plus.json index 7e1e2a87a6..a3441a8dd4 100644 --- a/resources/machines/ultimaker2plus.json +++ b/resources/machines/ultimaker2plus.json @@ -41,7 +41,7 @@ "retraction_amount": { "default": 6.0 }, "machine_disallowed_areas": { "default": [ [[-115.0, 112.5], [ -78.0, 112.5], [ -80.0, 102.5], [-115.0, 102.5]], - [[ 115.0, 112.5], [ 115.0, 102.5], [ 106.0, 102.5], [ 104.0, 112.5]], + [[ 115.0, 112.5], [ 115.0, 102.5], [ 105.0, 102.5], [ 103.0, 112.5]], [[-115.0, -112.5], [-115.0, -104.5], [ -84.0, -104.5], [ -82.0, -112.5]], [[ 115.0, -112.5], [ 108.0, -112.5], [ 110.0, -104.5], [ 115.0, -104.5]] ]} From 24950627dcefff1188eaedbc254b1aec035e004d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 7 Apr 2016 13:31:20 +0200 Subject: [PATCH 10/12] Removable drives with any name are now accepted by OSX I have no idea why the old plugin only accepted drives with the name "MASS STORAGE DEVICE", but it now simply lists all removable drives CURA-1365 --- .../OSXRemovableDrivePlugin.py | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py index 9de78a36d1..7ac5fc0838 100644 --- a/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py +++ b/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py @@ -16,16 +16,19 @@ import plistlib class OSXRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin): def checkRemovableDrives(self): drives = {} - p = subprocess.Popen(["system_profiler", "SPUSBDataType", "-xml"], stdout=subprocess.PIPE) + p = subprocess.Popen(["system_profiler", "SPUSBDataType", "-xml"], stdout = subprocess.PIPE) plist = plistlib.loads(p.communicate()[0]) p.wait() - for dev in self._findInTree(plist, "Mass Storage Device"): - if "removable_media" in dev and dev["removable_media"] == "yes" and "volumes" in dev and len(dev["volumes"]) > 0: - for vol in dev["volumes"]: - if "mount_point" in vol: - volume = vol["mount_point"] - drives[volume] = os.path.basename(volume) + for entry in plist: + if "_items" in entry: + for item in entry["_items"]: + for dev in item["_items"]: + if "removable_media" in dev and dev["removable_media"] == "yes" and "volumes" in dev and len(dev["volumes"]) > 0: + for vol in dev["volumes"]: + if "mount_point" in vol: + volume = vol["mount_point"] + drives[volume] = os.path.basename(volume) p = subprocess.Popen(["system_profiler", "SPCardReaderDataType", "-xml"], stdout=subprocess.PIPE) plist = plistlib.loads(p.communicate()[0]) @@ -51,16 +54,4 @@ class OSXRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin): if return_code != 0: return False else: - return True - - def _findInTree(self, t, n): - ret = [] - if type(t) is dict: - if "_name" in t and t["_name"] == n: - ret.append(t) - for k, v in t.items(): - ret += self._findInTree(v, n) - if type(t) is list: - for v in t: - ret += self._findInTree(v, n) - return ret + return True \ No newline at end of file From 75d3a136597bc4f32803205a7a6499db46169e7e Mon Sep 17 00:00:00 2001 From: ckielstra Date: Fri, 8 Apr 2016 09:51:46 +0200 Subject: [PATCH 11/12] Spelling --- plugins/ChangeLogPlugin/ChangeLog.txt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index c90633da27..8dc6a61dd8 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -13,8 +13,7 @@ You can now group objects together to make it easier to manipulate multiple obje You can now undo and redo your actions, like moving an object or scaling. *Setting Profiles -The new GUI allows custom profiles to load easily and intuitively, directly from -Cura. +The new GUI allows custom profiles to load easily and intuitively, directly from Cura. *3MF File Loading Support We’re happy to report we now support loading 3MF files. This is a new file format similar to AMF, but freely available. @@ -38,7 +37,7 @@ Prints the outer walls with a jittering motion to give your object a diffused fi The object is printed with a mid-air / net-like structure, following the mesh surface. The build plate will move up and down during diagonal segments. Though not visible in layer view, you can view the result in other software, such as Repetier Host or http://chilipeppr.com/tinyg. * Conical Support -An experimental filament cost-reduction feature for support. +An experimental filament, cost-reduction feature, for support. *Support Towers Specialized support for tiny overhang areas. @@ -48,6 +47,3 @@ A new, infill type that’s easily breakable, introduced specially for support. * Avoid Printed Parts While combing, the print head moves around printed parts, avoiding collisions with the nozzle and a part that’s already printed. - - - From e034e3f9e1136a5249aefc6f1affeedc57a4634f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 8 Apr 2016 09:58:34 +0200 Subject: [PATCH 12/12] Extra hull area now matches the assumptions in the rest of the code It's now also correct according to the original documentation. CURA-435 --- cura/ConvexHullJob.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/ConvexHullJob.py b/cura/ConvexHullJob.py index 7ef63e61e8..b934fa494b 100644 --- a/cura/ConvexHullJob.py +++ b/cura/ConvexHullJob.py @@ -47,7 +47,7 @@ class ConvexHullJob(Job): # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. # This is done because of rounding errors. - hull = hull.getMinkowskiHull(Polygon(numpy.array([[-1, -1], [-1, 1], [1, 1], [1, -1]], numpy.float32))) + hull = hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32))) profile = Application.getInstance().getMachineManager().getWorkingProfile() if profile: