Browse Source

Merge branch 'master' of github.com:Ultimaker/Cura

Tim Kuipers 8 years ago
parent
commit
6b3a25a491

+ 22 - 20
cura/BuildVolume.py

@@ -191,20 +191,22 @@ class BuildVolume(SceneNode):
         else:
             self._disallowed_area_mesh = None
 
-        self._volume_aabb = AxisAlignedBox(minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h, max_d))
+        self._volume_aabb = AxisAlignedBox(
+            minimum = Vector(min_w, min_h - 1.0, min_d),
+            maximum = Vector(max_w, max_h - self._raft_thickness, max_d))
 
-        skirt_size = 0.0
+        bed_adhesion_size = 0.0
 
         container_stack = Application.getInstance().getGlobalContainerStack()
         if container_stack:
-            skirt_size = self._getSkirtSize(container_stack)
+            bed_adhesion_size = self._getBedAdhesionSize(container_stack)
 
         # As this works better for UM machines, we only add the disallowed_area_size for the z direction.
         # This is probably wrong in all other cases. TODO!
         # The +1 and -1 is added as there is always a bit of extra room required to work properly.
         scale_to_max_bounds = AxisAlignedBox(
-            minimum = Vector(min_w + skirt_size + 1, min_h, min_d + disallowed_area_size - skirt_size + 1),
-            maximum = Vector(max_w - skirt_size - 1, max_h, max_d - disallowed_area_size + skirt_size - 1)
+            minimum = Vector(min_w + bed_adhesion_size + 1, min_h, min_d + disallowed_area_size - bed_adhesion_size + 1),
+            maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness, max_d - disallowed_area_size + bed_adhesion_size - 1)
         )
 
         Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
@@ -309,62 +311,62 @@ class BuildVolume(SceneNode):
                 [prime_x - PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE],
             ])
 
-        skirt_size = self._getSkirtSize(self._active_container_stack)
+        bed_adhesion_size = self._getBedAdhesionSize(self._active_container_stack)
 
         if disallowed_areas:
             # Extend every area already in the disallowed_areas with the skirt size.
             for area in disallowed_areas:
                 poly = Polygon(numpy.array(area, numpy.float32))
-                poly = poly.getMinkowskiHull(Polygon(approximatedCircleVertices(skirt_size)))
+                poly = poly.getMinkowskiHull(Polygon(approximatedCircleVertices(bed_adhesion_size)))
 
                 areas.append(poly)
 
         # Add the skirt areas around the borders of the build plate.
-        if skirt_size > 0:
+        if bed_adhesion_size > 0:
             half_machine_width = self._active_container_stack.getProperty("machine_width", "value") / 2
             half_machine_depth = self._active_container_stack.getProperty("machine_depth", "value") / 2
 
             areas.append(Polygon(numpy.array([
                 [-half_machine_width, -half_machine_depth],
                 [-half_machine_width, half_machine_depth],
-                [-half_machine_width + skirt_size, half_machine_depth - skirt_size],
-                [-half_machine_width + skirt_size, -half_machine_depth + skirt_size]
+                [-half_machine_width + bed_adhesion_size, half_machine_depth - bed_adhesion_size],
+                [-half_machine_width + bed_adhesion_size, -half_machine_depth + bed_adhesion_size]
             ], numpy.float32)))
 
             areas.append(Polygon(numpy.array([
                 [half_machine_width, half_machine_depth],
                 [half_machine_width, -half_machine_depth],
-                [half_machine_width - skirt_size, -half_machine_depth + skirt_size],
-                [half_machine_width - skirt_size, half_machine_depth - skirt_size]
+                [half_machine_width - bed_adhesion_size, -half_machine_depth + bed_adhesion_size],
+                [half_machine_width - bed_adhesion_size, half_machine_depth - bed_adhesion_size]
             ], numpy.float32)))
 
             areas.append(Polygon(numpy.array([
                 [-half_machine_width, half_machine_depth],
                 [half_machine_width, half_machine_depth],
-                [half_machine_width - skirt_size, half_machine_depth - skirt_size],
-                [-half_machine_width + skirt_size, half_machine_depth - skirt_size]
+                [half_machine_width - bed_adhesion_size, half_machine_depth - bed_adhesion_size],
+                [-half_machine_width + bed_adhesion_size, half_machine_depth - bed_adhesion_size]
             ], numpy.float32)))
 
             areas.append(Polygon(numpy.array([
                 [half_machine_width, -half_machine_depth],
                 [-half_machine_width, -half_machine_depth],
-                [-half_machine_width + skirt_size, -half_machine_depth + skirt_size],
-                [half_machine_width - skirt_size, -half_machine_depth + skirt_size]
+                [-half_machine_width + bed_adhesion_size, -half_machine_depth + bed_adhesion_size],
+                [half_machine_width - bed_adhesion_size, -half_machine_depth + bed_adhesion_size]
             ], numpy.float32)))
 
         self._disallowed_areas = areas
 
     ##  Convenience function to calculate the size of the bed adhesion in directions x, y.
-    def _getSkirtSize(self, container_stack):
+    def _getBedAdhesionSize(self, container_stack):
         skirt_size = 0.0
 
         adhesion_type = container_stack.getProperty("adhesion_type", "value")
         if adhesion_type == "skirt":
             skirt_distance = container_stack.getProperty("skirt_gap", "value")
             skirt_line_count = container_stack.getProperty("skirt_line_count", "value")
-            skirt_size = skirt_distance + (skirt_line_count * container_stack.getProperty("skirt_line_width", "value"))
+            skirt_size = skirt_distance + (skirt_line_count * container_stack.getProperty("skirt_brim_line_width", "value"))
         elif adhesion_type == "brim":
-            skirt_size = container_stack.getProperty("brim_line_count", "value") * container_stack.getProperty("skirt_line_width", "value")
+            skirt_size = container_stack.getProperty("brim_line_count", "value") * container_stack.getProperty("skirt_brim_line_width", "value")
         elif adhesion_type == "raft":
             skirt_size = container_stack.getProperty("raft_margin", "value")
 
@@ -379,5 +381,5 @@ class BuildVolume(SceneNode):
     def _clamp(self, value, min_value, max_value):
         return max(min(value, max_value), min_value)
 
-    _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"]
+    _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"]
     _raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap"]

+ 1 - 1
cura/ConvexHullDecorator.py

@@ -157,7 +157,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
                 vertex_data = mesh.getConvexHullTransformedVertices(world_transform)
                 # Don't use data below 0.
                 # TODO; We need a better check for this as this gives poor results for meshes with long edges.
-                vertex_data = vertex_data[vertex_data[:,1] >= 0]
+                vertex_data = vertex_data[vertex_data[:,1] >= -0.01]
 
                 if len(vertex_data) >= 4:
                     # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices

+ 8 - 2
cura/CuraApplication.py

@@ -97,6 +97,7 @@ class CuraApplication(QtApplication):
         SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True)
         SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True)
         SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True)
+        SettingDefinition.addSupportedProperty("global_inherits_stack", DefinitionPropertyType.Function, default = "-1")
         SettingDefinition.addSettingType("extruder", int, str, Validator)
 
         ## Add the 4 types of profiles to storage.
@@ -582,7 +583,8 @@ class CuraApplication(QtApplication):
 
             op.push()
             if group_node:
-                if len(group_node.getChildren()) == 1:
+                if len(group_node.getChildren()) == 1 and group_node.callDecoration("isGroup"):
+                    group_node.getChildren()[0].translate(group_node.getPosition())
                     group_node.getChildren()[0].setParent(group_node.getParent())
                     op = RemoveSceneNodeOperation(group_node)
                     op.push()
@@ -851,7 +853,11 @@ class CuraApplication(QtApplication):
 
     def _reloadMeshFinished(self, job):
         # TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh!
-        job._node.setMeshData(job.getResult().getMeshData())
+        mesh_data = job.getResult().getMeshData()
+        if mesh_data:
+            job._node.setMeshData(mesh_data)
+        else:
+            Logger.log("w", "Could not find a mesh in reloaded node.")
 
     def _openFile(self, file):
         job = ReadMeshJob(os.path.abspath(file))

+ 8 - 1
cura/PlatformPhysics.py

@@ -48,7 +48,14 @@ class PlatformPhysics:
             bbox = node.getBoundingBox()
 
             # Ignore intersections with the bottom
-            build_volume_bounding_box = self._build_volume.getBoundingBox().set(bottom=-9001)
+            build_volume_bounding_box = self._build_volume.getBoundingBox()
+            if build_volume_bounding_box:
+                # It's over 9000!
+                build_volume_bounding_box = build_volume_bounding_box.set(bottom=-9001)
+            else:
+                # No bounding box. This is triggered when running Cura from command line with a model for the first time
+                # In that situation there is a model, but no machine (and therefore no build volume.
+                return
             node._outside_buildarea = False
 
             # Mark the node as outside the build volume if the bounding box test fails.

+ 1 - 1
cura/PrintInformation.py

@@ -95,7 +95,7 @@ class PrintInformation(QObject):
             else:  # Machine with no extruder stacks
                 density = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("properties", {}).get("density", 0)
 
-            self._material_weights.append(float(amount) * float(density))
+            self._material_weights.append(float(amount) * float(density) / 1000)
             self._material_lengths.append(round((amount / (math.pi * r ** 2)) / 1000, 2))
         self.materialLengthsChanged.emit()
         self.materialWeightsChanged.emit()

+ 24 - 4
cura/Settings/ExtruderManager.py

@@ -22,7 +22,7 @@ class ExtruderManager(QObject):
     def __init__(self, parent = None):
         super().__init__(parent)
         self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs.
-        self._active_extruder_index = -1
+        self._active_extruder_index = 0
         UM.Application.getInstance().globalContainerStackChanged.connect(self.__globalContainerStackChanged)
         self._addCurrentMachineExtruders()
 
@@ -41,6 +41,19 @@ class ExtruderManager(QObject):
         except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong.
             return None
 
+    @pyqtProperty(int, notify = extrudersChanged)
+    def extruderCount(self):
+        if not UM.Application.getInstance().getGlobalContainerStack():
+            return 0 # No active machine, so no extruders.
+        return len(self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()])
+
+    @pyqtProperty("QVariantMap", notify=extrudersChanged)
+    def extruderIds(self):
+        map = {}
+        for position in self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()]:
+            map[position] = self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()][position].getId()
+        return map
+
     ##  The instance of the singleton pattern.
     #
     #   It's None if the extruder manager hasn't been created yet.
@@ -106,8 +119,11 @@ class ExtruderManager(QObject):
             for extruder_train in extruder_trains:
                 self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train
 
-                # Ensure that the extruder train stacks are linked to global stack.
-                extruder_train.setNextStack(UM.Application.getInstance().getGlobalContainerStack())
+                # Make sure the next stack is a stack that contains only the machine definition
+                if not extruder_train.getNextStack():
+                    shallowStack = UM.Settings.ContainerStack(machine_id + "_shallow")
+                    shallowStack.addContainer(machine_definition)
+                    extruder_train.setNextStack(shallowStack)
                 changed = True
         if changed:
             self.extrudersChanged.emit(machine_id)
@@ -220,7 +236,11 @@ class ExtruderManager(QObject):
             container_registry.addContainer(user_profile)
         container_stack.addContainer(user_profile)
 
-        container_stack.setNextStack(UM.Application.getInstance().getGlobalContainerStack())
+        # Make sure the next stack is a stack that contains only the machine definition
+        if not container_stack.getNextStack():
+            shallowStack = UM.Settings.ContainerStack(machine_id + "_shallow")
+            shallowStack.addContainer(machine_definition)
+            container_stack.setNextStack(shallowStack)
 
         container_registry.addContainer(container_stack)
 

+ 1 - 1
cura/Settings/ExtrudersModel.py

@@ -120,7 +120,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
                 material = extruder.findContainer({ "type": "material" })
                 if material:
                     extruder_name = "%s (%s)" % (material.getName(), extruder_name)
-                position = extruder.getBottom().getMetaDataEntry("position", default = "0") #Position in the definition.
+                position = extruder.getMetaDataEntry("position", default = "0")  # Get the position
                 try:
                     position = int(position)
                 except ValueError: #Not a proper int.

+ 113 - 6
cura/Settings/MachineManager.py

@@ -50,6 +50,8 @@ class MachineManager(QObject):
 
         Preferences.getInstance().addPreference("cura/active_machine", "")
 
+        self._global_event_keys = set()
+
         active_machine_id = Preferences.getInstance().getValue("cura/active_machine")
 
         self._printer_output_devices = []
@@ -58,7 +60,9 @@ class MachineManager(QObject):
         if active_machine_id != "":
             # An active machine was saved, so restore it.
             self.setActiveMachine(active_machine_id)
-            pass
+            if self._global_container_stack and self._global_container_stack.getProperty("machine_extruder_count", "value") > 1:
+                # Make sure _active_container_stack is properly initiated
+                ExtruderManager.getInstance().setActiveExtruderIndex(0)
 
         self._auto_change_material_hotend_flood_window = 10 # The minimum number of seconds between asking if the material or hotend on the machine should be used
         self._auto_change_material_hotend_flood_time = 0 # The last timestamp (in seconds) when the user was asked about changing the material or hotend to whatis loaded on the machine
@@ -197,7 +201,85 @@ class MachineManager(QObject):
 
     def _onGlobalPropertyChanged(self, key, property_name):
         if property_name == "value":
+            ## We can get recursion issues. So we store a list of keys that we are still handling to prevent this.
+            if key in self._global_event_keys:
+                return
+            self._global_event_keys.add(key)
             self.globalValueChanged.emit()
+
+            if self._active_container_stack and self._active_container_stack != self._global_container_stack:
+                # Make the global current settings mirror the stack values appropriate for this setting
+                if self._active_container_stack.getProperty("extruder_nr", "value") == int(self._active_container_stack.getProperty(key, "global_inherits_stack")):
+
+                    new_value = self._active_container_stack.getProperty(key, "value")
+                    self._global_container_stack.getTop().setProperty(key, "value", new_value)
+
+                # Global-only setting values should be set on all extruders and the global stack
+                if not self._global_container_stack.getProperty(key, "settable_per_extruder"):
+                    extruder_stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
+                    target_stack_position = int(self._active_container_stack.getProperty(key, "global_inherits_stack"))
+                    if target_stack_position == -1:  # Prevent -1 from selecting wrong stack.
+                        target_stack = self._active_container_stack
+                    else:
+                        target_stack = extruder_stacks[target_stack_position]
+                    new_value = target_stack.getProperty(key, "value")
+                    target_stack_has_user_value = target_stack.getTop().getInstance(key) != None
+                    for extruder_stack in extruder_stacks:
+                        if extruder_stack != target_stack:
+                            if target_stack_has_user_value:
+                                extruder_stack.getTop().setProperty(key, "value", new_value)
+                            else:
+                                # Remove from the value from the other stacks as well, unless the
+                                # top value from the other stacklevels is different than the new value
+                                for container in extruder_stack.getContainers():
+                                    if container.__class__ == UM.Settings.InstanceContainer and container.getInstance(key) != None:
+                                        if container.getProperty(key, "value") != new_value:
+                                            # It could be that the setting needs to be removed instead of updated.
+                                            temp = extruder_stack
+                                            containers = extruder_stack.getContainers()
+                                            # Ensure we have the entire 'chain'
+                                            while temp.getNextStack():
+                                                temp = temp.getNextStack()
+                                                containers.extend(temp.getContainers())
+                                            instance_needs_removal = False
+
+                                            if len(containers) > 1:
+                                                for index in range(1, len(containers)):
+                                                    deeper_container = containers[index]
+                                                    if deeper_container.getProperty(key, "value") is None:
+                                                        continue  # Deeper container does not have the value, so continue.
+                                                    if deeper_container.getProperty(key, "value") == new_value:
+                                                        # Removal will result in correct value, so do that.
+                                                        # We do this to prevent the reset from showing up unneeded.
+                                                        instance_needs_removal = True
+                                                        break
+                                                    else:
+                                                        # Container has the value, but it's not the same. Stop looking.
+                                                        break
+                                            if instance_needs_removal:
+                                                extruder_stack.getTop().removeInstance(key)
+                                            else:
+                                                extruder_stack.getTop().setProperty(key, "value", new_value)
+                                        else:
+                                            # Check if we really need to remove something.
+                                            if extruder_stack.getProperty(key, "value") != new_value:
+                                                extruder_stack.getTop().removeInstance(key)
+                                        break
+                    if self._global_container_stack.getProperty(key, "value") != new_value:
+                        self._global_container_stack.getTop().setProperty(key, "value", new_value)
+            self._global_event_keys.remove(key)
+
+        if property_name == "global_inherits_stack":
+            if self._active_container_stack and self._active_container_stack != self._global_container_stack:
+                # Update the global user value when the "global_inherits_stack" function points to a different stack
+                stack_index = int(self._active_container_stack.getProperty(key, property_name))
+                extruder_stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())]
+
+                if len(extruder_stacks) > stack_index:
+                    new_value = extruder_stacks[stack_index].getProperty(key, "value")
+                    if self._global_container_stack.getProperty(key, "value") != new_value:
+                        self._global_container_stack.getTop().setProperty(key, "value", new_value)
+
         if property_name == "validationState":
             if self._global_stack_valid:
                 changed_validation_state = self._active_container_stack.getProperty(key, property_name)
@@ -209,7 +291,6 @@ class MachineManager(QObject):
                 if not has_errors:
                     self._global_stack_valid = True
                     self.globalValidationChanged.emit()
-
     def _onGlobalContainerChanged(self):
         if self._global_container_stack:
             self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged)
@@ -233,7 +314,7 @@ class MachineManager(QObject):
             self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
             self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
             self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack)
-
+            self.globalValidationChanged.emit()
             material = self._global_container_stack.findContainer({"type": "material"})
             material.nameChanged.connect(self._onMaterialNameChanged)
 
@@ -254,6 +335,18 @@ class MachineManager(QObject):
 
     def _onInstanceContainersChanged(self, container):
         container_type = container.getMetaDataEntry("type")
+
+        if self._active_container_stack and self._active_container_stack != self._global_container_stack:
+            if int(self._active_container_stack.getProperty("extruder_nr", "value")) == 0:
+                global_container = self._global_container_stack.findContainer({"type": container_type})
+                if global_container and global_container != container:
+                    container_index = self._global_container_stack.getContainerIndex(global_container)
+                    self._global_container_stack.replaceContainer(container_index, container)
+
+            for key in container.getAllKeys():
+                # Make sure the values in this profile are distributed to other stacks if necessary
+                self._onGlobalPropertyChanged(key, "value")
+
         if container_type == "material":
             self.activeMaterialChanged.emit()
         elif container_type == "variant":
@@ -269,13 +362,14 @@ class MachineManager(QObject):
 
     @pyqtSlot(str, str)
     def addMachine(self, name, definition_id):
-        definitions = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = definition_id)
+        container_registry = UM.Settings.ContainerRegistry.getInstance()
+        definitions = container_registry.findDefinitionContainers(id = definition_id)
         if definitions:
             definition = definitions[0]
             name = self._createUniqueName("machine", "", name, definition.getName())
             new_global_stack = UM.Settings.ContainerStack(name)
             new_global_stack.addMetaDataEntry("type", "machine")
-            UM.Settings.ContainerRegistry.getInstance().addContainer(new_global_stack)
+            container_registry.addContainer(new_global_stack)
 
             variant_instance_container = self._updateVariantContainer(definition)
             material_instance_container = self._updateMaterialContainer(definition, variant_instance_container)
@@ -285,7 +379,7 @@ class MachineManager(QObject):
             current_settings_instance_container.addMetaDataEntry("machine", name)
             current_settings_instance_container.addMetaDataEntry("type", "user")
             current_settings_instance_container.setDefinition(definitions[0])
-            UM.Settings.ContainerRegistry.getInstance().addContainer(current_settings_instance_container)
+            container_registry.addContainer(current_settings_instance_container)
 
             # If a definition is found, its a list. Should only have one item.
             new_global_stack.addContainer(definition)
@@ -418,6 +512,19 @@ class MachineManager(QObject):
             return True
         return containers[0].isReadOnly()
 
+    ## Copy the value of the setting of the current extruder to all other extruders as well as the global container.
+    @pyqtSlot(str)
+    def copyValueToExtruders(self, key):
+        if not self._active_container_stack or self._global_container_stack.getProperty("machine_extruder_count", "value") <= 1:
+            return
+
+        new_value = self._active_container_stack.getProperty(key, "value")
+        stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())]
+        stacks.append(self._global_container_stack)
+        for extruder_stack in stacks:
+            if extruder_stack != self._active_container_stack and extruder_stack.getProperty(key, "value") != new_value:
+                extruder_stack.getTop().setProperty(key, "value", new_value)
+
     @pyqtSlot(result = str)
     def newQualityContainerFromQualityAndUser(self):
         new_container_id = self.duplicateContainer(self.activeQualityId)

+ 2 - 0
plugins/3MFReader/ThreeMFReader.py

@@ -111,6 +111,8 @@ class ThreeMFReader(MeshReader):
             if len(objects) > 1:
                 group_decorator = GroupDecorator()
                 result.addDecorator(group_decorator)
+            elif len(objects) == 1:
+                result = result.getChildren()[0] # Only one object found, return that.
         except Exception as e:
             Logger.log("e", "exception occured in 3mf reader: %s", e)
 

+ 1 - 1
plugins/LegacyProfileReader/DictionaryOfDoom.json

@@ -47,7 +47,7 @@
         "adhesion_type": "\"skirt\" if (platform_adhesion == \"None\") else platform_adhesion.lower()",
         "skirt_line_count": "skirt_line_count",
         "skirt_gap": "skirt_gap",
-        "skirt_minimal_length": "skirt_minimal_length",
+        "skirt_brim_minimal_length": "skirt_minimal_length",
         "brim_line_count": "brim_line_count",
         "raft_margin": "raft_margin",
         "raft_airgap": "float(raft_airgap_all) + float(raft_airgap)",

Some files were not shown because too many files changed in this diff