예제 #1
0
def test_getConvexHullPrintingMesh(convex_hull_decorator):
    node = SceneNode()
    node.addDecorator(PrintingDecorator())
    with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)):
        convex_hull_decorator.setNode(node)
    convex_hull_decorator._compute2DConvexHull = MagicMock(return_value = Polygon.approximatedCircle(10))
    assert convex_hull_decorator.getConvexHull() == Polygon.approximatedCircle(10)
예제 #2
0
파일: BuildVolume.py 프로젝트: daid/Cura
    def _computeDisallowedAreasPrime(self, border_size, used_extruders):
        result = {}

        machine_width = self._global_container_stack.getProperty("machine_width", "value")
        machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
        for extruder in used_extruders:
            prime_x = extruder.getProperty("extruder_prime_pos_x", "value")
            prime_y = - extruder.getProperty("extruder_prime_pos_y", "value")

            #Ignore extruder prime position if it is not set
            if prime_x == 0 and prime_y == 0:
                result[extruder.getId()] = []
                continue

            if not self._global_container_stack.getProperty("machine_center_is_zero", "value"):
                prime_x = prime_x - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
                prime_y = prime_y + machine_depth / 2

            prime_polygon = Polygon.approximatedCircle(PRIME_CLEARANCE)
            prime_polygon = prime_polygon.getMinkowskiHull(Polygon.approximatedCircle(border_size))

            prime_polygon = prime_polygon.translate(prime_x, prime_y)
            result[extruder.getId()] = [prime_polygon]

        return result
예제 #3
0
    def _computeDisallowedAreasPrimeBlob(self, border_size, used_extruders):
        result = {}

        machine_width = self._global_container_stack.getProperty("machine_width", "value")
        machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
        for extruder in used_extruders:
            prime_blob_enabled = extruder.getProperty("prime_blob_enable", "value")
            prime_x = extruder.getProperty("extruder_prime_pos_x", "value")
            prime_y = - extruder.getProperty("extruder_prime_pos_y", "value")

            #Ignore extruder prime position if it is not set or if blob is disabled
            if (prime_x == 0 and prime_y == 0) or not prime_blob_enabled:
                result[extruder.getId()] = []
                continue

            if not self._global_container_stack.getProperty("machine_center_is_zero", "value"):
                prime_x = prime_x - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
                prime_y = prime_y + machine_depth / 2

            prime_polygon = Polygon.approximatedCircle(PRIME_CLEARANCE)
            prime_polygon = prime_polygon.getMinkowskiHull(Polygon.approximatedCircle(border_size))

            prime_polygon = prime_polygon.translate(prime_x, prime_y)
            result[extruder.getId()] = [prime_polygon]

        return result
예제 #4
0
def test_getConvexHullPrintingMesh(convex_hull_decorator):
    node = SceneNode()
    node.addDecorator(PrintingDecorator())
    with patch("UM.Application.Application.getInstance",
               MagicMock(return_value=mocked_application)):
        convex_hull_decorator.setNode(node)
    convex_hull_decorator._compute2DConvexHull = MagicMock(
        return_value=Polygon.approximatedCircle(10))
    assert convex_hull_decorator.getConvexHull() == Polygon.approximatedCircle(
        10)
예제 #5
0
파일: BuildVolume.py 프로젝트: mifga/Cura
    def _computeDisallowedAreasPrime(self, border_size, used_extruders):
        result = {}

        machine_width = self._global_container_stack.getProperty("machine_width", "value")
        machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
        for extruder in used_extruders:
            prime_x = extruder.getProperty("extruder_prime_pos_x", "value") - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
            prime_y = machine_depth / 2 - extruder.getProperty("extruder_prime_pos_y", "value")

            prime_polygon = Polygon.approximatedCircle(PRIME_CLEARANCE)
            prime_polygon = prime_polygon.translate(prime_x, prime_y)
            prime_polygon = prime_polygon.getMinkowskiHull(Polygon.approximatedCircle(border_size))
            result[extruder.getId()] = [prime_polygon]

        return result
예제 #6
0
    def _computeDisallowedAreasPrime(self, border_size, used_extruders):
        result = {}

        machine_width = self._global_container_stack.getProperty("machine_width", "value")
        machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
        for extruder in used_extruders:
            prime_x = extruder.getProperty("extruder_prime_pos_x", "value") - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
            prime_y = machine_depth / 2 - extruder.getProperty("extruder_prime_pos_y", "value")

            prime_polygon = Polygon.approximatedCircle(PRIME_CLEARANCE)
            prime_polygon = prime_polygon.translate(prime_x, prime_y)
            prime_polygon = prime_polygon.getMinkowskiHull(Polygon.approximatedCircle(border_size))
            result[extruder.getId()] = [prime_polygon]

        return result
예제 #7
0
    def fromNode(cls, node, min_offset, scale=1):
        transform = node._transformation
        transform_x = transform._data[0][3]
        transform_y = transform._data[2][3]

        arrange_align = Preferences.getInstance().getValue(
            "mesh/arrange_align")
        if arrange_align:
            bb = node.getBoundingBox()
            bb_points = numpy.array(
                [[bb.right, bb.back], [bb.left, bb.back], [bb.left, bb.front],
                 [bb.right, bb.front]],
                dtype=numpy.float32)
            polygon = Polygon(bb_points)
        else:
            hull_verts = node.callDecoration("getConvexHull")
            # If a model is too small then it will not contain any points
            if hull_verts is None or not hull_verts.getPoints().any():
                return None, None
                # For one_at_a_time printing you need the convex hull head.
            polygon = node.callDecoration("getConvexHullHead") or hull_verts

        offset_verts = polygon.getMinkowskiHull(
            Polygon.approximatedCircle(min_offset))
        offset_points = copy.deepcopy(offset_verts._points)  # x, y
        offset_points[:, 0] = numpy.add(offset_points[:, 0], -transform_x)
        offset_points[:, 1] = numpy.add(offset_points[:, 1], -transform_y)
        offset_shape_arr = ShapeArray.fromPolygon(offset_points, scale=scale)

        hull_points = copy.deepcopy(polygon._points)
        hull_points[:, 0] = numpy.add(hull_points[:, 0], -transform_x)
        hull_points[:, 1] = numpy.add(hull_points[:, 1], -transform_y)
        hull_shape_arr = ShapeArray.fromPolygon(hull_points, scale=scale)

        return offset_shape_arr, hull_shape_arr
예제 #8
0
파일: BuildVolume.py 프로젝트: daid/Cura
    def _computeDisallowedAreasPrinted(self, used_extruders):
        result = {}
        for extruder in used_extruders:
            result[extruder.getId()] = []

        #Currently, the only normally printed object is the prime tower.
        if ExtruderManager.getInstance().getResolveOrValue("prime_tower_enable") == True:
            prime_tower_size = self._global_container_stack.getProperty("prime_tower_size", "value")
            machine_width = self._global_container_stack.getProperty("machine_width", "value")
            machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
            prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value")
            prime_tower_y = - self._global_container_stack.getProperty("prime_tower_position_y", "value")
            if not self._global_container_stack.getProperty("machine_center_is_zero", "value"):
                prime_tower_x = prime_tower_x - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
                prime_tower_y = prime_tower_y + machine_depth / 2

            prime_tower_area = Polygon([
                [prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size],
                [prime_tower_x, prime_tower_y - prime_tower_size],
                [prime_tower_x, prime_tower_y],
                [prime_tower_x - prime_tower_size, prime_tower_y],
            ])
            prime_tower_area = prime_tower_area.getMinkowskiHull(Polygon.approximatedCircle(0))
            for extruder in used_extruders:
                result[extruder.getId()].append(prime_tower_area) #The prime tower location is the same for each extruder, regardless of offset.

        return result
예제 #9
0
    def _add2DAdhesionMargin(self, poly: Polygon) -> Polygon:
        if not self._global_stack:
            return Polygon()
        # Compensate for raft/skirt/brim
        # Add extra margin depending on adhesion type
        adhesion_type = self._global_stack.getProperty("adhesion_type", "value")

        max_length_available = 0.5 * min(
            self._getSettingProperty("machine_width", "value"),
            self._getSettingProperty("machine_depth", "value")
        )

        if adhesion_type == "raft":
            extra_margin = min(max_length_available, max(0, self._getSettingProperty("raft_margin", "value")))
        elif adhesion_type == "brim":
            extra_margin = min(max_length_available, max(0, self._getSettingProperty("brim_line_count", "value") * self._getSettingProperty("skirt_brim_line_width", "value")))
        elif adhesion_type == "none":
            extra_margin = 0
        elif adhesion_type == "skirt":
            extra_margin = min(max_length_available, max(
                0, self._getSettingProperty("skirt_gap", "value") +
                   self._getSettingProperty("skirt_line_count", "value") * self._getSettingProperty("skirt_brim_line_width", "value")))
        else:
            raise Exception("Unknown bed adhesion type. Did you forget to update the convex hull calculations for your new bed adhesion type?")

        # Adjust head_and_fans with extra margin
        if extra_margin > 0:
            extra_margin_polygon = Polygon.approximatedCircle(extra_margin)
            poly = poly.getMinkowskiHull(extra_margin_polygon)
        return poly
예제 #10
0
    def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 350, y = 250, min_offset = 8):
        arranger = Arrange(x, y, x // 2, y // 2, scale = scale)
        arranger.centerFirst()

        if fixed_nodes is None:
            fixed_nodes = []
            for node_ in DepthFirstIterator(scene_root):
                # Only count sliceable objects
                if node_.callDecoration("isSliceable"):
                    fixed_nodes.append(node_)

        # Place all objects fixed nodes
        for fixed_node in fixed_nodes:
            vertices = fixed_node.callDecoration("getConvexHullHead") or fixed_node.callDecoration("getConvexHull")
            if not vertices:
                continue
            vertices = vertices.getMinkowskiHull(Polygon.approximatedCircle(min_offset))
            points = copy.deepcopy(vertices._points)

            # After scaling (like up to 0.1 mm) the node might not have points
            if not points.size:
                continue

            shape_arr = ShapeArray.fromPolygon(points, scale = scale)
            arranger.place(0, 0, shape_arr)

        # If a build volume was set, add the disallowed areas
        if Arrange.build_volume:
            disallowed_areas = Arrange.build_volume.getDisallowedAreasNoBrim()
            for area in disallowed_areas:
                points = copy.deepcopy(area._points)
                shape_arr = ShapeArray.fromPolygon(points, scale = scale)
                arranger.place(0, 0, shape_arr, update_empty = False)
        return arranger
예제 #11
0
    def fromNode(cls, node, min_offset, scale=0.5):
        transform = node._transformation
        transform_x = transform._data[0][3]
        transform_y = transform._data[2][3]
        hull_verts = node.callDecoration("getConvexHull")
        # If a model is too small then it will not contain any points
        if hull_verts is None or not hull_verts.getPoints().any():
            return None, None
        # For one_at_a_time printing you need the convex hull head.
        hull_head_verts = node.callDecoration(
            "getConvexHullHead") or hull_verts

        offset_verts = hull_head_verts.getMinkowskiHull(
            Polygon.approximatedCircle(min_offset))
        offset_points = copy.deepcopy(offset_verts._points)  # x, y
        offset_points[:, 0] = numpy.add(offset_points[:, 0], -transform_x)
        offset_points[:, 1] = numpy.add(offset_points[:, 1], -transform_y)
        offset_shape_arr = ShapeArray.fromPolygon(offset_points, scale=scale)

        hull_points = copy.deepcopy(hull_verts._points)
        hull_points[:, 0] = numpy.add(hull_points[:, 0], -transform_x)
        hull_points[:, 1] = numpy.add(hull_points[:, 1], -transform_y)
        hull_shape_arr = ShapeArray.fromPolygon(hull_points,
                                                scale=scale)  # x, y

        return offset_shape_arr, hull_shape_arr
예제 #12
0
    def _add2DAdhesionMargin(self, poly: Polygon) -> Polygon:
        if not self._global_stack:
            return Polygon()
        # Compensate for raft/skirt/brim
        # Add extra margin depending on adhesion type
        adhesion_type = self._global_stack.getProperty("adhesion_type",
                                                       "value")

        if adhesion_type == "raft":
            extra_margin = max(
                0, self._getSettingProperty("raft_margin", "value"))
        elif adhesion_type == "brim":
            extra_margin = max(
                0,
                self._getSettingProperty("brim_line_count", "value") *
                self._getSettingProperty("skirt_brim_line_width", "value"))
        elif adhesion_type == "none":
            extra_margin = 0
        elif adhesion_type == "skirt":
            extra_margin = max(
                0,
                self._getSettingProperty("skirt_gap", "value") +
                self._getSettingProperty("skirt_line_count", "value") *
                self._getSettingProperty("skirt_brim_line_width", "value"))
        else:
            raise Exception(
                "Unknown bed adhesion type. Did you forget to update the convex hull calculations for your new bed adhesion type?"
            )

        # Adjust head_and_fans with extra margin
        if extra_margin > 0:
            extra_margin_polygon = Polygon.approximatedCircle(extra_margin)
            poly = poly.getMinkowskiHull(extra_margin_polygon)
        return poly
예제 #13
0
    def _computeDisallowedAreasPrinted(self, used_extruders):
        result = {}
        for extruder in used_extruders:
            result[extruder.getId()] = []

        #Currently, the only normally printed object is the prime tower.
        if ExtruderManager.getInstance().getResolveOrValue("prime_tower_enable") == True:
            prime_tower_size = self._global_container_stack.getProperty("prime_tower_size", "value")
            machine_width = self._global_container_stack.getProperty("machine_width", "value")
            machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
            prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value")
            prime_tower_y = - self._global_container_stack.getProperty("prime_tower_position_y", "value")
            if not self._global_container_stack.getProperty("machine_center_is_zero", "value"):
                prime_tower_x = prime_tower_x - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
                prime_tower_y = prime_tower_y + machine_depth / 2

            prime_tower_area = Polygon([
                [prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size],
                [prime_tower_x, prime_tower_y - prime_tower_size],
                [prime_tower_x, prime_tower_y],
                [prime_tower_x - prime_tower_size, prime_tower_y],
            ])
            prime_tower_area = prime_tower_area.getMinkowskiHull(Polygon.approximatedCircle(0))
            for extruder in used_extruders:
                result[extruder.getId()].append(prime_tower_area) #The prime tower location is the same for each extruder, regardless of offset.

        return result
예제 #14
0
파일: Arrange.py 프로젝트: rwreynolds/Cura
    def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 350, y = 250, min_offset = 8):
        arranger = Arrange(x, y, x // 2, y // 2, scale = scale)
        arranger.centerFirst()

        if fixed_nodes is None:
            fixed_nodes = []
            for node_ in DepthFirstIterator(scene_root):
                # Only count sliceable objects
                if node_.callDecoration("isSliceable"):
                    fixed_nodes.append(node_)

        # Place all objects fixed nodes
        for fixed_node in fixed_nodes:
            vertices = fixed_node.callDecoration("getConvexHullHead") or fixed_node.callDecoration("getConvexHull")
            if not vertices:
                continue
            vertices = vertices.getMinkowskiHull(Polygon.approximatedCircle(min_offset))
            points = copy.deepcopy(vertices._points)

            # After scaling (like up to 0.1 mm) the node might not have points
            if len(points) == 0:
                continue

            shape_arr = ShapeArray.fromPolygon(points, scale = scale)
            arranger.place(0, 0, shape_arr)

        # If a build volume was set, add the disallowed areas
        if Arrange.build_volume:
            disallowed_areas = Arrange.build_volume.getDisallowedAreasNoBrim()
            for area in disallowed_areas:
                points = copy.deepcopy(area._points)
                shape_arr = ShapeArray.fromPolygon(points, scale = scale)
                arranger.place(0, 0, shape_arr, update_empty = False)
        return arranger
예제 #15
0
파일: Arrange.py 프로젝트: userosos/Cura-1
    def create(cls,
               scene_root=None,
               fixed_nodes=None,
               scale=0.5,
               x=350,
               y=250,
               min_offset=8) -> "Arrange":
        """Helper to create an :py:class:`cura.Arranging.Arrange.Arrange` instance

        Either fill in scene_root and create will find all sliceable nodes by itself, or use fixed_nodes to provide the
        nodes yourself.

        :param scene_root: Root for finding all scene nodes default = None
        :param fixed_nodes: Scene nodes to be placed default = None
        :param scale: default = 0.5
        :param x: default = 350
        :param y: default = 250
        :param min_offset: default = 8
        """

        arranger = Arrange(x, y, x // 2, y // 2, scale=scale)
        arranger.centerFirst()

        if fixed_nodes is None:
            fixed_nodes = []
            for node_ in DepthFirstIterator(scene_root):
                # Only count sliceable objects
                if node_.callDecoration("isSliceable"):
                    fixed_nodes.append(node_)

        # Place all objects fixed nodes
        for fixed_node in fixed_nodes:
            vertices = fixed_node.callDecoration(
                "getConvexHullHead") or fixed_node.callDecoration(
                    "getConvexHull")
            if not vertices:
                continue
            vertices = vertices.getMinkowskiHull(
                Polygon.approximatedCircle(min_offset))
            points = copy.deepcopy(vertices._points)

            # After scaling (like up to 0.1 mm) the node might not have points
            if not points.size:
                continue
            try:
                shape_arr = ShapeArray.fromPolygon(points, scale=scale)
            except ValueError:
                Logger.logException("w", "Unable to create polygon")
                continue
            arranger.place(0, 0, shape_arr)

        # If a build volume was set, add the disallowed areas
        if Arrange.build_volume:
            disallowed_areas = Arrange.build_volume.getDisallowedAreasNoBrim()
            for area in disallowed_areas:
                points = copy.deepcopy(area._points)
                shape_arr = ShapeArray.fromPolygon(points, scale=scale)
                arranger.place(0, 0, shape_arr, update_empty=False)
        return arranger
예제 #16
0
    def test_singleExtruder(self, build_volume: BuildVolume):
        mocked_global_stack = MagicMock(name = "mocked_global_stack")
        mocked_global_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect)

        mocked_extruder_stack = MagicMock(name = "mocked_extruder_stack")
        mocked_extruder_stack.getId = MagicMock(return_value = "0")
        mocked_extruder_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect)

        build_volume._global_container_stack = mocked_global_stack

        # Create a polygon that should be the result
        resulting_polygon = Polygon.approximatedCircle(PRIME_CLEARANCE)
        # Since we want a blob of size 12;
        resulting_polygon = resulting_polygon.getMinkowskiHull(Polygon.approximatedCircle(12))
        # In the The translation result is 25, -50 (due to the settings used)
        resulting_polygon = resulting_polygon.translate(25, -50)
        assert build_volume._computeDisallowedAreasPrimeBlob(12, [mocked_extruder_stack]) == {"0": [resulting_polygon]}
예제 #17
0
    def fromNode(
        cls,
        node: "SceneNode",
        min_offset: float,
        scale: float = 0.5,
        include_children: bool = False
    ) -> Tuple[Optional["ShapeArray"], Optional["ShapeArray"]]:
        """Instantiate an offset and hull ShapeArray from a scene node.

        :param node: source node where the convex hull must be present
        :param min_offset: offset for the offset ShapeArray
        :param scale: scale the coordinates
        :return: A tuple containing an offset and hull shape array
        """

        transform = node._transformation
        transform_x = transform._data[0][3]
        transform_y = transform._data[2][3]
        hull_verts = node.callDecoration("getConvexHull")
        # If a model is too small then it will not contain any points
        if hull_verts is None or not hull_verts.getPoints().any():
            return None, None
        # For one_at_a_time printing you need the convex hull head.
        hull_head_verts = node.callDecoration(
            "getConvexHullHead") or hull_verts
        if hull_head_verts is None:
            hull_head_verts = Polygon()

        # If the child-nodes are included, adjust convex hulls as well:
        if include_children:
            children = node.getAllChildren()
            if not children is None:
                for child in children:
                    # 'Inefficient' combination of convex hulls through known code rather than mess it up:
                    child_hull = child.callDecoration("getConvexHull")
                    if not child_hull is None:
                        hull_verts = hull_verts.unionConvexHulls(child_hull)
                    child_hull_head = child.callDecoration(
                        "getConvexHullHead") or child_hull
                    if not child_hull_head is None:
                        hull_head_verts = hull_head_verts.unionConvexHulls(
                            child_hull_head)

        offset_verts = hull_head_verts.getMinkowskiHull(
            Polygon.approximatedCircle(min_offset))
        offset_points = copy.deepcopy(offset_verts._points)  # x, y
        offset_points[:, 0] = numpy.add(offset_points[:, 0], -transform_x)
        offset_points[:, 1] = numpy.add(offset_points[:, 1], -transform_y)
        offset_shape_arr = ShapeArray.fromPolygon(offset_points, scale=scale)

        hull_points = copy.deepcopy(hull_verts._points)
        hull_points[:, 0] = numpy.add(hull_points[:, 0], -transform_x)
        hull_points[:, 1] = numpy.add(hull_points[:, 1], -transform_y)
        hull_shape_arr = ShapeArray.fromPolygon(hull_points,
                                                scale=scale)  # x, y

        return offset_shape_arr, hull_shape_arr
예제 #18
0
def test_parts_of_fromNode():
    from UM.Math.Polygon import Polygon
    p = Polygon(numpy.array([[-2, -2], [2, -2], [2, 2], [-2, 2]], dtype=numpy.int32))
    offset = 1
    p_offset = p.getMinkowskiHull(Polygon.approximatedCircle(offset))
    assert len(numpy.where(p_offset._points[:, 0] >= 2.9)) > 0
    assert len(numpy.where(p_offset._points[:, 0] <= -2.9)) > 0
    assert len(numpy.where(p_offset._points[:, 1] >= 2.9)) > 0
    assert len(numpy.where(p_offset._points[:, 1] <= -2.9)) > 0
예제 #19
0
def test_parts_of_fromNode2():
    from UM.Math.Polygon import Polygon
    p = Polygon(numpy.array([[-2, -2], [2, -2], [2, 2], [-2, 2]], dtype=numpy.int32) * 2)  # 4x4
    offset = 13.3
    scale = 0.5
    p_offset = p.getMinkowskiHull(Polygon.approximatedCircle(offset))
    shape_arr1 = ShapeArray.fromPolygon(p._points, scale = scale)
    shape_arr2 = ShapeArray.fromPolygon(p_offset._points, scale = scale)
    assert shape_arr1.arr.shape[0] >= (4 * scale) - 1  # -1 is to account for rounding errors
    assert shape_arr2.arr.shape[0] >= (2 * offset + 4) * scale - 1
예제 #20
0
def test_parts_of_fromNode():
    from UM.Math.Polygon import Polygon
    p = Polygon(
        numpy.array([[-2, -2], [2, -2], [2, 2], [-2, 2]], dtype=numpy.int32))
    offset = 1
    p_offset = p.getMinkowskiHull(Polygon.approximatedCircle(offset))
    assert len(numpy.where(p_offset._points[:, 0] >= 2.9)) > 0
    assert len(numpy.where(p_offset._points[:, 0] <= -2.9)) > 0
    assert len(numpy.where(p_offset._points[:, 1] >= 2.9)) > 0
    assert len(numpy.where(p_offset._points[:, 1] <= -2.9)) > 0
예제 #21
0
def test_parts_of_fromNode():
    """Just adding some stuff to ensure fromNode works as expected. Some parts should actually be in UM"""

    from UM.Math.Polygon import Polygon
    p = Polygon(
        numpy.array([[-2, -2], [2, -2], [2, 2], [-2, 2]], dtype=numpy.int32))
    offset = 1
    p_offset = p.getMinkowskiHull(Polygon.approximatedCircle(offset))
    assert len(numpy.where(p_offset._points[:, 0] >= 2.9)) > 0
    assert len(numpy.where(p_offset._points[:, 0] <= -2.9)) > 0
    assert len(numpy.where(p_offset._points[:, 1] >= 2.9)) > 0
    assert len(numpy.where(p_offset._points[:, 1] <= -2.9)) > 0
예제 #22
0
파일: ShapeArray.py 프로젝트: daid/Cura
    def fromNode(cls, node, min_offset, scale = 0.5):
        transform = node._transformation
        transform_x = transform._data[0][3]
        transform_y = transform._data[2][3]
        hull_verts = node.callDecoration("getConvexHull")
        # For one_at_a_time printing you need the convex hull head.
        hull_head_verts = node.callDecoration("getConvexHullHead") or hull_verts

        offset_verts = hull_head_verts.getMinkowskiHull(Polygon.approximatedCircle(min_offset))
        offset_points = copy.deepcopy(offset_verts._points)  # x, y
        offset_points[:, 0] = numpy.add(offset_points[:, 0], -transform_x)
        offset_points[:, 1] = numpy.add(offset_points[:, 1], -transform_y)
        offset_shape_arr = ShapeArray.fromPolygon(offset_points, scale = scale)

        hull_points = copy.deepcopy(hull_verts._points)
        hull_points[:, 0] = numpy.add(hull_points[:, 0], -transform_x)
        hull_points[:, 1] = numpy.add(hull_points[:, 1], -transform_y)
        hull_shape_arr = ShapeArray.fromPolygon(hull_points, scale = scale)  # x, y

        return offset_shape_arr, hull_shape_arr
예제 #23
0
    def fromNode(cls, node, min_offset, scale = 0.5, include_children = False):
        transform = node._transformation
        transform_x = transform._data[0][3]
        transform_y = transform._data[2][3]
        hull_verts = node.callDecoration("getConvexHull")
        # If a model is too small then it will not contain any points
        if hull_verts is None or not hull_verts.getPoints().any():
            return None, None
        # For one_at_a_time printing you need the convex hull head.
        hull_head_verts = node.callDecoration("getConvexHullHead") or hull_verts
        if hull_head_verts is None:
            hull_head_verts = Polygon()

        # If the child-nodes are included, adjust convex hulls as well:
        if include_children:
            children = node.getAllChildren()
            if not children is None:
                for child in children:
                    # 'Inefficient' combination of convex hulls through known code rather than mess it up:
                    child_hull = child.callDecoration("getConvexHull")
                    if not child_hull is None:
                        hull_verts = hull_verts.unionConvexHulls(child_hull)
                    child_hull_head = child.callDecoration("getConvexHullHead") or child_hull
                    if not child_hull_head is None:
                        hull_head_verts = hull_head_verts.unionConvexHulls(child_hull_head)

        offset_verts = hull_head_verts.getMinkowskiHull(Polygon.approximatedCircle(min_offset))
        offset_points = copy.deepcopy(offset_verts._points)  # x, y
        offset_points[:, 0] = numpy.add(offset_points[:, 0], -transform_x)
        offset_points[:, 1] = numpy.add(offset_points[:, 1], -transform_y)
        offset_shape_arr = ShapeArray.fromPolygon(offset_points, scale = scale)

        hull_points = copy.deepcopy(hull_verts._points)
        hull_points[:, 0] = numpy.add(hull_points[:, 0], -transform_x)
        hull_points[:, 1] = numpy.add(hull_points[:, 1], -transform_y)
        hull_shape_arr = ShapeArray.fromPolygon(hull_points, scale = scale)  # x, y

        return offset_shape_arr, hull_shape_arr
예제 #24
0
 def test_equalityString(self):
     polygon = Polygon.approximatedCircle(42)
     assert polygon != "42"  # Not the answer after all!
예제 #25
0
파일: BuildVolume.py 프로젝트: mifga/Cura
    def _updateDisallowedAreas(self):
        if not self._global_container_stack:
            return

        self._error_areas = []

        extruder_manager = ExtruderManager.getInstance()
        used_extruders = extruder_manager.getUsedExtruderStacks()
        disallowed_border_size = self._getEdgeDisallowedSize()

        if not used_extruders:
            # If no extruder is used, assume that the active extruder is used (else nothing is drawn)
            if extruder_manager.getActiveExtruderStack():
                used_extruders = [extruder_manager.getActiveExtruderStack()]
            else:
                used_extruders = [self._global_container_stack]

        result_areas = self._computeDisallowedAreasStatic(disallowed_border_size, used_extruders) #Normal machine disallowed areas can always be added.
        prime_areas = self._computeDisallowedAreasPrime(disallowed_border_size, used_extruders)
        prime_disallowed_areas = self._computeDisallowedAreasStatic(0, used_extruders) #Where the priming is not allowed to happen. This is not added to the result, just for collision checking.

        #Check if prime positions intersect with disallowed areas.
        for extruder in used_extruders:
            extruder_id = extruder.getId()

            collision = False
            for prime_polygon in prime_areas[extruder_id]:
                for disallowed_polygon in prime_disallowed_areas[extruder_id]:
                    if prime_polygon.intersectsPolygon(disallowed_polygon) is not None:
                        collision = True
                        break
                if collision:
                    break

                #Also check other prime positions (without additional offset).
                for other_extruder_id in prime_areas:
                    if extruder_id == other_extruder_id: #It is allowed to collide with itself.
                        continue
                    for other_prime_polygon in prime_areas[other_extruder_id]:
                        if prime_polygon.intersectsPolygon(other_prime_polygon):
                            collision = True
                            break
                    if collision:
                        break
                if collision:
                    break

            result_areas[extruder_id].extend(prime_areas[extruder_id])

            nozzle_disallowed_areas = extruder.getProperty("nozzle_disallowed_areas", "value")
            for area in nozzle_disallowed_areas:
                polygon = Polygon(numpy.array(area, numpy.float32))
                polygon = polygon.getMinkowskiHull(Polygon.approximatedCircle(disallowed_border_size))
                result_areas[extruder_id].append(polygon) #Don't perform the offset on these.

        # Add prime tower location as disallowed area.
        prime_tower_collision = False
        prime_tower_areas = self._computeDisallowedAreasPrinted(used_extruders)
        for extruder_id in prime_tower_areas:
            for prime_tower_area in prime_tower_areas[extruder_id]:
                for area in result_areas[extruder_id]:
                    if prime_tower_area.intersectsPolygon(area) is not None:
                        prime_tower_collision = True
                        break
                if prime_tower_collision: #Already found a collision.
                    break
            if not prime_tower_collision:
                result_areas[extruder_id].extend(prime_tower_areas[extruder_id])
            else:
                self._error_areas.extend(prime_tower_areas[extruder_id])

        self._has_errors = len(self._error_areas) > 0

        self._disallowed_areas = []
        for extruder_id in result_areas:
            self._disallowed_areas.extend(result_areas[extruder_id])
예제 #26
0
 def test_equality(self):
     polygon_1 = Polygon.approximatedCircle(2)
     polygon_2 = Polygon.approximatedCircle(2)
     assert polygon_1 == polygon_2
예제 #27
0
    def run(self):
        if self._build_plate_number is None:
            self.setResult(StartJobResult.Error)
            return

        stack = Application.getInstance().getGlobalContainerStack()
        if not stack:
            self.setResult(StartJobResult.Error)
            return

        # Don't slice if there is a setting with an error value.
        if Application.getInstance().getMachineManager().stacksHaveErrors:
            self.setResult(StartJobResult.SettingError)
            return

        if Application.getInstance().getBuildVolume().hasErrors():
            self.setResult(StartJobResult.BuildPlateError)
            return

        # Don't slice if the buildplate or the nozzle type is incompatible with the materials
        if not Application.getInstance().getMachineManager().variantBuildplateCompatible and \
                not Application.getInstance().getMachineManager().variantBuildplateUsable:
            self.setResult(StartJobResult.MaterialIncompatible)
            return

        for position, extruder_stack in stack.extruders.items():
            material = extruder_stack.findContainer({"type": "material"})
            if not extruder_stack.isEnabled:
                continue
            if material:
                if material.getMetaDataEntry("compatible") == False:
                    self.setResult(StartJobResult.MaterialIncompatible)
                    return

        # Don't slice if there is a per object setting with an error value.
        for node in DepthFirstIterator(self._scene.getRoot()):
            if not isinstance(node, CuraSceneNode) or not node.isSelectable():
                continue

            if self._checkStackForErrors(node.callDecoration("getStack")):
                self.setResult(StartJobResult.ObjectSettingError)
                return

        with self._scene.getSceneLock():
            # Remove old layer data.
            for node in DepthFirstIterator(self._scene.getRoot()):
                if node.callDecoration("getLayerData") and node.callDecoration(
                        "getBuildPlateNumber") == self._build_plate_number:
                    node.getParent().removeChild(node)
                    break

            # Get the objects in their groups to print.
            object_groups = []
            if stack.getProperty("print_sequence", "value") == "one_at_a_time":
                for node in OneAtATimeIterator(self._scene.getRoot()):
                    temp_list = []

                    # Node can't be printed, so don't bother sending it.
                    if getattr(node, "_outside_buildarea", False):
                        continue

                    # Filter on current build plate
                    build_plate_number = node.callDecoration(
                        "getBuildPlateNumber")
                    if build_plate_number is not None and build_plate_number != self._build_plate_number:
                        continue

                    children = node.getAllChildren()
                    children.append(node)
                    for child_node in children:
                        if child_node.getMeshData() and child_node.getMeshData(
                        ).getVertices() is not None:
                            temp_list.append(child_node)

                    if temp_list:
                        object_groups.append(temp_list)
                    Job.yieldThread()
                if len(object_groups) == 0:
                    Logger.log(
                        "w",
                        "No objects suitable for one at a time found, or no correct order found"
                    )
            else:
                temp_list = []
                has_printing_mesh = False
                # print convex hull nodes as "faux-raft"
                print_convex_hulls = stack.getProperty("blackbelt_raft",
                                                       "value")
                for node in DepthFirstIterator(self._scene.getRoot()):
                    slice_node = (print_convex_hulls
                                  and type(node) is ConvexHullNode
                                  ) or node.callDecoration("isSliceable")
                    if slice_node and node.getMeshData() and node.getMeshData(
                    ).getVertices() is not None:
                        per_object_stack = node.callDecoration("getStack")
                        is_non_printing_mesh = False
                        if per_object_stack:
                            is_non_printing_mesh = any(
                                per_object_stack.getProperty(key, "value")
                                for key in NON_PRINTING_MESH_SETTINGS)

                        # Find a reason not to add the node
                        if node.callDecoration(
                                "getBuildPlateNumber"
                        ) != self._build_plate_number and type(
                                node) is not ConvexHullNode:
                            # NB: ConvexHullNodes get none of the usual decorators, so skip checking for them
                            continue
                        if getattr(node, "_outside_buildarea",
                                   False) and not is_non_printing_mesh:
                            continue

                        temp_list.append(node)
                        if not is_non_printing_mesh:
                            has_printing_mesh = True

                    Job.yieldThread()

                #If the list doesn't have any model with suitable settings then clean the list
                # otherwise CuraEngine will crash
                if not has_printing_mesh:
                    temp_list.clear()

                if temp_list:
                    object_groups.append(temp_list)

            extruders_enabled = {
                position: stack.isEnabled
                for position, stack in Application.getInstance().
                getGlobalContainerStack().extruders.items()
            }
            filtered_object_groups = []
            for group in object_groups:
                stack = Application.getInstance().getGlobalContainerStack()
                skip_group = False
                for node in group:
                    # ConvexHullNodes get none of the usual decorators. If it made it here, it is meant to be printed
                    if type(
                            node
                    ) is not ConvexHullNode and not extruders_enabled[
                            node.callDecoration("getActiveExtruderPosition")]:
                        skip_group = True
                        break
                if not skip_group:
                    filtered_object_groups.append(group)

            # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
            # able to find a possible sequence or because there are no objects on the build plate (or they are outside
            # the build volume)
            if not filtered_object_groups:
                self.setResult(StartJobResult.NothingToSlice)
                return

            container_registry = ContainerRegistry.getInstance()
            stack_id = stack.getId()

            # Adapt layer_height and material_flow for a slanted gantry
            gantry_angle = self._scene.getRoot().callDecoration(
                "getGantryAngle")
            if gantry_angle:  # not 0 or None
                # Act on a copy of the stack, so these changes don't cause a reslice
                _stack = CuraContainerStack(stack_id + "_temp")
                index = 0
                for container in stack.getContainers():
                    if container_registry.isReadOnly(container.getId()):
                        _stack.replaceContainer(index, container)
                    else:
                        _stack.replaceContainer(index,
                                                copy.deepcopy(container))
                    index = index + 1
                stack = _stack

                # Make sure CuraEngine does not create any supports
                # support_enable is set in the frontend so support options are settable,
                # but CuraEngine support structures don't work for slanted gantry
                stack.setProperty("support_enable", "value", False)
                # Make sure CuraEngine does not create a raft (we create one manually)
                # Adhsion type is used in the frontend to show the raft in the viewport
                stack.setProperty("adhesion_type", "value", "none")

                for key in ["layer_height", "layer_height_0"]:
                    current_value = stack.getProperty(key, "value")
                    stack.setProperty(key, "value",
                                      current_value / math.sin(gantry_angle))

            self._buildGlobalSettingsMessage(stack)
            self._buildGlobalInheritsStackMessage(stack)

            # Build messages for extruder stacks
            for position, extruder_stack in Application.getInstance(
            ).getGlobalContainerStack().extruders.items():
                if gantry_angle:  # not 0 or None
                    # Act on a copy of the stack, so these changes don't cause a reslice
                    _extruder_stack = CuraContainerStack(
                        extruder_stack.getId() + "_temp")
                    index = 0
                    for container in extruder_stack.getContainers():
                        if container_registry.isReadOnly(container.getId()):
                            _extruder_stack.replaceContainer(index, container)
                        else:
                            _extruder_stack.replaceContainer(
                                index, copy.deepcopy(container))
                        index = index + 1
                    extruder_stack = _extruder_stack
                    extruder_stack.setNextStack(stack)
                    for key in [
                            "material_flow", "prime_tower_flow",
                            "spaghetti_flow"
                    ]:
                        if extruder_stack.hasProperty(key, "value"):
                            current_value = extruder_stack.getProperty(
                                key, "value")
                            extruder_stack.setProperty(
                                key, "value",
                                current_value * math.sin(gantry_angle))
                self._buildExtruderMessage(extruder_stack)

            bottom_cutting_meshes = []
            raft_meshes = []
            if gantry_angle:  # not 0 or None
                # Add a modifier mesh to all printable meshes touching the belt
                for group in filtered_object_groups:
                    added_meshes = []
                    for object in group:

                        is_non_printing_mesh = False
                        per_object_stack = object.callDecoration("getStack")
                        if per_object_stack:
                            is_non_printing_mesh = any(
                                per_object_stack.getProperty(key, "value")
                                for key in NON_PRINTING_MESH_SETTINGS)

                        # ConvexHullNodes get none of the usual decorators. If it made it here, it is meant to be printed
                        if type(object) is ConvexHullNode:
                            raft_thickness = stack.getProperty(
                                "blackbelt_raft_thickness", "value")
                            raft_margin = stack.getProperty(
                                "blackbelt_raft_margin", "value")

                            mb = MeshBuilder()
                            hull_polygon = object.getHull()
                            if raft_margin > 0:
                                hull_polygon = hull_polygon.getMinkowskiHull(
                                    Polygon.approximatedCircle(raft_margin))
                            mb.addConvexPolygonExtrusion(
                                hull_polygon.getPoints()[::-1], 0,
                                raft_thickness)

                            new_node = self._addMesh(mb, "raftMesh")
                            added_meshes.append(new_node)
                            raft_meshes.append(new_node.getName())

                        elif not is_non_printing_mesh:
                            extruder_stack_index = object.callDecoration(
                                "getActiveExtruderPosition")
                            if not extruder_stack_index:
                                extruder_stack_index = 0
                            extruder_stack = ExtruderManager.getInstance(
                            ).getMachineExtruders(Application.getInstance(
                            ).getGlobalContainerStack().getId())[int(
                                extruder_stack_index)]

                            aabb = object.getBoundingBox()

                            if aabb.bottom < 0:
                                # mesh extends below the belt; add a cutting mesh to cut off the part below the bottom
                                height = -aabb.bottom
                                center = Vector(aabb.center.x, -height / 2,
                                                aabb.center.z)

                                mb = MeshBuilder()
                                mb.addCube(width=aabb.width,
                                           height=height,
                                           depth=aabb.depth,
                                           center=center)

                                new_node = self._addMesh(
                                    mb, "bottomCuttingMesh")
                                added_meshes.append(new_node)
                                bottom_cutting_meshes.append(
                                    new_node.getName())

                    if added_meshes:
                        group += added_meshes

            transform_matrix = self._scene.getRoot().callDecoration(
                "getTransformMatrix")
            front_offset = None

            raft_offset = 0
            raft_speed = None
            raft_flow = 1.0

            if stack.getProperty("blackbelt_raft", "value"):
                raft_offset = stack.getProperty(
                    "blackbelt_raft_thickness", "value") + stack.getProperty(
                        "blackbelt_raft_gap", "value")
                raft_speed = stack.getProperty("blackbelt_raft_speed", "value")
                raft_flow = stack.getProperty("blackbelt_raft_flow",
                                              "value") * math.sin(gantry_angle)

            for group in filtered_object_groups:
                group_message = self._slice_message.addRepeatedMessage(
                    "object_lists")
                if group[0].getParent() is not None and group[0].getParent(
                ).callDecoration("isGroup"):
                    self._handlePerObjectSettings(group[0].getParent(),
                                                  group_message)
                for object in group:
                    if type(object) is ConvexHullNode:
                        continue

                    mesh_data = object.getMeshData()
                    rot_scale = object.getWorldTransformation().getTransposed(
                    ).getData()[0:3, 0:3]
                    translate = object.getWorldTransformation().getData()[:3,
                                                                          3]
                    # offset all non-raft objects if rafts are enabled
                    # air gap is applied here to vertically offset objects from the raft
                    if object.getName() not in raft_meshes:
                        translate[1] = translate[1] + raft_offset

                    # This effectively performs a limited form of MeshData.getTransformed that ignores normals.
                    verts = mesh_data.getVertices()
                    verts = verts.dot(rot_scale)
                    verts += translate

                    if transform_matrix:
                        verts = transformVertices(verts, transform_matrix)

                        is_non_printing_mesh = object.getName(
                        ) in bottom_cutting_meshes or object.getName(
                        ) in raft_meshes
                        if not is_non_printing_mesh:
                            per_object_stack = object.callDecoration(
                                "getStack")
                            if per_object_stack:
                                is_non_printing_mesh = any(
                                    per_object_stack.getProperty(key, "value")
                                    for key in NON_PRINTING_MESH_SETTINGS)

                        if not is_non_printing_mesh:
                            _front_offset = verts[:, 1].min()
                            if front_offset is None or _front_offset < front_offset:
                                front_offset = _front_offset

                    # Convert from Y up axes to Z up axes. Equals a 90 degree rotation.
                    verts[:, [1, 2]] = verts[:, [2, 1]]
                    verts[:, 1] *= -1

                    obj = group_message.addRepeatedMessage("objects")
                    obj.id = id(object)

                    indices = mesh_data.getIndices()
                    if indices is not None:
                        flat_verts = numpy.take(verts,
                                                indices.flatten(),
                                                axis=0)
                    else:
                        flat_verts = numpy.array(verts)

                    obj.vertices = flat_verts

                    if object.getName() in raft_meshes:
                        self._addSettingsMessage(
                            obj, {
                                "wall_line_count": 99999999,
                                "speed_wall_0": raft_speed,
                                "speed_wall_x": raft_speed,
                                "material_flow": raft_flow
                            })

                    elif object.getName() in bottom_cutting_meshes:
                        self._addSettingsMessage(
                            obj, {
                                "cutting_mesh": True,
                                "wall_line_count": 0,
                                "top_layers": 0,
                                "bottom_layers": 0,
                                "infill_line_distance": 0
                            })

                    else:
                        self._handlePerObjectSettings(object, obj)
                        Job.yieldThread()

                # Store the front-most coordinate of the scene so the scene can be moved back into place post slicing
                # TODO: this should be handled per mesh-group instead of per scene
                # One-at-a-time printing should be disabled for slanted gantry printers for now
                self._scene.getRoot().callDecoration("setSceneFrontOffset",
                                                     front_offset)

        self.setResult(StartJobResult.Finished)
예제 #28
0
    def _updateDisallowedAreas(self):
        if not self._global_container_stack:
            return

        self._error_areas = []

        extruder_manager = ExtruderManager.getInstance()
        used_extruders = extruder_manager.getUsedExtruderStacks()
        disallowed_border_size = self._getEdgeDisallowedSize()

        result_areas = self._computeDisallowedAreasStatic(disallowed_border_size, used_extruders) #Normal machine disallowed areas can always be added.
        prime_areas = self._computeDisallowedAreasPrime(disallowed_border_size, used_extruders)
        prime_disallowed_areas = self._computeDisallowedAreasStatic(0, used_extruders) #Where the priming is not allowed to happen. This is not added to the result, just for collision checking.

        #Check if prime positions intersect with disallowed areas.
        for extruder in used_extruders:
            extruder_id = extruder.getId()

            collision = False
            for prime_polygon in prime_areas[extruder_id]:
                for disallowed_polygon in prime_disallowed_areas[extruder_id]:
                    if prime_polygon.intersectsPolygon(disallowed_polygon) is not None:
                        collision = True
                        break
                if collision:
                    break

                #Also check other prime positions (without additional offset).
                for other_extruder_id in prime_areas:
                    if extruder_id == other_extruder_id: #It is allowed to collide with itself.
                        continue
                    for other_prime_polygon in prime_areas[other_extruder_id]:
                        if prime_polygon.intersectsPolygon(other_prime_polygon):
                            collision = True
                            break
                    if collision:
                        break
                if collision:
                    break

            result_areas[extruder_id].extend(prime_areas[extruder_id])

            nozzle_disallowed_areas = extruder.getProperty("nozzle_disallowed_areas", "value")
            for area in nozzle_disallowed_areas:
                polygon = Polygon(numpy.array(area, numpy.float32))
                polygon = polygon.getMinkowskiHull(Polygon.approximatedCircle(disallowed_border_size))
                result_areas[extruder_id].append(polygon) #Don't perform the offset on these.

        # Add prime tower location as disallowed area.
        prime_tower_collision = False
        prime_tower_areas = self._computeDisallowedAreasPrinted(used_extruders)
        for extruder_id in prime_tower_areas:
            for prime_tower_area in prime_tower_areas[extruder_id]:
                for area in result_areas[extruder_id]:
                    if prime_tower_area.intersectsPolygon(area) is not None:
                        prime_tower_collision = True
                        break
                if prime_tower_collision: #Already found a collision.
                    break
            if not prime_tower_collision:
                result_areas[extruder_id].extend(prime_tower_areas[extruder_id])
            else:
                self._error_areas.extend(prime_tower_areas[extruder_id])

        self._has_errors = len(self._error_areas) > 0

        self._disallowed_areas = []
        for extruder_id in result_areas:
            self._disallowed_areas.extend(result_areas[extruder_id])
예제 #29
0
파일: BuildVolume.py 프로젝트: cederom/Cura
    def _computeDisallowedAreasStatic(self, border_size, used_extruders):
        #Convert disallowed areas to polygons and dilate them.
        machine_disallowed_polygons = []
        for area in self._global_container_stack.getProperty("machine_disallowed_areas", "value"):
            polygon = Polygon(numpy.array(area, numpy.float32))
            polygon = polygon.getMinkowskiHull(Polygon.approximatedCircle(border_size))
            machine_disallowed_polygons.append(polygon)

        result = {}
        for extruder in used_extruders:
            extruder_id = extruder.getId()
            offset_x = extruder.getProperty("machine_nozzle_offset_x", "value")
            if offset_x is None:
                offset_x = 0
            offset_y = extruder.getProperty("machine_nozzle_offset_y", "value")
            if offset_y is None:
                offset_y = 0
            result[extruder_id] = []

            for polygon in machine_disallowed_polygons:
                result[extruder_id].append(polygon.translate(offset_x, offset_y)) #Compensate for the nozzle offset of this extruder.

            #Add the border around the edge of the build volume.
            left_unreachable_border = 0
            right_unreachable_border = 0
            top_unreachable_border = 0
            bottom_unreachable_border = 0
            #The build volume is defined as the union of the area that all extruders can reach, so we need to know the relative offset to all extruders.
            for other_extruder in ExtruderManager.getInstance().getActiveExtruderStacks():
                other_offset_x = other_extruder.getProperty("machine_nozzle_offset_x", "value")
                other_offset_y = other_extruder.getProperty("machine_nozzle_offset_y", "value")
                left_unreachable_border = min(left_unreachable_border, other_offset_x - offset_x)
                right_unreachable_border = max(right_unreachable_border, other_offset_x - offset_x)
                top_unreachable_border = min(top_unreachable_border, other_offset_y - offset_y)
                bottom_unreachable_border = max(bottom_unreachable_border, other_offset_y - offset_y)
            half_machine_width = self._global_container_stack.getProperty("machine_width", "value") / 2
            half_machine_depth = self._global_container_stack.getProperty("machine_depth", "value") / 2
            if border_size - left_unreachable_border > 0:
                result[extruder_id].append(Polygon(numpy.array([
                    [-half_machine_width, -half_machine_depth],
                    [-half_machine_width, half_machine_depth],
                    [-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border],
                    [-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border]
                ], numpy.float32)))
            if border_size + right_unreachable_border > 0:
                result[extruder_id].append(Polygon(numpy.array([
                    [half_machine_width, half_machine_depth],
                    [half_machine_width, -half_machine_depth],
                    [half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border],
                    [half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border]
                ], numpy.float32)))
            if border_size + bottom_unreachable_border > 0:
                result[extruder_id].append(Polygon(numpy.array([
                    [-half_machine_width, half_machine_depth],
                    [half_machine_width, half_machine_depth],
                    [half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border],
                    [-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border]
                ], numpy.float32)))
            if border_size - top_unreachable_border > 0:
                result[extruder_id].append(Polygon(numpy.array([
                    [half_machine_width, -half_machine_depth],
                    [-half_machine_width, -half_machine_depth],
                    [-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border],
                    [half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border]
                ], numpy.float32)))

        return result
예제 #30
0
파일: BuildVolume.py 프로젝트: iagane/Cura
    def _updateDisallowedAreas(self):
        if not self._global_container_stack:
            return

        self._has_errors = False  # Reset.
        self._error_areas = []
        disallowed_areas = copy.deepcopy(
            self._global_container_stack.getProperty(
                "machine_disallowed_areas", "value"))
        areas = []

        machine_width = self._global_container_stack.getProperty(
            "machine_width", "value")
        machine_depth = self._global_container_stack.getProperty(
            "machine_depth", "value")
        prime_tower_area = None

        # Add prime tower location as disallowed area.
        if ExtruderManager.getInstance().getResolveOrValue(
                "prime_tower_enable") == True:
            prime_tower_size = self._global_container_stack.getProperty(
                "prime_tower_size", "value")
            prime_tower_x = self._global_container_stack.getProperty(
                "prime_tower_position_x", "value") - machine_width / 2
            prime_tower_y = -self._global_container_stack.getProperty(
                "prime_tower_position_y", "value") + machine_depth / 2

            prime_tower_area = Polygon([
                [
                    prime_tower_x - prime_tower_size,
                    prime_tower_y - prime_tower_size
                ],
                [prime_tower_x, prime_tower_y - prime_tower_size],
                [prime_tower_x, prime_tower_y],
                [prime_tower_x - prime_tower_size, prime_tower_y],
            ])
        disallowed_polygons = []

        # Check if prime positions intersect with disallowed areas
        prime_collision = False
        if disallowed_areas:
            for area in disallowed_areas:
                poly = Polygon(numpy.array(area, numpy.float32))

                # Minkowski with zero, to ensure that the polygon is correct & watertight.
                poly = poly.getMinkowskiHull(Polygon.approximatedCircle(0))
                disallowed_polygons.append(poly)

            extruder_manager = ExtruderManager.getInstance()
            extruders = extruder_manager.getMachineExtruders(
                self._global_container_stack.getId())
            prime_polygons = []
            # Each extruder has it's own prime location
            for extruder in extruders:
                prime_x = extruder.getProperty("extruder_prime_pos_x",
                                               "value") - machine_width / 2
                prime_y = machine_depth / 2 - extruder.getProperty(
                    "extruder_prime_pos_y", "value")

                prime_polygon = Polygon([
                    [prime_x - PRIME_CLEARANCE, prime_y - PRIME_CLEARANCE],
                    [prime_x + PRIME_CLEARANCE, prime_y - PRIME_CLEARANCE],
                    [prime_x + PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE],
                    [prime_x - PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE],
                ])
                prime_polygon = prime_polygon.getMinkowskiHull(
                    Polygon.approximatedCircle(0))
                collision = False

                # Check if prime polygon is intersecting with any of the other disallowed areas.
                # Note that we check the prime area without bed adhesion.
                for poly in disallowed_polygons:
                    if prime_polygon.intersectsPolygon(poly) is not None:
                        collision = True
                        break

                # Also collide with other prime positions
                for poly in prime_polygons:
                    if prime_polygon.intersectsPolygon(poly) is not None:
                        collision = True
                        break

                if not collision:
                    # Prime area is valid. Add as normal.
                    # Once it's added like this, it will recieve a bed adhesion offset, just like the others.
                    prime_polygons.append(prime_polygon)
                else:
                    self._error_areas.append(prime_polygon)
                    prime_collision = collision or prime_collision

            disallowed_polygons.extend(prime_polygons)

        disallowed_border_size = self._getEdgeDisallowedSize()

        # Extend every area already in the disallowed_areas with the skirt size.
        if disallowed_areas:
            for poly in disallowed_polygons:
                poly = poly.getMinkowskiHull(
                    Polygon.approximatedCircle(disallowed_border_size))
                areas.append(poly)

        # Add the skirt areas around the borders of the build plate.
        if disallowed_border_size > 0:
            half_machine_width = self._global_container_stack.getProperty(
                "machine_width", "value") / 2
            half_machine_depth = self._global_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 + disallowed_border_size,
                             half_machine_depth - disallowed_border_size
                         ],
                         [
                             -half_machine_width + disallowed_border_size,
                             -half_machine_depth + disallowed_border_size
                         ]], numpy.float32)))

            areas.append(
                Polygon(
                    numpy.array(
                        [[half_machine_width, half_machine_depth],
                         [half_machine_width, -half_machine_depth],
                         [
                             half_machine_width - disallowed_border_size,
                             -half_machine_depth + disallowed_border_size
                         ],
                         [
                             half_machine_width - disallowed_border_size,
                             half_machine_depth - disallowed_border_size
                         ]], numpy.float32)))

            areas.append(
                Polygon(
                    numpy.array(
                        [[-half_machine_width, half_machine_depth],
                         [half_machine_width, half_machine_depth],
                         [
                             half_machine_width - disallowed_border_size,
                             half_machine_depth - disallowed_border_size
                         ],
                         [
                             -half_machine_width + disallowed_border_size,
                             half_machine_depth - disallowed_border_size
                         ]], numpy.float32)))

            areas.append(
                Polygon(
                    numpy.array(
                        [[half_machine_width, -half_machine_depth],
                         [-half_machine_width, -half_machine_depth],
                         [
                             -half_machine_width + disallowed_border_size,
                             -half_machine_depth + disallowed_border_size
                         ],
                         [
                             half_machine_width - disallowed_border_size,
                             -half_machine_depth + disallowed_border_size
                         ]], numpy.float32)))

        # Check if the prime tower area intersects with any of the other areas.
        # If this is the case, add it to the error area's so it can be drawn in red.
        # If not, add it back to disallowed area's, so it's rendered as normal.
        prime_tower_collision = False
        if prime_tower_area:
            # Using Minkowski of 0 fixes the prime tower area so it's rendered correctly
            prime_tower_area = prime_tower_area.getMinkowskiHull(
                Polygon.approximatedCircle(0))
            for area in areas:
                if prime_tower_area.intersectsPolygon(area) is not None:
                    prime_tower_collision = True
                    break

            if not prime_tower_collision:
                areas.append(prime_tower_area)
            else:
                self._error_areas.append(prime_tower_area)
        # The buildplate has errors if either prime tower or prime has a colission.
        self._has_errors = prime_tower_collision or prime_collision
        self._disallowed_areas = areas
예제 #31
0
파일: BuildVolume.py 프로젝트: mifga/Cura
    def _computeDisallowedAreasStatic(self, border_size, used_extruders):
        #Convert disallowed areas to polygons and dilate them.
        machine_disallowed_polygons = []
        for area in self._global_container_stack.getProperty("machine_disallowed_areas", "value"):
            polygon = Polygon(numpy.array(area, numpy.float32))
            polygon = polygon.getMinkowskiHull(Polygon.approximatedCircle(border_size))
            machine_disallowed_polygons.append(polygon)

        result = {}
        for extruder in used_extruders:
            extruder_id = extruder.getId()
            offset_x = extruder.getProperty("machine_nozzle_offset_x", "value")
            if offset_x is None:
                offset_x = 0
            offset_y = extruder.getProperty("machine_nozzle_offset_y", "value")
            if offset_y is None:
                offset_y = 0
            result[extruder_id] = []

            for polygon in machine_disallowed_polygons:
                result[extruder_id].append(polygon.translate(offset_x, offset_y)) #Compensate for the nozzle offset of this extruder.

            #Add the border around the edge of the build volume.
            left_unreachable_border = 0
            right_unreachable_border = 0
            top_unreachable_border = 0
            bottom_unreachable_border = 0
            #The build volume is defined as the union of the area that all extruders can reach, so we need to know the relative offset to all extruders.
            for other_extruder in ExtruderManager.getInstance().getActiveExtruderStacks():
                other_offset_x = other_extruder.getProperty("machine_nozzle_offset_x", "value")
                other_offset_y = other_extruder.getProperty("machine_nozzle_offset_y", "value")
                left_unreachable_border = min(left_unreachable_border, other_offset_x - offset_x)
                right_unreachable_border = max(right_unreachable_border, other_offset_x - offset_x)
                top_unreachable_border = min(top_unreachable_border, other_offset_y - offset_y)
                bottom_unreachable_border = max(bottom_unreachable_border, other_offset_y - offset_y)
            half_machine_width = self._global_container_stack.getProperty("machine_width", "value") / 2
            half_machine_depth = self._global_container_stack.getProperty("machine_depth", "value") / 2

            if self._shape != "elliptic":
                if border_size - left_unreachable_border > 0:
                    result[extruder_id].append(Polygon(numpy.array([
                        [-half_machine_width, -half_machine_depth],
                        [-half_machine_width, half_machine_depth],
                        [-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border],
                        [-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border]
                    ], numpy.float32)))
                if border_size + right_unreachable_border > 0:
                    result[extruder_id].append(Polygon(numpy.array([
                        [half_machine_width, half_machine_depth],
                        [half_machine_width, -half_machine_depth],
                        [half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border],
                        [half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border]
                    ], numpy.float32)))
                if border_size + bottom_unreachable_border > 0:
                    result[extruder_id].append(Polygon(numpy.array([
                        [-half_machine_width, half_machine_depth],
                        [half_machine_width, half_machine_depth],
                        [half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border],
                        [-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border]
                    ], numpy.float32)))
                if border_size - top_unreachable_border > 0:
                    result[extruder_id].append(Polygon(numpy.array([
                        [half_machine_width, -half_machine_depth],
                        [-half_machine_width, -half_machine_depth],
                        [-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border],
                        [half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border]
                    ], numpy.float32)))
            else:
                sections = 32
                arc_vertex = [0, half_machine_depth - border_size]
                for i in range(0, sections):
                    quadrant = math.floor(4 * i / sections)
                    vertices = []
                    if quadrant == 0:
                        vertices.append([-half_machine_width, half_machine_depth])
                    elif quadrant == 1:
                        vertices.append([-half_machine_width, -half_machine_depth])
                    elif quadrant == 2:
                        vertices.append([half_machine_width, -half_machine_depth])
                    elif quadrant == 3:
                        vertices.append([half_machine_width, half_machine_depth])
                    vertices.append(arc_vertex)

                    angle = 2 * math.pi * (i + 1) / sections
                    arc_vertex = [-(half_machine_width - border_size) * math.sin(angle), (half_machine_depth - border_size) * math.cos(angle)]
                    vertices.append(arc_vertex)

                    result[extruder_id].append(Polygon(numpy.array(vertices, numpy.float32)))

                if border_size > 0:
                    result[extruder_id].append(Polygon(numpy.array([
                        [-half_machine_width, -half_machine_depth],
                        [-half_machine_width, half_machine_depth],
                        [-half_machine_width + border_size, 0]
                    ], numpy.float32)))
                    result[extruder_id].append(Polygon(numpy.array([
                        [-half_machine_width, half_machine_depth],
                        [ half_machine_width, half_machine_depth],
                        [ 0, half_machine_depth - border_size]
                    ], numpy.float32)))
                    result[extruder_id].append(Polygon(numpy.array([
                        [ half_machine_width, half_machine_depth],
                        [ half_machine_width, -half_machine_depth],
                        [ half_machine_width - border_size, 0]
                    ], numpy.float32)))
                    result[extruder_id].append(Polygon(numpy.array([
                        [ half_machine_width,-half_machine_depth],
                        [-half_machine_width,-half_machine_depth],
                        [ 0, -half_machine_depth + border_size]
                    ], numpy.float32)))

        return result
예제 #32
0
 def test_equalitySamePolygon(self):
     polygon = Polygon.approximatedCircle(2)
     assert polygon == polygon
예제 #33
0
    def test_isInside(self):
        polygon = Polygon.approximatedCircle(5)
        assert polygon.isInside((0, 0))

        assert not polygon.isInside((9001, 9001))
예제 #34
0
 def test_inequality(self):
     # This case should short cirquit because the length of points are not the same
     polygon_1 = Polygon.approximatedCircle(2)
     polygon_2 = Polygon()
     assert polygon_1 != polygon_2
예제 #35
0
    def _computeDisallowedAreasStatic(self, border_size, used_extruders):
        #Convert disallowed areas to polygons and dilate them.
        machine_disallowed_polygons = []
        for area in self._global_container_stack.getProperty(
                "machine_disallowed_areas", "value"):
            polygon = Polygon(numpy.array(area, numpy.float32))
            polygon = polygon.getMinkowskiHull(
                Polygon.approximatedCircle(border_size))
            machine_disallowed_polygons.append(polygon)

        result = {}
        for extruder in used_extruders:
            extruder_id = extruder.getId()
            offset_x = extruder.getProperty("machine_nozzle_offset_x", "value")
            if not offset_x:
                offset_x = 0
            offset_y = extruder.getProperty("machine_nozzle_offset_y", "value")
            if not offset_y:
                offset_y = 0
            result[extruder_id] = []

            for polygon in machine_disallowed_polygons:
                result[extruder_id].append(
                    polygon.translate(offset_x, offset_y)
                )  #Compensate for the nozzle offset of this extruder.

            #Add the border around the edge of the build volume.
            left_unreachable_border = 0
            right_unreachable_border = 0
            top_unreachable_border = 0
            bottom_unreachable_border = 0
            #The build volume is defined as the union of the area that all extruders can reach, so we need to know the relative offset to all extruders.
            for other_extruder in ExtruderManager.getInstance(
            ).getActiveExtruderStacks():
                other_offset_x = other_extruder.getProperty(
                    "machine_nozzle_offset_x", "value")
                other_offset_y = other_extruder.getProperty(
                    "machine_nozzle_offset_y", "value")
                left_unreachable_border = min(left_unreachable_border,
                                              other_offset_x - offset_x)
                right_unreachable_border = max(right_unreachable_border,
                                               other_offset_x - offset_x)
                top_unreachable_border = min(top_unreachable_border,
                                             other_offset_y - offset_y)
                bottom_unreachable_border = max(bottom_unreachable_border,
                                                other_offset_y - offset_y)
            half_machine_width = self._global_container_stack.getProperty(
                "machine_width", "value") / 2
            half_machine_depth = self._global_container_stack.getProperty(
                "machine_depth", "value") / 2
            if border_size - left_unreachable_border > 0:
                result[extruder_id].append(
                    Polygon(
                        numpy.array(
                            [[-half_machine_width, -half_machine_depth],
                             [-half_machine_width, half_machine_depth],
                             [
                                 -half_machine_width + border_size -
                                 left_unreachable_border, half_machine_depth -
                                 border_size - bottom_unreachable_border
                             ],
                             [
                                 -half_machine_width + border_size -
                                 left_unreachable_border, -half_machine_depth +
                                 border_size - top_unreachable_border
                             ]], numpy.float32)))
            if border_size + right_unreachable_border > 0:
                result[extruder_id].append(
                    Polygon(
                        numpy.array(
                            [[half_machine_width, half_machine_depth],
                             [half_machine_width, -half_machine_depth],
                             [
                                 half_machine_width - border_size -
                                 right_unreachable_border,
                                 -half_machine_depth + border_size -
                                 top_unreachable_border
                             ],
                             [
                                 half_machine_width - border_size -
                                 right_unreachable_border, half_machine_depth -
                                 border_size - bottom_unreachable_border
                             ]], numpy.float32)))
            if border_size + bottom_unreachable_border > 0:
                result[extruder_id].append(
                    Polygon(
                        numpy.array(
                            [[-half_machine_width, half_machine_depth],
                             [half_machine_width, half_machine_depth],
                             [
                                 half_machine_width - border_size -
                                 right_unreachable_border, half_machine_depth -
                                 border_size - bottom_unreachable_border
                             ],
                             [
                                 -half_machine_width + border_size -
                                 left_unreachable_border, half_machine_depth -
                                 border_size - bottom_unreachable_border
                             ]], numpy.float32)))
            if border_size - top_unreachable_border > 0:
                result[extruder_id].append(
                    Polygon(
                        numpy.array(
                            [[half_machine_width, -half_machine_depth],
                             [-half_machine_width, -half_machine_depth],
                             [
                                 -half_machine_width + border_size -
                                 left_unreachable_border, -half_machine_depth +
                                 border_size - top_unreachable_border
                             ],
                             [
                                 half_machine_width - border_size -
                                 right_unreachable_border,
                                 -half_machine_depth + border_size -
                                 top_unreachable_border
                             ]], numpy.float32)))

        return result
예제 #36
0
    def run(self) -> None:
        if self._build_plate_number is None:
            self.setResult(StartJobResult.Error)
            return

        stack = CuraApplication.getInstance().getGlobalContainerStack()
        if not stack:
            self.setResult(StartJobResult.Error)
            return

        # Don't slice if there is a setting with an error value.
        if CuraApplication.getInstance().getMachineManager().stacksHaveErrors:
            self.setResult(StartJobResult.SettingError)
            return

        if CuraApplication.getInstance().getBuildVolume().hasErrors():
            self.setResult(StartJobResult.BuildPlateError)
            return

        # Don't slice if the buildplate or the nozzle type is incompatible with the materials
        if not CuraApplication.getInstance().getMachineManager().variantBuildplateCompatible and \
                not CuraApplication.getInstance().getMachineManager().variantBuildplateUsable:
            self.setResult(StartJobResult.MaterialIncompatible)
            return

        for position, extruder_stack in stack.extruders.items():
            material = extruder_stack.findContainer({"type": "material"})
            if not extruder_stack.isEnabled:
                continue
            if material:
                if material.getMetaDataEntry("compatible") == False:
                    self.setResult(StartJobResult.MaterialIncompatible)
                    return

        # Don't slice if there is a per object setting with an error value.
        for node in DepthFirstIterator(
                self._scene.getRoot()
        ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
            if not isinstance(node, CuraSceneNode) or not node.isSelectable():
                continue

            if self._checkStackForErrors(node.callDecoration("getStack")):
                self.setResult(StartJobResult.ObjectSettingError)
                return

        with self._scene.getSceneLock():
            # Remove old layer data.
            for node in DepthFirstIterator(
                    self._scene.getRoot()
            ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
                if node.callDecoration("getLayerData") and node.callDecoration(
                        "getBuildPlateNumber") == self._build_plate_number:
                    node.getParent().removeChild(node)
                    break

            global_enable_support = stack.getProperty("support_enable",
                                                      "value")

            # Get the objects in their groups to print.
            object_groups = []
            if stack.getProperty("print_sequence", "value") == "one_at_a_time":
                # note that one_at_a_time printing is disabled on belt printers due to collission risk
                for node in OneAtATimeIterator(
                        self._scene.getRoot()
                ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
                    temp_list = []

                    # Node can't be printed, so don't bother sending it.
                    if getattr(node, "_outside_buildarea", False):
                        continue

                    # Filter on current build plate
                    build_plate_number = node.callDecoration(
                        "getBuildPlateNumber")
                    if build_plate_number is not None and build_plate_number != self._build_plate_number:
                        continue

                    children = node.getAllChildren()
                    children.append(node)
                    for child_node in children:
                        if child_node.getMeshData() and child_node.getMeshData(
                        ).getVertices() is not None:
                            temp_list.append(child_node)

                    if temp_list:
                        object_groups.append(temp_list)
                    Job.yieldThread()
                if len(object_groups) == 0:
                    Logger.log(
                        "w",
                        "No objects suitable for one at a time found, or no correct order found"
                    )
            else:
                temp_list = []
                has_printing_mesh = False
                # print convex hull nodes as "faux-raft"
                print_convex_hulls = self._preferences.getValue(
                    "BeltPlugin/raft")
                for node in DepthFirstIterator(
                        self._scene.getRoot()
                ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
                    slice_node = (print_convex_hulls
                                  and type(node) is ConvexHullNode
                                  ) or node.callDecoration("isSliceable")
                    if slice_node and node.getMeshData() and node.getMeshData(
                    ).getVertices() is not None:
                        per_object_stack = node.callDecoration("getStack")
                        is_non_printing_mesh = False
                        if per_object_stack:
                            is_non_printing_mesh = any(
                                per_object_stack.getProperty(key, "value")
                                for key in NON_PRINTING_MESH_SETTINGS)

                        # Find a reason not to add the node
                        if node.callDecoration(
                                "getBuildPlateNumber"
                        ) != self._build_plate_number and type(
                                node) is not ConvexHullNode:
                            # NB: ConvexHullNodes get none of the usual decorators, so skip checking for them
                            continue
                        if getattr(node, "_outside_buildarea",
                                   False) and not is_non_printing_mesh:
                            continue

                        temp_list.append(node)
                        if not is_non_printing_mesh:
                            has_printing_mesh = True

                    Job.yieldThread()

                #If the list doesn't have any model with suitable settings then clean the list
                # otherwise CuraEngine will crash
                if not has_printing_mesh:
                    temp_list.clear()

                if temp_list:
                    object_groups.append(temp_list)

            global_stack = CuraApplication.getInstance(
            ).getGlobalContainerStack()
            if not global_stack:
                return
            extruders_enabled = {
                position: stack.isEnabled
                for position, stack in global_stack.extruders.items()
            }
            filtered_object_groups = []
            has_model_with_disabled_extruders = False
            associated_disabled_extruders = set()
            for group in object_groups:
                stack = global_stack
                skip_group = False
                for node in group:
                    # Only check if the printing extruder is enabled for printing meshes
                    is_non_printing_mesh = node.callDecoration(
                        "evaluateIsNonPrintingMesh")
                    extruder_position = node.callDecoration(
                        "getActiveExtruderPosition")
                    if extruder_position is None:  # raft meshes may not have an extruder position (yet)
                        extruder_position = "0"
                    if not is_non_printing_mesh and not extruders_enabled[
                            extruder_position]:
                        skip_group = True
                        has_model_with_disabled_extruders = True
                        associated_disabled_extruders.add(extruder_position)
                if not skip_group:
                    filtered_object_groups.append(group)

            if has_model_with_disabled_extruders:
                self.setResult(StartJobResult.ObjectsWithDisabledExtruder)
                associated_disabled_extruders = {
                    str(c)
                    for c in sorted(
                        [int(p) + 1 for p in associated_disabled_extruders])
                }
                self.setMessage(", ".join(associated_disabled_extruders))
                return

            # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
            # able to find a possible sequence or because there are no objects on the build plate (or they are outside
            # the build volume)
            if not filtered_object_groups:
                self.setResult(StartJobResult.NothingToSlice)
                return

            container_registry = ContainerRegistry.getInstance()
            stack_id = stack.getId()

            # Adapt layer_height and material_flow for a slanted gantry
            gantry_angle = self._scene.getRoot().callDecoration(
                "getGantryAngle")
            #gantry_angle = float(self._preferences.getValue("BeltPlugin/gantry_angle"))
            if gantry_angle:  # not 0 or None
                # Act on a copy of the stack, so these changes don't cause a reslice
                _stack = CuraContainerStack(stack_id + "_temp")
                for index, container in enumerate(stack.getContainers()):
                    if container_registry.isReadOnly(container.getId()):
                        _stack.replaceContainer(index, container)
                    else:
                        _stack.replaceContainer(index,
                                                copy.deepcopy(container))
                stack = _stack

                # Make sure CuraEngine does not create any supports
                # support_enable is set in the frontend so support options are settable,
                # but CuraEngine support structures don't work for slanted gantry
                stack.setProperty("support_enable", "value", False)
                # Make sure CuraEngine does not create a raft (we create one manually)
                # Adhesion type is used in the frontend to show the raft in the viewport
                stack.setProperty("adhesion_type", "value", "none")

                for key in ["layer_height", "layer_height_0"]:
                    current_value = stack.getProperty(key, "value")
                    stack.setProperty(key, "value",
                                      current_value / math.sin(gantry_angle))

            self._buildGlobalSettingsMessage(stack)
            self._buildGlobalInheritsStackMessage(stack)

            # Build messages for extruder stacks
            # Send the extruder settings in the order of extruder positions. Somehow, if you send e.g. extruder 3 first,
            # then CuraEngine can slice with the wrong settings. This I think should be fixed in CuraEngine as well.
            extruder_stack_list = sorted(list(global_stack.extruders.items()),
                                         key=lambda item: int(item[0]))
            for _, extruder_stack in extruder_stack_list:
                if gantry_angle:  # not 0 or None
                    # Act on a copy of the stack, so these changes don't cause a reslice
                    _extruder_stack = CuraContainerStack(
                        extruder_stack.getId() + "_temp")
                    for index, container in enumerate(
                            extruder_stack.getContainers()):
                        if container_registry.isReadOnly(container.getId()):
                            _extruder_stack.replaceContainer(index, container)
                        else:
                            _extruder_stack.replaceContainer(
                                index, copy.deepcopy(container))
                    extruder_stack = _extruder_stack
                    extruder_stack.setNextStack(stack)
                    for key in [
                            "material_flow", "prime_tower_flow",
                            "spaghetti_flow"
                    ]:
                        if extruder_stack.hasProperty(key, "value"):
                            current_value = extruder_stack.getProperty(
                                key, "value")
                            extruder_stack.setProperty(
                                key, "value",
                                current_value * math.sin(gantry_angle))
                self._buildExtruderMessage(extruder_stack)

            bottom_cutting_meshes = []
            raft_meshes = []
            support_meshes = []
            if gantry_angle:  # not 0 or None
                for group in filtered_object_groups:
                    added_meshes = []
                    for object in group:

                        is_non_printing_mesh = False
                        per_object_stack = object.callDecoration("getStack")

                        # ConvexHullNodes get none of the usual decorators. If it made it here, it is meant to be printed
                        if type(object) is ConvexHullNode:
                            raft_thickness = self._preferences.getValue(
                                "BeltPlugin/raft_thickness")
                            raft_margin = self._preferences.getValue(
                                "BeltPlugin/raft_margin")

                            mb = MeshBuilder()
                            hull_polygon = object.getHull()
                            if raft_margin > 0:
                                hull_polygon = hull_polygon.getMinkowskiHull(
                                    Polygon.approximatedCircle(raft_margin))
                            mb.addConvexPolygonExtrusion(
                                hull_polygon.getPoints()[::-1], 0,
                                raft_thickness)

                            new_node = self._addMeshFromBuilder(mb, "raftMesh")
                            added_meshes.append(new_node)
                            raft_meshes.append(new_node.getName())

                        elif not is_non_printing_mesh:
                            # add support mesh if needed
                            belt_support_gantry_angle_bias = None
                            belt_support_minimum_island_area = None
                            if per_object_stack:
                                is_non_printing_mesh = any(
                                    per_object_stack.getProperty(key, "value")
                                    for key in NON_PRINTING_MESH_SETTINGS)

                                node_enable_support = per_object_stack.getProperty(
                                    "support_enable", "value")
                                if per_object_stack.getProperty(
                                        "support_mesh", "value"):
                                    node_enable_support = node_enable_support or per_object_stack.getProperty(
                                        "support_mesh_drop_down", "value")
                                add_support_mesh = node_enable_support if node_enable_support is not None else global_enable_support

                                belt_support_gantry_angle_bias = self._preferences.getValue(
                                    "BeltPlugin/support_gantry_angle_bias")
                                belt_support_minimum_island_area = self._preferences.getValue(
                                    "BeltPlugin/support_minimum_island_area")
                            else:
                                add_support_mesh = global_enable_support

                            if add_support_mesh:
                                #preferences = Application.getInstance().getPreferences()
                                if belt_support_gantry_angle_bias is None:
                                    belt_support_gantry_angle_bias = self._preferences.getValue(
                                        "BeltPlugin/support_gantry_angle_bias")
                                biased_down_angle = math.radians(
                                    belt_support_gantry_angle_bias)
                                if belt_support_minimum_island_area is None:
                                    belt_support_minimum_island_area = self._preferences.getValue(
                                        "BeltPlugin/support_minimum_island_area"
                                    )
                                support_mesh_data = SupportMeshCreator(
                                    down_vector=numpy.array([
                                        0, -math.cos(
                                            math.radians(biased_down_angle)),
                                        -math.sin(biased_down_angle)
                                    ]),
                                    bottom_cut_off=stack.getProperty(
                                        "wall_line_width_0", "value") / 2,
                                    minimum_island_area=
                                    belt_support_minimum_island_area
                                ).createSupportMeshForNode(object)
                                if support_mesh_data:
                                    new_node = self._addMeshFromData(
                                        support_mesh_data,
                                        "generatedSupportMesh")
                                    added_meshes.append(new_node)
                                    support_meshes.append(new_node.getName())

                            # check if the bottom needs to be cut off
                            aabb = object.getBoundingBox()

                            if aabb.bottom < 0:
                                # mesh extends below the belt; add a cutting mesh to cut off the part below the bottom
                                height = -aabb.bottom
                                center = Vector(aabb.center.x, -height / 2,
                                                aabb.center.z)

                                mb = MeshBuilder()
                                mb.addCube(width=aabb.width,
                                           height=height,
                                           depth=aabb.depth,
                                           center=center)

                                new_node = self._addMeshFromBuilder(
                                    mb, "bottomCuttingMesh")
                                added_meshes.append(new_node)
                                bottom_cutting_meshes.append(
                                    new_node.getName())

                    if added_meshes:
                        group += added_meshes

            transform_matrix = self._scene.getRoot().callDecoration(
                "getTransformMatrix")
            front_offset = None

            raft_offset = 0
            raft_speed = None
            raft_flow = 1.0

            if self._preferences.getValue("BeltPlugin/raft"):
                raft_offset = self._preferences.getValue(
                    "BeltPlugin/raft_thickness")
                raft_speed = self._preferences.getValue(
                    "BeltPlugin/raft_speed")
                raft_flow = self._preferences.getValue(
                    "BeltPlugin/raft_flow") * math.sin(gantry_angle)

            adhesion_extruder_nr = stack.getProperty("adhesion_extruder_nr",
                                                     "value")
            support_extruder_nr = stack.getProperty("support_extruder_nr",
                                                    "value")

            for group in filtered_object_groups:
                group_message = self._slice_message.addRepeatedMessage(
                    "object_lists")
                if group[0].getParent() is not None and group[0].getParent(
                ).callDecoration("isGroup"):
                    self._handlePerObjectSettings(group[0].getParent(),
                                                  group_message)

                if transform_matrix:
                    scene_front = None
                    for object in group:
                        if type(object) is ConvexHullNode:
                            continue

                        is_non_printing_mesh = object.getName(
                        ) in bottom_cutting_meshes or object.getName(
                        ) in raft_meshes
                        if not is_non_printing_mesh:
                            per_object_stack = object.callDecoration(
                                "getStack")
                            if per_object_stack:
                                is_non_printing_mesh = any(
                                    per_object_stack.getProperty(key, "value")
                                    for key in NON_PRINTING_MESH_SETTINGS)

                        if not is_non_printing_mesh:
                            _front = object.getBoundingBox().back
                            if scene_front is None or _front < scene_front:
                                scene_front = _front

                    if scene_front is not None:
                        front_offset = transformVertices(
                            numpy.array([[0, 0, scene_front]]),
                            transform_matrix)[0][1]

                for object in group:
                    if type(object) is ConvexHullNode:
                        continue

                    mesh_data = object.getMeshData()
                    rot_scale = object.getWorldTransformation().getTransposed(
                    ).getData()[0:3, 0:3]
                    translate = object.getWorldTransformation().getData()[:3,
                                                                          3]
                    # offset all non-raft objects if rafts are enabled
                    # air gap is applied here to vertically offset objects from the raft
                    if object.getName() not in raft_meshes:
                        translate[1] += raft_offset
                    if front_offset:
                        translate[2] -= front_offset

                    # This effectively performs a limited form of MeshData.getTransformed that ignores normals.
                    verts = mesh_data.getVertices()
                    verts = verts.dot(rot_scale)
                    verts += translate

                    if transform_matrix:
                        verts = transformVertices(verts, transform_matrix)

                    # Convert from Y up axes to Z up axes. Equals a 90 degree rotation.
                    verts[:, [1, 2]] = verts[:, [2, 1]]
                    verts[:, 1] *= -1

                    obj = group_message.addRepeatedMessage("objects")
                    obj.id = id(object)

                    obj.name = object.getName()
                    indices = mesh_data.getIndices()
                    if indices is not None:
                        flat_verts = numpy.take(verts,
                                                indices.flatten(),
                                                axis=0)
                    else:
                        flat_verts = numpy.array(verts)

                    obj.vertices = flat_verts

                    if object.getName() in raft_meshes:
                        self._addSettingsMessage(
                            obj, {
                                "wall_line_count": 99999999,
                                "speed_wall_0": raft_speed,
                                "speed_wall_x": raft_speed,
                                "material_flow": raft_flow,
                                "extruder_nr": adhesion_extruder_nr
                            })

                    elif object.getName() in support_meshes:
                        self._addSettingsMessage(
                            obj, {
                                "support_mesh": "True",
                                "support_mesh_drop_down": "False",
                                "extruder_nr": support_extruder_nr
                            })

                    elif object.getName() in bottom_cutting_meshes:
                        self._addSettingsMessage(
                            obj, {
                                "cutting_mesh": True,
                                "wall_line_count": 0,
                                "top_layers": 0,
                                "bottom_layers": 0,
                                "infill_line_distance": 0,
                                "extruder_nr": 0
                            })

                    else:
                        self._handlePerObjectSettings(object, obj)
                    Job.yieldThread()

                # Store the front-most coordinate of the scene so the scene can be moved back into place post slicing
                # TODO: this should be handled per mesh-group instead of per scene
                # One-at-a-time printing should be disabled for slanted gantry printers for now

                self._scene.getRoot().callDecoration("setSceneFrontOffset",
                                                     front_offset)

        self.setResult(StartJobResult.Finished)
예제 #37
0
    def _computeDisallowedAreasStatic(self, border_size, used_extruders):
        #Convert disallowed areas to polygons and dilate them.
        machine_disallowed_polygons = []
        for area in self._global_container_stack.getProperty("machine_disallowed_areas", "value"):
            polygon = Polygon(numpy.array(area, numpy.float32))
            polygon = polygon.getMinkowskiHull(Polygon.approximatedCircle(border_size))
            machine_disallowed_polygons.append(polygon)

        result = {}
        for extruder in used_extruders:
            extruder_id = extruder.getId()
            offset_x = extruder.getProperty("machine_nozzle_offset_x", "value")
            if offset_x is None:
                offset_x = 0
            offset_y = extruder.getProperty("machine_nozzle_offset_y", "value")
            if offset_y is None:
                offset_y = 0
            result[extruder_id] = []

            for polygon in machine_disallowed_polygons:
                result[extruder_id].append(polygon.translate(offset_x, offset_y)) #Compensate for the nozzle offset of this extruder.

            #Add the border around the edge of the build volume.
            left_unreachable_border = 0
            right_unreachable_border = 0
            top_unreachable_border = 0
            bottom_unreachable_border = 0
            #The build volume is defined as the union of the area that all extruders can reach, so we need to know the relative offset to all extruders.
            for other_extruder in ExtruderManager.getInstance().getActiveExtruderStacks():
                other_offset_x = other_extruder.getProperty("machine_nozzle_offset_x", "value")
                other_offset_y = other_extruder.getProperty("machine_nozzle_offset_y", "value")
                left_unreachable_border = min(left_unreachable_border, other_offset_x - offset_x)
                right_unreachable_border = max(right_unreachable_border, other_offset_x - offset_x)
                top_unreachable_border = min(top_unreachable_border, other_offset_y - offset_y)
                bottom_unreachable_border = max(bottom_unreachable_border, other_offset_y - offset_y)
            half_machine_width = self._global_container_stack.getProperty("machine_width", "value") / 2
            half_machine_depth = self._global_container_stack.getProperty("machine_depth", "value") / 2

            if self._shape != "elliptic":
                if border_size - left_unreachable_border > 0:
                    result[extruder_id].append(Polygon(numpy.array([
                        [-half_machine_width, -half_machine_depth],
                        [-half_machine_width, half_machine_depth],
                        [-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border],
                        [-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border]
                    ], numpy.float32)))
                if border_size + right_unreachable_border > 0:
                    result[extruder_id].append(Polygon(numpy.array([
                        [half_machine_width, half_machine_depth],
                        [half_machine_width, -half_machine_depth],
                        [half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border],
                        [half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border]
                    ], numpy.float32)))
                if border_size + bottom_unreachable_border > 0:
                    result[extruder_id].append(Polygon(numpy.array([
                        [-half_machine_width, half_machine_depth],
                        [half_machine_width, half_machine_depth],
                        [half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border],
                        [-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border]
                    ], numpy.float32)))
                if border_size - top_unreachable_border > 0:
                    result[extruder_id].append(Polygon(numpy.array([
                        [half_machine_width, -half_machine_depth],
                        [-half_machine_width, -half_machine_depth],
                        [-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border],
                        [half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border]
                    ], numpy.float32)))
            else:
                sections = 32
                arc_vertex = [0, half_machine_depth - border_size]
                for i in range(0, sections):
                    quadrant = math.floor(4 * i / sections)
                    vertices = []
                    if quadrant == 0:
                        vertices.append([-half_machine_width, half_machine_depth])
                    elif quadrant == 1:
                        vertices.append([-half_machine_width, -half_machine_depth])
                    elif quadrant == 2:
                        vertices.append([half_machine_width, -half_machine_depth])
                    elif quadrant == 3:
                        vertices.append([half_machine_width, half_machine_depth])
                    vertices.append(arc_vertex)

                    angle = 2 * math.pi * (i + 1) / sections
                    arc_vertex = [-(half_machine_width - border_size) * math.sin(angle), (half_machine_depth - border_size) * math.cos(angle)]
                    vertices.append(arc_vertex)

                    result[extruder_id].append(Polygon(numpy.array(vertices, numpy.float32)))

                if border_size > 0:
                    result[extruder_id].append(Polygon(numpy.array([
                        [-half_machine_width, -half_machine_depth],
                        [-half_machine_width, half_machine_depth],
                        [-half_machine_width + border_size, 0]
                    ], numpy.float32)))
                    result[extruder_id].append(Polygon(numpy.array([
                        [-half_machine_width, half_machine_depth],
                        [ half_machine_width, half_machine_depth],
                        [ 0, half_machine_depth - border_size]
                    ], numpy.float32)))
                    result[extruder_id].append(Polygon(numpy.array([
                        [ half_machine_width, half_machine_depth],
                        [ half_machine_width, -half_machine_depth],
                        [ half_machine_width - border_size, 0]
                    ], numpy.float32)))
                    result[extruder_id].append(Polygon(numpy.array([
                        [ half_machine_width,-half_machine_depth],
                        [-half_machine_width,-half_machine_depth],
                        [ 0, -half_machine_depth + border_size]
                    ], numpy.float32)))

        return result