Welcome to mirror list, hosted at ThFree Co, Russian Federation.

OneAtATimeIterator.py « cura - github.com/Ultimaker/Cura.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: e7ad833e1c089a833d309077aa71ab1b90876d3e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.

import sys

from shapely import affinity
from shapely.geometry import Polygon

from UM.Scene.Iterator.Iterator import Iterator
from UM.Scene.SceneNode import SceneNode


# Iterator that determines the object print order when one-at a time mode is enabled.
#
# In one-at-a-time mode, only one extruder can be enabled to print. In order to maximize the number of objects we can
# print, we need to print from the corner that's closest to the extruder that's being used. Here is an illustration:
#
#  +--------------------------------+
#  |                                |
#  |                                |
#  |                                |          - Rectangle represents the complete print head including fans, etc.
#  |       X                X       |    y     - X's are the nozzles
#  |      (1)              (2)      |    ^
#  |                                |    |
#  +--------------------------------+    +--> x
#
# In this case, the nozzles are symmetric, nozzle (1) is closer to the bottom left corner while (2) is closer to the
# bottom right. If we use nozzle (1) to print, then we better off printing from the bottom left corner so the print
# head will not collide into an object on its top-right side, which is a very large unused area. Following the same
# logic, if we are printing with nozzle (2), then it's better to print from the bottom-right side.
#
# This iterator determines the print order following the rules above.
#
class OneAtATimeIterator(Iterator):

    def __init__(self, scene_node):
        from cura.CuraApplication import CuraApplication
        self._global_stack = CuraApplication.getInstance().getGlobalContainerStack()
        self._original_node_list = []

        super().__init__(scene_node)  # Call super to make multiple inheritance work.

    def getMachineNearestCornerToExtruder(self, global_stack):
        head_and_fans_coordinates = global_stack.getHeadAndFansCoordinates()

        used_extruder = None
        for extruder in global_stack.extruders.values():
            if extruder.isEnabled:
                used_extruder = extruder
                break

        extruder_offsets = [used_extruder.getProperty("machine_nozzle_offset_x", "value"),
                            used_extruder.getProperty("machine_nozzle_offset_y", "value")]

        # find the corner that's closest to the origin
        min_distance2 = sys.maxsize
        min_coord = None
        for coord in head_and_fans_coordinates:
            x = coord[0] - extruder_offsets[0]
            y = coord[1] - extruder_offsets[1]

            distance2 = x**2 + y**2
            if distance2 <= min_distance2:
                min_distance2 = distance2
                min_coord = coord

        return min_coord

    def _checkForCollisions(self) -> bool:
        all_nodes = []
        for node in self._scene_node.getChildren():
            if not issubclass(type(node), SceneNode):
                continue
            convex_hull = node.callDecoration("getConvexHullHead")
            if not convex_hull:
                continue

            bounding_box = node.getBoundingBox()
            from UM.Math.Polygon import Polygon
            bounding_box_polygon = Polygon([[bounding_box.left, bounding_box.front],
                                            [bounding_box.left, bounding_box.back],
                                            [bounding_box.right, bounding_box.back],
                                            [bounding_box.right, bounding_box.front]])

            all_nodes.append({"node": node,
                              "bounding_box": bounding_box_polygon,
                              "convex_hull": convex_hull})

        has_collisions = False
        for i, node_dict in enumerate(all_nodes):
            for j, other_node_dict in enumerate(all_nodes):
                if i == j:
                    continue
                if node_dict["bounding_box"].intersectsPolygon(other_node_dict["convex_hull"]):
                    has_collisions = True
                    break

            if has_collisions:
                break

        return has_collisions

    def _fillStack(self):
        min_coord = self.getMachineNearestCornerToExtruder(self._global_stack)
        transform_x = -int(round(min_coord[0] / abs(min_coord[0])))
        transform_y = -int(round(min_coord[1] / abs(min_coord[1])))

        machine_size = [self._global_stack.getProperty("machine_width", "value"),
                        self._global_stack.getProperty("machine_depth", "value")]

        def flip_x(polygon):
            tm2 = [-1, 0, 0, 1, 0, 0]
            return affinity.affine_transform(affinity.translate(polygon, xoff = -machine_size[0]), tm2)

        def flip_y(polygon):
            tm2 = [1, 0, 0, -1, 0, 0]
            return affinity.affine_transform(affinity.translate(polygon, yoff = -machine_size[1]), tm2)

        if self._checkForCollisions():
            self._node_stack = []
            return

        node_list = []
        for node in self._scene_node.getChildren():
            if not issubclass(type(node), SceneNode):
                continue

            convex_hull = node.callDecoration("getConvexHull")
            if convex_hull:
                xmin = min(x for x, _ in convex_hull._points)
                xmax = max(x for x, _ in convex_hull._points)
                ymin = min(y for _, y in convex_hull._points)
                ymax = max(y for _, y in convex_hull._points)

                convex_hull_polygon = Polygon.from_bounds(xmin, ymin, xmax, ymax)
                if transform_x < 0:
                    convex_hull_polygon = flip_x(convex_hull_polygon)
                if transform_y < 0:
                    convex_hull_polygon = flip_y(convex_hull_polygon)

                node_list.append({"node": node,
                                  "min_coord": [convex_hull_polygon.bounds[0], convex_hull_polygon.bounds[1]],
                                  })

        node_list = sorted(node_list, key = lambda d: d["min_coord"])

        self._node_stack = [d["node"] for d in node_list]