Browse Source

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

Ghostkeeper 5 years ago
parent
commit
7f969528f3

+ 1 - 1
cura/BuildVolume.py

@@ -1102,7 +1102,7 @@ class BuildVolume(SceneNode):
 
         # If we are printing one at a time, we need to add the bed adhesion size to the disallowed areas of the objects
         if container_stack.getProperty("print_sequence", "value") == "one_at_a_time":
-            return 0.1  # Return a very small value, so we do draw disallowed area's near the edges.
+            return 0.1
 
         bed_adhesion_size = self._calculateBedAdhesionSize(used_extruders)
         support_expansion = self._calculateSupportExpansion(self._global_container_stack)

+ 1 - 1
cura/CuraApplication.py

@@ -145,7 +145,7 @@ class CuraApplication(QtApplication):
     # SettingVersion represents the set of settings available in the machine/extruder definitions.
     # You need to make sure that this version number needs to be increased if there is any non-backwards-compatible
     # changes of the settings.
-    SettingVersion = 10
+    SettingVersion = 11
 
     Created = False
 

+ 49 - 26
cura/Scene/ConvexHullDecorator.py

@@ -88,27 +88,34 @@ class ConvexHullDecorator(SceneNodeDecorator):
 
         return self._add2DAdhesionMargin(hull)
 
-    ##  Get the unmodified 2D projected convex hull with 2D adhesion area of the node (if any)
+    ##  Get the unmodified 2D projected convex hull of the node (if any)
+    #   In case of one-at-a-time, this includes adhesion and head+fans clearance
     def getConvexHull(self) -> Optional[Polygon]:
         if self._node is None:
             return None
         if self._node.callDecoration("isNonPrintingMesh"):
             return None
-        hull = self._compute2DConvexHull()
 
-        if self._global_stack and self._node is not None and hull is not None:
-            # Parent can be None if node is just loaded.
-            if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self.hasGroupAsParent(self._node):
-                hull = hull.getMinkowskiHull(Polygon(numpy.array(self._global_stack.getProperty("machine_head_polygon", "value"), numpy.float32)))
-                hull = self._add2DAdhesionMargin(hull)
-        return hull
+        # Parent can be None if node is just loaded.
+        if self._isSingularOneAtATimeNode():
+            hull = self.getConvexHullHeadFull()
+            if hull is None:
+                return None
+            hull = self._add2DAdhesionMargin(hull)
+            return hull
+
+        return self._compute2DConvexHull()
 
-    ##  Get the convex hull of the node with the full head size
+    ##  For one at the time this is the convex hull of the node with the full head size
+    #   In case of printing all at once this is None.
     def getConvexHullHeadFull(self) -> Optional[Polygon]:
         if self._node is None:
             return None
 
-        return self._compute2DConvexHeadFull()
+        if self._isSingularOneAtATimeNode():
+            return self._compute2DConvexHeadFull()
+
+        return None
 
     @staticmethod
     def hasGroupAsParent(node: "SceneNode") -> bool:
@@ -118,38 +125,47 @@ class ConvexHullDecorator(SceneNodeDecorator):
         return bool(parent.callDecoration("isGroup"))
 
     ##  Get convex hull of the object + head size
-    #   In case of printing all at once this is the same as the convex hull.
+    #   In case of printing all at once this is None.
     #   For one at the time this is area with intersection of mirrored head
     def getConvexHullHead(self) -> Optional[Polygon]:
         if self._node is None:
             return None
         if self._node.callDecoration("isNonPrintingMesh"):
             return None
-        if self._global_stack:
-            if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self.hasGroupAsParent(self._node):
-                head_with_fans = self._compute2DConvexHeadMin()
-                if head_with_fans is None:
-                    return None
-                head_with_fans_with_adhesion_margin = self._add2DAdhesionMargin(head_with_fans)
-                return head_with_fans_with_adhesion_margin
+        if self._isSingularOneAtATimeNode():
+            head_with_fans = self._compute2DConvexHeadMin()
+            if head_with_fans is None:
+                return None
+            head_with_fans_with_adhesion_margin = self._add2DAdhesionMargin(head_with_fans)
+            return head_with_fans_with_adhesion_margin
         return None
 
     ##  Get convex hull of the node
-    #   In case of printing all at once this is the same as the convex hull.
+    #   In case of printing all at once this None??
     #   For one at the time this is the area without the head.
     def getConvexHullBoundary(self) -> Optional[Polygon]:
         if self._node is None:
             return None
-        
+
         if self._node.callDecoration("isNonPrintingMesh"):
             return None
 
-        if self._global_stack:
-            if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self.hasGroupAsParent(self._node):
-                # Printing one at a time and it's not an object in a group
-                return self._compute2DConvexHull()
+        if self._isSingularOneAtATimeNode():
+            # Printing one at a time and it's not an object in a group
+            return self._compute2DConvexHull()
         return None
 
+    ## Get the buildplate polygon where will be printed
+    #   In case of printing all at once this is the same as convex hull (no individual adhesion)
+    #   For one at the time this includes the adhesion area
+    def getPrintingArea(self) -> Optional[Polygon]:
+        if self._isSingularOneAtATimeNode():
+            # In one-at-a-time mode, every printed object gets it's own adhesion
+            printing_area = self.getAdhesionArea()
+        else:
+            printing_area = self.getConvexHull()
+        return printing_area
+
     ##  The same as recomputeConvexHull, but using a timer if it was set.
     def recomputeConvexHullDelayed(self) -> None:
         if self._recompute_convex_hull_timer is not None:
@@ -172,10 +188,9 @@ class ConvexHullDecorator(SceneNodeDecorator):
                 self._convex_hull_node = None
             return
 
-        convex_hull = self.getConvexHull()
         if self._convex_hull_node:
             self._convex_hull_node.setParent(None)
-        hull_node = ConvexHullNode.ConvexHullNode(self._node, convex_hull, self._raft_thickness, root)
+        hull_node = ConvexHullNode.ConvexHullNode(self._node, self.getPrintingArea(), self._raft_thickness, root)
         self._convex_hull_node = hull_node
 
     def _onSettingValueChanged(self, key: str, property_name: str) -> None:
@@ -416,6 +431,14 @@ class ConvexHullDecorator(SceneNodeDecorator):
             return True
         return self.__isDescendant(root, node.getParent())
 
+    ## True if print_sequence is one_at_a_time and _node is not part of a group
+    def _isSingularOneAtATimeNode(self) -> bool:
+        if self._node is None:
+            return False
+        return self._global_stack is not None \
+            and self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" \
+            and not self.hasGroupAsParent(self._node)
+
     _affected_settings = [
         "adhesion_type", "raft_margin", "print_sequence",
         "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "skirt_distance", "brim_line_count"]

+ 5 - 1
cura/Scene/ConvexHullNode.py

@@ -46,6 +46,7 @@ class ConvexHullNode(SceneNode):
 
         # The node this mesh is "watching"
         self._node = node
+        # Area of the head + fans for display as a shadow on the buildplate
         self._convex_hull_head_mesh = None  # type: Optional[MeshData]
 
         self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged)
@@ -79,14 +80,17 @@ class ConvexHullNode(SceneNode):
 
         if self.getParent():
             if self.getMeshData() and isinstance(self._node, SceneNode) and self._node.callDecoration("getBuildPlateNumber") == Application.getInstance().getMultiBuildPlateModel().activeBuildPlate:
+                # The object itself (+ adhesion in one-at-a-time mode)
                 renderer.queueNode(self, transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8)
                 if self._convex_hull_head_mesh:
+                    # The full head. Rendered as a hint to the user: If this area overlaps another object A; this object
+                    # cannot be printed after A, because the head would hit A while printing the current object
                     renderer.queueNode(self, shader = ConvexHullNode.shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8)
 
         return True
 
     def _onNodeDecoratorsChanged(self, node: SceneNode) -> None:
-        convex_hull_head = self._node.callDecoration("getConvexHullHead")
+        convex_hull_head = self._node.callDecoration("getConvexHullHeadFull")
         if convex_hull_head:
             convex_hull_head_builder = MeshBuilder()
             convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height - self._thickness)

+ 1 - 1
cura/Scene/CuraSceneNode.py

@@ -88,7 +88,7 @@ class CuraSceneNode(SceneNode):
 
     ##  Return if any area collides with the convex hull of this scene node
     def collidesWithAreas(self, areas: List[Polygon]) -> bool:
-        convex_hull = self.callDecoration("getConvexHull")
+        convex_hull = self.callDecoration("getPrintingArea")
         if convex_hull:
             if not convex_hull.isValid():
                 return False

+ 69 - 0
plugins/VersionUpgrade/VersionUpgrade44to45/VersionUpgrade44to45.py

@@ -0,0 +1,69 @@
+import configparser
+from typing import Tuple, List
+import io
+from UM.VersionUpgrade import VersionUpgrade
+
+# Merged preferences: machine_head_polygon and machine_head_with_fans_polygon -> machine_head_with_fans_polygon
+# When both are present, machine_head_polygon will be removed
+# When only one of the two is present, it's value will be used
+
+
+class VersionUpgrade44to45(VersionUpgrade):
+    def getCfgVersion(self, serialised: str) -> int:
+        parser = configparser.ConfigParser(interpolation = None, comment_prefixes=())
+        parser.read_string(serialised)
+        format_version = int(parser.get("general", "version"))  # Explicitly give an exception when this fails. That means that the file format is not recognised.
+        setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
+        return format_version * 1000000 + setting_version
+
+    ##  Upgrades Preferences to have the new version number.
+    #
+    #   This renames the renamed settings in the list of visible settings.
+    def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
+        parser = configparser.ConfigParser(interpolation = None)
+        parser.read_string(serialized)
+
+        # Update version number.
+        parser["metadata"]["setting_version"] = "11"
+
+        result = io.StringIO()
+        parser.write(result)
+        return [filename], [result.getvalue()]
+
+    ##  Upgrades instance containers to have the new version
+    #   number.
+    #
+    #   This renames the renamed settings in the containers.
+    def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
+        parser = configparser.ConfigParser(interpolation = None)
+        parser.read_string(serialized)
+
+        # Update version number.
+        parser["metadata"]["setting_version"] = "11"
+
+        if "values" in parser:
+            # merge machine_head_with_fans_polygon (preferred) and machine_head_polygon
+            if "machine_head_with_fans_polygon" in parser["values"]:
+                if "machine_head_polygon" in parser["values"]:
+                    del parser["values"]["machine_head_polygon"]
+            elif "machine_head_polygon" in parser["values"]:
+                parser["values"]["machine_head_with_fans_polygon"] = parser["values"]["machine_head_polygon"]
+                del parser["values"]["machine_head_polygon"]
+
+        result = io.StringIO()
+        parser.write(result)
+        return [filename], [result.getvalue()]
+
+    ##  Upgrades stacks to have the new version number.
+    def upgradeStack(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
+        parser = configparser.ConfigParser(interpolation = None)
+        parser.read_string(serialized)
+
+        # Update version number.
+        if "metadata" not in parser:
+            parser["metadata"] = {}
+        parser["metadata"]["setting_version"] = "11"
+
+        result = io.StringIO()
+        parser.write(result)
+        return [filename], [result.getvalue()]

+ 61 - 0
plugins/VersionUpgrade/VersionUpgrade44to45/__init__.py

@@ -0,0 +1,61 @@
+# Copyright (c) 2019 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from typing import Any, Dict, TYPE_CHECKING
+
+from . import VersionUpgrade44to45
+
+
+if TYPE_CHECKING:
+    from UM.Application import Application
+
+upgrade = VersionUpgrade44to45.VersionUpgrade44to45()
+
+
+def getMetaData() -> Dict[str, Any]:
+    return {
+        "version_upgrade": {
+            # From                           To                              Upgrade function
+            ("preferences", 6000010): ("preferences", 6000011, upgrade.upgradePreferences),
+            ("machine_stack", 4000010): ("machine_stack", 4000011, upgrade.upgradeStack),
+            ("extruder_train", 4000010): ("extruder_train", 4000011, upgrade.upgradeStack),
+            ("definition_changes", 4000010): ("definition_changes", 4000011, upgrade.upgradeInstanceContainer),
+            ("quality_changes", 4000010): ("quality_changes", 4000011, upgrade.upgradeInstanceContainer),
+            ("quality", 4000010): ("quality", 4000011, upgrade.upgradeInstanceContainer),
+            ("user", 4000010): ("user", 4000011, upgrade.upgradeInstanceContainer),
+        },
+        "sources": {
+            "preferences": {
+                "get_version": upgrade.getCfgVersion,
+                "location": {"."}
+            },
+            "machine_stack": {
+                "get_version": upgrade.getCfgVersion,
+                "location": {"./machine_instances"}
+            },
+            "extruder_train": {
+                "get_version": upgrade.getCfgVersion,
+                "location": {"./extruders"}
+            },
+            "definition_changes": {
+                "get_version": upgrade.getCfgVersion,
+                "location": {"./definition_changes"}
+            },
+            "quality_changes": {
+                "get_version": upgrade.getCfgVersion,
+                "location": {"./quality_changes"}
+            },
+            "quality": {
+                "get_version": upgrade.getCfgVersion,
+                "location": {"./quality"}
+            },
+            "user": {
+                "get_version": upgrade.getCfgVersion,
+                "location": {"./user"}
+            }
+        }
+    }
+
+
+def register(app: "Application") -> Dict[str, Any]:
+    return {"version_upgrade": upgrade}

+ 8 - 0
plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json

@@ -0,0 +1,8 @@
+{
+    "name": "Version Upgrade 4.4 to 4.5",
+    "author": "Ultimaker B.V.",
+    "version": "1.0.0",
+    "description": "Upgrades configurations from Cura 4.4 to Cura 4.5.",
+    "api": "7.0",
+    "i18n-catalog": "cura"
+}

+ 42 - 0
plugins/VersionUpgrade/VersionUpgrade44to45/tests/TestVersionUpgrade44To45.py

@@ -0,0 +1,42 @@
+import configparser
+
+import VersionUpgrade44to45
+import pytest
+
+before_update = """[general]
+version = 4
+name = Creality CR-10S_settings
+definition = creality_cr10s
+
+[metadata]
+type = definition_changes
+setting_version = 11
+
+[values]
+%s
+"""
+before_after_list = [
+        ("machine_head_with_fans_polygon = [[-99, 99], [-99, -44], [45, 99], [45, -44]]", "[[-99, 99], [-99, -44], [45, 99], [45, -44]]"),
+        ("", None),
+        ("machine_head_polygon = [[-98, 99], [-99, -44], [45, 99], [45, -44]]", "[[-98, 99], [-99, -44], [45, 99], [45, -44]]"),
+        ("machine_head_polygon = [[-87, 99], [-99, -44], [45, 99], [45, -44]]\nmachine_head_with_fans_polygon = [[-99, 99], [-99, -44], [45, 99], [45, -44]]", "[[-99, 99], [-99, -44], [45, 99], [45, -44]]"),
+    ]
+
+
+class TestVersionUpgrade44to45:
+
+    @pytest.mark.parametrize("after_string, after_value", before_after_list)
+    def test_upgrade(self, after_string, after_value):
+        upgrader = VersionUpgrade44to45.VersionUpgrade44to45()
+
+
+        file_name, new_data =  upgrader.upgradeInstanceContainer(before_update % after_string, "whatever")
+        parser = configparser.ConfigParser(interpolation=None)
+        parser.read_string(new_data[0])
+
+        if after_value is None:
+            assert "machine_head_with_fans_polygon" not in parser["values"]
+        else:
+            assert parser["values"]["machine_head_with_fans_polygon"] == after_value
+
+        assert "machine_head_polygon" not in parser["values"]

+ 1 - 1
resources/definitions/builder_premium_large.def.json

@@ -88,7 +88,7 @@
         "adhesion_type": { "default_value": "skirt" },
         "machine_nozzle_heat_up_speed": { "default_value": 2 },
         "machine_nozzle_cool_down_speed": { "default_value": 2 },
-        "machine_head_polygon": { "default_value": [[-75, -18],[-75, 35],[18, 35],[18, -18]] },
+        "machine_head_with_fans_polygon": { "default_value": [[-75, -18],[-75, 35],[18, 35],[18, -18]] },
         "gantry_height": { "value": "55" },
         "machine_max_feedrate_x": { "default_value": 300 },
         "machine_max_feedrate_y": { "default_value": 300 },

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