예제 #1
0
    def run(self):
        if not self._node:
            return
        ## If the scene node is a group, use the hull of the children to calculate its hull.
        if self._node.callDecoration("isGroup"):
            hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32))
            for child in self._node.getChildren():
                child_hull = child.callDecoration("getConvexHull") 
                if child_hull:
                    hull.setPoints(numpy.append(hull.getPoints(), child_hull.getPoints(), axis = 0))

                if hull.getPoints().size < 3:
                    self._node.callDecoration("setConvexHull", None)
                    self._node.callDecoration("setConvexHullJob", None)
                    return

                Job.yieldThread()

        else: 
            if not self._node.getMeshData():
                return
            mesh = self._node.getMeshData()
            vertex_data = mesh.getTransformed(self._node.getWorldTransformation()).getVertices()
            # Don't use data below 0. TODO; We need a better check for this as this gives poor results for meshes with long edges.
            vertex_data = vertex_data[vertex_data[:,1]>0]
            hull = Polygon(numpy.rint(vertex_data[:, [0, 2]]).astype(int))

        # First, calculate the normal convex hull around the points
        hull = hull.getConvexHull()

        # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
        # This is done because of rounding errors.
        hull = hull.getMinkowskiHull(Polygon(numpy.array([[-1, -1], [-1, 1], [1, 1], [1, -1]], numpy.float32)))

        profile = Application.getInstance().getMachineManager().getActiveProfile()
        if profile:
            if profile.getSettingValue("print_sequence") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
                # Printing one at a time and it's not an object in a group
                self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull))
                head_hull = hull.getMinkowskiHull(Polygon(numpy.array(profile.getSettingValue("machine_head_with_fans_polygon"),numpy.float32)))
                self._node.callDecoration("setConvexHullHead", head_hull)
                hull = hull.getMinkowskiHull(Polygon(numpy.array(profile.getSettingValue("machine_head_polygon"),numpy.float32)))
            else:
                self._node.callDecoration("setConvexHullHead", None)
        hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot())
        self._node.callDecoration("setConvexHullNode", hull_node)
        self._node.callDecoration("setConvexHull", hull)
        self._node.callDecoration("setConvexHullJob", None)

        if self._node.getParent().callDecoration("isGroup"):
            job = self._node.getParent().callDecoration("getConvexHullJob")
            if job:
                job.cancel()
            self._node.getParent().callDecoration("setConvexHull", None)
            hull_node = self._node.getParent().callDecoration("getConvexHullNode")
            if hull_node:
                hull_node.setParent(None)
예제 #2
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
예제 #3
0
    def _updateDisallowedAreas(self):
        if not self._active_container_stack:
            return

        disallowed_areas = self._active_container_stack.getProperty("machine_disallowed_areas", "value")
        areas = []

        skirt_size = self._getSkirtSize(self._active_container_stack)

        if disallowed_areas:
            # Extend every area already in the disallowed_areas with the skirt size.
            for area in disallowed_areas:
                poly = Polygon(numpy.array(area, numpy.float32))
                poly = poly.getMinkowskiHull(Polygon(numpy.array([
                    [-skirt_size, 0],
                    [-skirt_size * 0.707, skirt_size * 0.707],
                    [0, skirt_size],
                    [skirt_size * 0.707, skirt_size * 0.707],
                    [skirt_size, 0],
                    [skirt_size * 0.707, -skirt_size * 0.707],
                    [0, -skirt_size],
                    [-skirt_size * 0.707, -skirt_size * 0.707]
                ], numpy.float32)))

                areas.append(poly)

        # Add the skirt areas around the borders of the build plate.
        if skirt_size > 0:
            half_machine_width = self._active_container_stack.getProperty("machine_width", "value") / 2
            half_machine_depth = self._active_container_stack.getProperty("machine_depth", "value") / 2

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

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

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

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

        self._disallowed_areas = areas
예제 #4
0
    def _offsetHull(self, convex_hull: Polygon) -> Polygon:
        """Offset the convex hull with settings that influence the collision area.

        :param convex_hull: Polygon of the original convex hull.
        :return: New Polygon instance that is offset with everything that
        influences the collision area.
        """

        horizontal_expansion = max(
            self._getSettingProperty("xy_offset", "value"),
            self._getSettingProperty("xy_offset_layer_0", "value"))

        mold_width = 0
        if self._getSettingProperty("mold_enabled", "value"):
            mold_width = self._getSettingProperty("mold_width", "value")
        hull_offset = horizontal_expansion + mold_width
        if hull_offset > 0:  #TODO: Implement Minkowski subtraction for if the offset < 0.
            expansion_polygon = Polygon(
                numpy.array(
                    [[-hull_offset, -hull_offset], [-hull_offset, hull_offset],
                     [hull_offset, hull_offset], [hull_offset, -hull_offset]],
                    numpy.float32))
            return convex_hull.getMinkowskiHull(expansion_polygon)
        else:
            return convex_hull
예제 #5
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
예제 #6
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
예제 #7
0
    def _updateDisallowedAreas(self):
        if not self._active_instance or not self._active_profile:
            return

        disallowed_areas = self._active_instance.getMachineSettingValue("machine_disallowed_areas")
        areas = []

        skirt_size = 0.0
        if self._active_profile:
            skirt_size = self._getSkirtSize(self._active_profile)

        if disallowed_areas:
            for area in disallowed_areas:
                poly = Polygon(numpy.array(area, numpy.float32))
                poly = poly.getMinkowskiHull(Polygon(numpy.array([
                    [-skirt_size, 0],
                    [-skirt_size * 0.707, skirt_size * 0.707],
                    [0, skirt_size],
                    [skirt_size * 0.707, skirt_size * 0.707],
                    [skirt_size, 0],
                    [skirt_size * 0.707, -skirt_size * 0.707],
                    [0, -skirt_size],
                    [-skirt_size * 0.707, -skirt_size * 0.707]
                ], numpy.float32)))

                areas.append(poly)

        if skirt_size > 0:
            half_machine_width = self._active_instance.getMachineSettingValue("machine_width") / 2
            half_machine_depth = self._active_instance.getMachineSettingValue("machine_depth") / 2

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

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

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

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

        self._disallowed_areas = areas
예제 #8
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
예제 #9
0
    def _updateDisallowedAreas(self):
        if not self._active_container_stack:
            return

        disallowed_areas = self._active_container_stack.getProperty("machine_disallowed_areas", "value")
        areas = []

        skirt_size = self._getSkirtSize(self._active_container_stack)

        if disallowed_areas:
            # Extend every area already in the disallowed_areas with the skirt size.
            for area in disallowed_areas:
                poly = Polygon(numpy.array(area, numpy.float32))
                poly = poly.getMinkowskiHull(Polygon(numpy.array([
                    [-skirt_size, 0],
                    [-skirt_size * 0.707, skirt_size * 0.707],
                    [0, skirt_size],
                    [skirt_size * 0.707, skirt_size * 0.707],
                    [skirt_size, 0],
                    [skirt_size * 0.707, -skirt_size * 0.707],
                    [0, -skirt_size],
                    [-skirt_size * 0.707, -skirt_size * 0.707]
                ], numpy.float32)))

                areas.append(poly)

        # Add the skirt areas around the borders of the build plate.
        if skirt_size > 0:
            half_machine_width = self._active_container_stack.getProperty("machine_width", "value") / 2
            half_machine_depth = self._active_container_stack.getProperty("machine_depth", "value") / 2

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

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

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

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

        self._disallowed_areas = areas
예제 #10
0
    def _updateDisallowedAreas(self):
        if not self._active_instance or not self._active_profile:
            return

        disallowed_areas = self._active_instance.getMachineSettingValue("machine_disallowed_areas")
        areas = []

        skirt_size = 0.0
        if self._active_profile:
            skirt_size = self._getSkirtSize(self._active_profile)

        if disallowed_areas:
            for area in disallowed_areas:
                poly = Polygon(numpy.array(area, numpy.float32))
                poly = poly.getMinkowskiHull(Polygon(numpy.array([
                    [-skirt_size, 0],
                    [-skirt_size * 0.707, skirt_size * 0.707],
                    [0, skirt_size],
                    [skirt_size * 0.707, skirt_size * 0.707],
                    [skirt_size, 0],
                    [skirt_size * 0.707, -skirt_size * 0.707],
                    [0, -skirt_size],
                    [-skirt_size * 0.707, -skirt_size * 0.707]
                ], numpy.float32)))

                areas.append(poly)

        if skirt_size > 0:
            half_machine_width = self._active_instance.getMachineSettingValue("machine_width") / 2
            half_machine_depth = self._active_instance.getMachineSettingValue("machine_depth") / 2

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

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

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

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

        self._disallowed_areas = areas
예제 #11
0
    def run(self):
        if not self._node:
            return
        ## If the scene node is a group, use the hull of the children to calculate its hull.
        if self._node.callDecoration("isGroup"):
            hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32))
            for child in self._node.getChildren():
                child_hull = child.callDecoration("getConvexHull") 
                if child_hull:
                    hull.setPoints(numpy.append(hull.getPoints(), child_hull.getPoints(), axis = 0))
                    
                if hull.getPoints().size < 3:
                    self._node.callDecoration("setConvexHull", None)
                    self._node.callDecoration("setConvexHullJob", None)
                    return
                    
        else: 
            if not self._node.getMeshData():
                return
            mesh = self._node.getMeshData()
            vertex_data = mesh.getTransformed(self._node.getWorldTransformation()).getVertices()

            hull = Polygon(numpy.rint(vertex_data[:, [0, 2]]).astype(int))
        
        # First, calculate the normal convex hull around the points
        hull = hull.getConvexHull()
        #print("hull: " , self._node.callDecoration("isGroup"), " " , hull.getPoints())
        # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
        hull = hull.getMinkowskiHull(Polygon(numpy.array([[-1, -1], [-1, 1], [1, 1], [1, -1]], numpy.float32)))
       
        hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot())
        self._node.callDecoration("setConvexHullNode", hull_node)
        self._node.callDecoration("setConvexHull", hull)
        self._node.callDecoration("setConvexHullJob", None)
예제 #12
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
예제 #13
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
예제 #14
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
예제 #15
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
예제 #16
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
예제 #17
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
예제 #18
0
    def run(self):
        if not self._node or not self._node.getMeshData():
            return

        mesh = self._node.getMeshData()
        vertexData = mesh.getTransformed(self._node.getWorldTransformation()).getVertices()

        hull = Polygon(numpy.rint(vertexData[:, [0, 2]]).astype(int))

        # First, calculate the normal convex hull around the points
        hull = hull.getConvexHull()
        # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
        hull = hull.getMinkowskiHull(Polygon(numpy.array([[-1, -1], [-1, 1], [1, 1], [1, -1]], numpy.float32)))

        hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot())

        self._node._convex_hull = hull
        delattr(self._node, "_convex_hull_job")
예제 #19
0
    def _offsetHull(self, convex_hull: Polygon) -> Polygon:
        horizontal_expansion = max(
            self._getSettingProperty("xy_offset", "value"),
            self._getSettingProperty("xy_offset_layer_0", "value"))

        mold_width = 0
        if self._getSettingProperty("mold_enabled", "value"):
            mold_width = self._getSettingProperty("mold_width", "value")
        hull_offset = horizontal_expansion + mold_width
        if hull_offset > 0:  #TODO: Implement Minkowski subtraction for if the offset < 0.
            expansion_polygon = Polygon(
                numpy.array(
                    [[-hull_offset, -hull_offset], [-hull_offset, hull_offset],
                     [hull_offset, hull_offset], [hull_offset, -hull_offset]],
                    numpy.float32))
            return convex_hull.getMinkowskiHull(expansion_polygon)
        else:
            return convex_hull
예제 #20
0
    def _offsetHull(self, convex_hull: Polygon) -> Polygon:
        horizontal_expansion = max(
            self._getSettingProperty("xy_offset", "value"),
            self._getSettingProperty("xy_offset_layer_0", "value")
        )

        mold_width = 0
        if self._getSettingProperty("mold_enabled", "value"):
            mold_width = self._getSettingProperty("mold_width", "value")
        hull_offset = horizontal_expansion + mold_width
        if hull_offset > 0: #TODO: Implement Minkowski subtraction for if the offset < 0.
            expansion_polygon = Polygon(numpy.array([
                [-hull_offset, -hull_offset],
                [-hull_offset, hull_offset],
                [hull_offset, hull_offset],
                [hull_offset, -hull_offset]
            ], numpy.float32))
            return convex_hull.getMinkowskiHull(expansion_polygon)
        else:
            return convex_hull
예제 #21
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
예제 #22
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
예제 #23
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])
예제 #24
0
    def run(self):
        if not self._node:
            return
        ## If the scene node is a group, use the hull of the children to calculate its hull.
        if self._node.callDecoration("isGroup"):
            hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32))
            for child in self._node.getChildren():
                child_hull = child.callDecoration("getConvexHull")
                if child_hull:
                    hull.setPoints(
                        numpy.append(hull.getPoints(),
                                     child_hull.getPoints(),
                                     axis=0))

                if hull.getPoints().size < 3:
                    self._node.callDecoration("setConvexHull", None)
                    self._node.callDecoration("setConvexHullJob", None)
                    return

                Job.yieldThread()

        else:
            if not self._node.getMeshData():
                return
            mesh = self._node.getMeshData()
            vertex_data = mesh.getTransformed(
                self._node.getWorldTransformation()).getVertices()
            # Don't use data below 0.
            # TODO; We need a better check for this as this gives poor results for meshes with long edges.
            vertex_data = vertex_data[vertex_data[:, 1] >= 0]

            # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices
            # This is done to greatly speed up further convex hull calculations as the convex hull
            # becomes much less complex when dealing with highly detailed models.
            vertex_data = numpy.round(vertex_data, 1)

            vertex_data = vertex_data[:, [
                0, 2
            ]]  # Drop the Y components to project to 2D.

            # Grab the set of unique points.
            #
            # This basically finds the unique rows in the array by treating them as opaque groups of bytes
            # which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch.
            # See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array
            vertex_byte_view = numpy.ascontiguousarray(vertex_data).view(
                numpy.dtype(
                    (numpy.void,
                     vertex_data.dtype.itemsize * vertex_data.shape[1])))
            _, idx = numpy.unique(vertex_byte_view, return_index=True)
            vertex_data = vertex_data[idx]  # Select the unique rows by index.

            hull = Polygon(vertex_data)

        # First, calculate the normal convex hull around the points
        hull = hull.getConvexHull()

        # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
        # This is done because of rounding errors.
        hull = hull.getMinkowskiHull(
            Polygon(
                numpy.array(
                    [[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]],
                    numpy.float32)))

        global_stack = Application.getInstance().getGlobalContainerStack()
        if global_stack:
            if global_stack.getProperty(
                    "print_sequence",
                    "value") == "one_at_a_time" and not self._node.getParent(
                    ).callDecoration("isGroup"):
                # Printing one at a time and it's not an object in a group
                self._node.callDecoration("setConvexHullBoundary",
                                          copy.deepcopy(hull))
                head_and_fans = Polygon(
                    numpy.array(
                        global_stack.getProperty(
                            "machine_head_with_fans_polygon", "value"),
                        numpy.float32))

                # Full head hull is used to actually check the order.
                full_head_hull = hull.getMinkowskiHull(head_and_fans)
                self._node.callDecoration("setConvexHullHeadFull",
                                          full_head_hull)
                mirrored = copy.deepcopy(head_and_fans)
                mirrored.mirror([0, 0], [0, 1])  #Mirror horizontally.
                mirrored.mirror([0, 0], [1, 0])  #Mirror vertically.
                head_and_fans = head_and_fans.intersectionConvexHulls(mirrored)

                # Min head hull is used for the push free
                min_head_hull = hull.getMinkowskiHull(head_and_fans)
                self._node.callDecoration("setConvexHullHead", min_head_hull)
                hull = hull.getMinkowskiHull(
                    Polygon(
                        numpy.array(
                            global_stack.getProperty("machine_head_polygon",
                                                     "value"), numpy.float32)))
            else:
                self._node.callDecoration("setConvexHullHead", None)
        if self._node.getParent(
        ) is None:  # Node was already deleted before job is done.
            self._node.callDecoration("setConvexHullNode", None)
            self._node.callDecoration("setConvexHull", None)
            self._node.callDecoration("setConvexHullJob", None)
            return

        hull_node = ConvexHullNode.ConvexHullNode(
            self._node, hull,
            Application.getInstance().getController().getScene().getRoot())
        self._node.callDecoration("setConvexHullNode", hull_node)
        self._node.callDecoration("setConvexHull", hull)
        self._node.callDecoration("setConvexHullJob", None)

        if self._node.getParent() and self._node.getParent().callDecoration(
                "isGroup"):
            job = self._node.getParent().callDecoration("getConvexHullJob")
            if job:
                job.cancel()
            self._node.getParent().callDecoration("setConvexHull", None)
            hull_node = self._node.getParent().callDecoration(
                "getConvexHullNode")
            if hull_node:
                hull_node.setParent(None)
예제 #25
0
class BuildVolume(SceneNode):
    VolumeOutlineColor = Color(12, 169, 227, 255)

    raftThicknessChanged = Signal()

    def __init__(self, parent = None):
        super().__init__(parent)

        self._width = 0
        self._height = 0
        self._depth = 0

        self._shader = None

        self._grid_mesh = None
        self._grid_shader = None

        self._disallowed_areas = []
        self._disallowed_area_mesh = None

        self._prime_tower_area = None
        self._prime_tower_area_mesh = None

        self.setCalculateBoundingBox(False)
        self._volume_aabb = None

        self._raft_thickness = 0.0
        self._adhesion_type = None
        self._platform = Platform(self)

        self._global_container_stack = None
        Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
        self._onGlobalContainerStackChanged()

        self._active_extruder_stack = None
        ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
        self._onActiveExtruderStackChanged()

        self._has_errors = False

    def setWidth(self, width):
        if width: self._width = width

    def setHeight(self, height):
        if height: self._height = height

    def setDepth(self, depth):
        if depth: self._depth = depth

    def getDisallowedAreas(self):
        return self._disallowed_areas

    def setDisallowedAreas(self, areas):
        self._disallowed_areas = areas

    def render(self, renderer):
        if not self.getMeshData():
            return True

        if not self._shader:
            self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader"))
            self._grid_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "grid.shader"))

        renderer.queueNode(self, mode = RenderBatch.RenderMode.Lines)
        renderer.queueNode(self, mesh = self._grid_mesh, shader = self._grid_shader, backface_cull = True)
        if self._disallowed_area_mesh:
            renderer.queueNode(self, mesh = self._disallowed_area_mesh, shader = self._shader, transparent = True, backface_cull = True, sort = -9)

        if self._prime_tower_area_mesh:
            renderer.queueNode(self, mesh = self._prime_tower_area_mesh, shader = self._shader, transparent=True,
                               backface_cull=True, sort=-8)

        return True

    ##  Recalculates the build volume & disallowed areas.
    def rebuild(self):
        if not self._width or not self._height or not self._depth:
            return

        min_w = -self._width / 2
        max_w = self._width / 2
        min_h = 0.0
        max_h = self._height
        min_d = -self._depth / 2
        max_d = self._depth / 2

        mb = MeshBuilder()

        # Outline 'cube' of the build volume
        mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor)

        mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)

        mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)

        self.setMeshData(mb.build())

        mb = MeshBuilder()
        mb.addQuad(
            Vector(min_w, min_h - 0.2, min_d),
            Vector(max_w, min_h - 0.2, min_d),
            Vector(max_w, min_h - 0.2, max_d),
            Vector(min_w, min_h - 0.2, max_d)
        )

        for n in range(0, 6):
            v = mb.getVertex(n)
            mb.setVertexUVCoordinates(n, v[0], v[2])
        self._grid_mesh = mb.build()

        disallowed_area_height = 0.1
        disallowed_area_size = 0
        if self._disallowed_areas:
            mb = MeshBuilder()
            color = Color(0.0, 0.0, 0.0, 0.15)
            for polygon in self._disallowed_areas:
                points = polygon.getPoints()
                first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d))
                previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d))
                for point in points:
                    new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height, self._clamp(point[1], min_d, max_d))
                    mb.addFace(first, previous_point, new_point, color = color)
                    previous_point = new_point

                # Find the largest disallowed area to exclude it from the maximum scale bounds.
                # This is a very nasty hack. This pretty much only works for UM machines.
                # This disallowed area_size needs a -lot- of rework at some point in the future: TODO
                if numpy.min(points[:, 1]) >= 0: # This filters out all areas that have points to the left of the centre. This is done to filter the skirt area.
                    size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1]))
                else:
                    size = 0
                disallowed_area_size = max(size, disallowed_area_size)

            self._disallowed_area_mesh = mb.build()
        else:
            self._disallowed_area_mesh = None

        if self._prime_tower_area:
            mb = MeshBuilder()
            color = Color(1.0, 0.0, 0.0, 0.5)
            points = self._prime_tower_area.getPoints()
            first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height,
                           self._clamp(points[0][1], min_d, max_d))
            previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height,
                                    self._clamp(points[0][1], min_d, max_d))
            for point in points:
                new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height,
                                   self._clamp(point[1], min_d, max_d))
                mb.addFace(first, previous_point, new_point, color=color)
                previous_point = new_point

            self._prime_tower_area_mesh = mb.build()
        else:
            self._prime_tower_area_mesh = None

        self._volume_aabb = AxisAlignedBox(
            minimum = Vector(min_w, min_h - 1.0, min_d),
            maximum = Vector(max_w, max_h - self._raft_thickness, max_d))

        bed_adhesion_size = 0.0

        container_stack = Application.getInstance().getGlobalContainerStack()
        if container_stack:
            bed_adhesion_size = self._getBedAdhesionSize(container_stack)

        # As this works better for UM machines, we only add the disallowed_area_size for the z direction.
        # This is probably wrong in all other cases. TODO!
        # The +1 and -1 is added as there is always a bit of extra room required to work properly.
        scale_to_max_bounds = AxisAlignedBox(
            minimum = Vector(min_w + bed_adhesion_size + 1, min_h, min_d + disallowed_area_size - bed_adhesion_size + 1),
            maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness, max_d - disallowed_area_size + bed_adhesion_size - 1)
        )

        Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds

    def getBoundingBox(self):
        return self._volume_aabb

    def _buildVolumeMessage(self):
        Message(catalog.i18nc(
            "@info:status",
            "The build volume height has been reduced due to the value of the"
            " \"Print Sequence\" setting to prevent the gantry from colliding"
            " with printed models.")).show()

    def getRaftThickness(self):
        return self._raft_thickness

    def _updateRaftThickness(self):
        old_raft_thickness = self._raft_thickness
        self._adhesion_type = self._global_container_stack.getProperty("adhesion_type", "value")
        self._raft_thickness = 0.0
        if self._adhesion_type == "raft":
            self._raft_thickness = (
                self._global_container_stack.getProperty("raft_base_thickness", "value") +
                self._global_container_stack.getProperty("raft_interface_thickness", "value") +
                self._global_container_stack.getProperty("raft_surface_layers", "value") *
                    self._global_container_stack.getProperty("raft_surface_thickness", "value") +
                self._global_container_stack.getProperty("raft_airgap", "value"))

        # Rounding errors do not matter, we check if raft_thickness has changed at all
        if old_raft_thickness != self._raft_thickness:
            self.setPosition(Vector(0, -self._raft_thickness, 0), SceneNode.TransformSpace.World)
            self.raftThicknessChanged.emit()

    def _onGlobalContainerStackChanged(self):
        if self._global_container_stack:
            self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)

        self._global_container_stack = Application.getInstance().getGlobalContainerStack()

        if self._global_container_stack:
            self._global_container_stack.propertyChanged.connect(self._onSettingPropertyChanged)

            self._width = self._global_container_stack.getProperty("machine_width", "value")
            machine_height = self._global_container_stack.getProperty("machine_height", "value")
            if self._global_container_stack.getProperty("print_sequence", "value") == "one_at_a_time":
                self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height)
                if self._height < machine_height:
                    self._buildVolumeMessage()
            else:
                self._height = self._global_container_stack.getProperty("machine_height", "value")
            self._depth = self._global_container_stack.getProperty("machine_depth", "value")

            self._updateDisallowedAreas()
            self._updateRaftThickness()

            self.rebuild()

    def _onActiveExtruderStackChanged(self):
        if self._active_extruder_stack:
            self._active_extruder_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
        self._active_extruder_stack = ExtruderManager.getInstance().getActiveExtruderStack()
        if self._active_extruder_stack:
            self._active_extruder_stack.propertyChanged.connect(self._onSettingPropertyChanged)

    def _onSettingPropertyChanged(self, setting_key, property_name):
        if property_name != "value":
            return

        rebuild_me = False
        if setting_key == "print_sequence":
            machine_height = self._global_container_stack.getProperty("machine_height", "value")
            if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time":
                self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height)
                if self._height < machine_height:
                    self._buildVolumeMessage()
            else:
                self._height = self._global_container_stack.getProperty("machine_height", "value")
            rebuild_me = True

        if setting_key in self._skirt_settings or setting_key in self._prime_settings or setting_key in self._tower_settings or setting_key == "print_sequence":
            self._updateDisallowedAreas()
            rebuild_me = True

        if setting_key in self._raft_settings:
            self._updateRaftThickness()
            rebuild_me = True

        if rebuild_me:
            self.rebuild()

    def hasErrors(self):
        return self._has_errors

    def _updateDisallowedAreas(self):
        if not self._global_container_stack:
            return
        self._has_errors = False  # Reset.
        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")
        self._prime_tower_area = None
        # Add prime tower location as disallowed area.
        if self._global_container_stack.getProperty("prime_tower_enable", "value") == 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

            self._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],
            ])

        # Add extruder prime locations as disallowed areas.
        # Probably needs some rework after coordinate system change.
        extruder_manager = ExtruderManager.getInstance()
        extruders = extruder_manager.getMachineExtruders(self._global_container_stack.getId())
        for single_extruder in extruders:
            extruder_prime_pos_x = single_extruder.getProperty("extruder_prime_pos_x", "value")
            extruder_prime_pos_y = single_extruder.getProperty("extruder_prime_pos_y", "value")
            # TODO: calculate everything in CuraEngine/Firmware/lower left as origin coordinates.
            # Here we transform the extruder prime pos (lower left as origin) to Cura coordinates
            # (center as origin, y from back to front)
            prime_x = extruder_prime_pos_x - machine_width / 2
            prime_y = machine_depth / 2 - extruder_prime_pos_y
            disallowed_areas.append([
                [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],
            ])

        bed_adhesion_size = self._getBedAdhesionSize(self._global_container_stack)

        if disallowed_areas:
            # Extend every area already in the disallowed_areas with the skirt size.
            for area in disallowed_areas:
                poly = Polygon(numpy.array(area, numpy.float32))
                poly = poly.getMinkowskiHull(Polygon(approximatedCircleVertices(bed_adhesion_size)))

                areas.append(poly)

        if self._prime_tower_area:
            self._prime_tower_area = self._prime_tower_area.getMinkowskiHull(Polygon(approximatedCircleVertices(bed_adhesion_size)))

        # Add the skirt areas around the borders of the build plate.
        if bed_adhesion_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 + bed_adhesion_size, half_machine_depth - bed_adhesion_size],
                [-half_machine_width + bed_adhesion_size, -half_machine_depth + bed_adhesion_size]
            ], numpy.float32)))

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

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

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

        # Check if the prime tower area intersects with any of the other areas.
        # If this is the case, keep the polygon seperate, so it can be drawn in red.
        # If not, add it back to disallowed area's, so it's rendered as normal.
        collision = False
        if self._prime_tower_area:
            for area in areas:
                if self._prime_tower_area.intersectsPolygon(area) is not None:
                    collision = True
                    break
            if not collision:
                areas.append(self._prime_tower_area)
                self._prime_tower_area = None
        self._has_errors = collision
        self._disallowed_areas = areas

    ##  Convenience function to calculate the size of the bed adhesion in directions x, y.
    def _getBedAdhesionSize(self, container_stack):
        skirt_size = 0.0

        # If we are printing one at a time, we need to add the bed adhesion size to the disallowed areas of the objects
        if container_stack.getProperty("print_sequence", "value") == "one_at_a_time":
            return 0.1  # Return a very small value, so we do draw disallowed area's near the edges.

        adhesion_type = container_stack.getProperty("adhesion_type", "value")
        if adhesion_type == "skirt":
            skirt_distance = container_stack.getProperty("skirt_gap", "value")
            skirt_line_count = container_stack.getProperty("skirt_line_count", "value")
            skirt_size = skirt_distance + (skirt_line_count * container_stack.getProperty("skirt_brim_line_width", "value"))
        elif adhesion_type == "brim":
            skirt_size = container_stack.getProperty("brim_line_count", "value") * container_stack.getProperty("skirt_brim_line_width", "value")
        elif adhesion_type == "raft":
            skirt_size = container_stack.getProperty("raft_margin", "value")

        if container_stack.getProperty("draft_shield_enabled", "value"):
            skirt_size += container_stack.getProperty("draft_shield_dist", "value")

        if container_stack.getProperty("xy_offset", "value"):
            skirt_size += container_stack.getProperty("xy_offset", "value")

        return skirt_size

    def _clamp(self, value, min_value, max_value):
        return max(min(value, max_value), min_value)

    _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"]
    _raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap"]
    _prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "extruder_prime_pos_z"]
    _tower_settings = ["prime_tower_enable", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"]
예제 #26
0
    def _updateDisallowedAreas(self):
        if not self._global_container_stack:
            return
        self._has_errors = False  # Reset.
        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")
        self._prime_tower_area = None
        # Add prime tower location as disallowed area.
        if self._global_container_stack.getProperty("prime_tower_enable", "value") == 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

            self._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],
            ])

        # Add extruder prime locations as disallowed areas.
        # Probably needs some rework after coordinate system change.
        extruder_manager = ExtruderManager.getInstance()
        extruders = extruder_manager.getMachineExtruders(self._global_container_stack.getId())
        for single_extruder in extruders:
            extruder_prime_pos_x = single_extruder.getProperty("extruder_prime_pos_x", "value")
            extruder_prime_pos_y = single_extruder.getProperty("extruder_prime_pos_y", "value")
            # TODO: calculate everything in CuraEngine/Firmware/lower left as origin coordinates.
            # Here we transform the extruder prime pos (lower left as origin) to Cura coordinates
            # (center as origin, y from back to front)
            prime_x = extruder_prime_pos_x - machine_width / 2
            prime_y = machine_depth / 2 - extruder_prime_pos_y
            disallowed_areas.append([
                [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],
            ])

        bed_adhesion_size = self._getBedAdhesionSize(self._global_container_stack)

        if disallowed_areas:
            # Extend every area already in the disallowed_areas with the skirt size.
            for area in disallowed_areas:
                poly = Polygon(numpy.array(area, numpy.float32))
                poly = poly.getMinkowskiHull(Polygon(approximatedCircleVertices(bed_adhesion_size)))

                areas.append(poly)

        if self._prime_tower_area:
            self._prime_tower_area = self._prime_tower_area.getMinkowskiHull(Polygon(approximatedCircleVertices(bed_adhesion_size)))

        # Add the skirt areas around the borders of the build plate.
        if bed_adhesion_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 + bed_adhesion_size, half_machine_depth - bed_adhesion_size],
                [-half_machine_width + bed_adhesion_size, -half_machine_depth + bed_adhesion_size]
            ], numpy.float32)))

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

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

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

        # Check if the prime tower area intersects with any of the other areas.
        # If this is the case, keep the polygon seperate, so it can be drawn in red.
        # If not, add it back to disallowed area's, so it's rendered as normal.
        collision = False
        if self._prime_tower_area:
            for area in areas:
                if self._prime_tower_area.intersectsPolygon(area) is not None:
                    collision = True
                    break
            if not collision:
                areas.append(self._prime_tower_area)
                self._prime_tower_area = None
        self._has_errors = collision
        self._disallowed_areas = areas
예제 #27
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
예제 #28
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
예제 #29
0
    def run(self):
        if not self._node:
            return
        ## If the scene node is a group, use the hull of the children to calculate its hull.
        if self._node.callDecoration("isGroup"):
            hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32))
            for child in self._node.getChildren():
                child_hull = child.callDecoration("getConvexHull") 
                if child_hull:
                    hull.setPoints(numpy.append(hull.getPoints(), child_hull.getPoints(), axis = 0))

                if hull.getPoints().size < 3:
                    self._node.callDecoration("setConvexHull", None)
                    self._node.callDecoration("setConvexHullJob", None)
                    return

                Job.yieldThread()

        else: 
            if not self._node.getMeshData():
                return
            mesh = self._node.getMeshData()
            vertex_data = mesh.getTransformed(self._node.getWorldTransformation()).getVertices()
            # Don't use data below 0.
            # TODO; We need a better check for this as this gives poor results for meshes with long edges.
            vertex_data = vertex_data[vertex_data[:,1] >= 0]

            # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices
            # This is done to greatly speed up further convex hull calculations as the convex hull
            # becomes much less complex when dealing with highly detailed models.
            vertex_data = numpy.round(vertex_data, 1)
            duplicates = (vertex_data[:,0] == vertex_data[:,1]) | (vertex_data[:,1] == vertex_data[:,2]) | (vertex_data[:,0] == vertex_data[:,2])
            vertex_data = numpy.delete(vertex_data, numpy.where(duplicates), axis = 0)

            hull = Polygon(vertex_data[:, [0, 2]])

        # First, calculate the normal convex hull around the points
        hull = hull.getConvexHull()

        # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
        # This is done because of rounding errors.
        hull = hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32)))

        profile = Application.getInstance().getMachineManager().getWorkingProfile()
        if profile:
            if profile.getSettingValue("print_sequence") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
                # Printing one at a time and it's not an object in a group
                self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull))
                head_and_fans = Polygon(numpy.array(profile.getSettingValue("machine_head_with_fans_polygon"), numpy.float32))

                # Full head hull is used to actually check the order.
                full_head_hull = hull.getMinkowskiHull(head_and_fans)
                self._node.callDecoration("setConvexHullHeadFull", full_head_hull)
                mirrored = copy.deepcopy(head_and_fans)
                mirrored.mirror([0, 0], [0, 1]) #Mirror horizontally.
                mirrored.mirror([0, 0], [1, 0]) #Mirror vertically.
                head_and_fans = head_and_fans.intersectionConvexHulls(mirrored)

                # Min head hull is used for the push free
                min_head_hull = hull.getMinkowskiHull(head_and_fans)
                self._node.callDecoration("setConvexHullHead", min_head_hull)
                hull = hull.getMinkowskiHull(Polygon(numpy.array(profile.getSettingValue("machine_head_polygon"),numpy.float32)))
            else:
                self._node.callDecoration("setConvexHullHead", None)
        if self._node.getParent() is None:  # Node was already deleted before job is done.
            self._node.callDecoration("setConvexHullNode",None)
            self._node.callDecoration("setConvexHull", None)
            self._node.callDecoration("setConvexHullJob", None)
            return

        hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot())
        self._node.callDecoration("setConvexHullNode", hull_node)
        self._node.callDecoration("setConvexHull", hull)
        self._node.callDecoration("setConvexHullJob", None)

        if self._node.getParent() and self._node.getParent().callDecoration("isGroup"):
            job = self._node.getParent().callDecoration("getConvexHullJob")
            if job:
                job.cancel()
            self._node.getParent().callDecoration("setConvexHull", None)
            hull_node = self._node.getParent().callDecoration("getConvexHullNode")
            if hull_node:
                hull_node.setParent(None)
예제 #30
0
    def run(self):
        if not self._node:
            return
        ## If the scene node is a group, use the hull of the children to calculate its hull.
        if self._node.callDecoration("isGroup"):
            hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32))
            for child in self._node.getChildren():
                child_hull = child.callDecoration("getConvexHull")
                if child_hull:
                    hull.setPoints(
                        numpy.append(hull.getPoints(),
                                     child_hull.getPoints(),
                                     axis=0))

                if hull.getPoints().size < 3:
                    self._node.callDecoration("setConvexHull", None)
                    self._node.callDecoration("setConvexHullJob", None)
                    return

                Job.yieldThread()

        else:
            if not self._node.getMeshData():
                return
            mesh = self._node.getMeshData()
            vertex_data = mesh.getTransformed(
                self._node.getWorldTransformation()).getVertices()
            # Don't use data below 0.
            # TODO; We need a better check for this as this gives poor results for meshes with long edges.
            vertex_data = vertex_data[vertex_data[:, 1] >= 0]

            # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices
            # This is done to greatly speed up further convex hull calculations as the convex hull
            # becomes much less complex when dealing with highly detailed models.
            vertex_data = numpy.round(vertex_data, 1)
            duplicates = (vertex_data[:, 0] == vertex_data[:, 1]) | (
                vertex_data[:, 1] == vertex_data[:, 2]) | (
                    vertex_data[:, 0] == vertex_data[:, 2])
            vertex_data = numpy.delete(vertex_data,
                                       numpy.where(duplicates),
                                       axis=0)

            hull = Polygon(vertex_data[:, [0, 2]])

        # First, calculate the normal convex hull around the points
        hull = hull.getConvexHull()

        # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
        # This is done because of rounding errors.
        hull = hull.getMinkowskiHull(
            Polygon(
                numpy.array(
                    [[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]],
                    numpy.float32)))

        profile = Application.getInstance().getMachineManager(
        ).getWorkingProfile()
        if profile:
            if profile.getSettingValue(
                    "print_sequence"
            ) == "one_at_a_time" and not self._node.getParent().callDecoration(
                    "isGroup"):
                # Printing one at a time and it's not an object in a group
                self._node.callDecoration("setConvexHullBoundary",
                                          copy.deepcopy(hull))
                head_and_fans = Polygon(
                    numpy.array(
                        profile.getSettingValue(
                            "machine_head_with_fans_polygon"), numpy.float32))

                # Full head hull is used to actually check the order.
                full_head_hull = hull.getMinkowskiHull(head_and_fans)
                self._node.callDecoration("setConvexHullHeadFull",
                                          full_head_hull)
                mirrored = copy.deepcopy(head_and_fans)
                mirrored.mirror([0, 0], [0, 1])  #Mirror horizontally.
                mirrored.mirror([0, 0], [1, 0])  #Mirror vertically.
                head_and_fans = head_and_fans.intersectionConvexHulls(mirrored)

                # Min head hull is used for the push free
                min_head_hull = hull.getMinkowskiHull(head_and_fans)
                self._node.callDecoration("setConvexHullHead", min_head_hull)
                hull = hull.getMinkowskiHull(
                    Polygon(
                        numpy.array(
                            profile.getSettingValue("machine_head_polygon"),
                            numpy.float32)))
            else:
                self._node.callDecoration("setConvexHullHead", None)
        if self._node.getParent(
        ) is None:  # Node was already deleted before job is done.
            self._node.callDecoration("setConvexHullNode", None)
            self._node.callDecoration("setConvexHull", None)
            self._node.callDecoration("setConvexHullJob", None)
            return

        hull_node = ConvexHullNode.ConvexHullNode(
            self._node, hull,
            Application.getInstance().getController().getScene().getRoot())
        self._node.callDecoration("setConvexHullNode", hull_node)
        self._node.callDecoration("setConvexHull", hull)
        self._node.callDecoration("setConvexHullJob", None)

        if self._node.getParent() and self._node.getParent().callDecoration(
                "isGroup"):
            job = self._node.getParent().callDecoration("getConvexHullJob")
            if job:
                job.cancel()
            self._node.getParent().callDecoration("setConvexHull", None)
            hull_node = self._node.getParent().callDecoration(
                "getConvexHullNode")
            if hull_node:
                hull_node.setParent(None)
예제 #31
0
    def _updateDisallowedAreas(self):
        if not self._active_container_stack:
            return

        disallowed_areas = copy.deepcopy(
            self._active_container_stack.getProperty("machine_disallowed_areas", "value"))
        areas = []

        # Add extruder prime locations as disallowed areas.
        # Probably needs some rework after coordinate system change.
        extruder_manager = ExtruderManager.getInstance()
        extruders = extruder_manager.getMachineExtruders(self._active_container_stack.getId())
        machine_width = self._active_container_stack.getProperty("machine_width", "value")
        machine_depth = self._active_container_stack.getProperty("machine_depth", "value")
        for single_extruder in extruders:
            extruder_prime_pos_x = single_extruder.getProperty("extruder_prime_pos_x", "value")
            extruder_prime_pos_y = single_extruder.getProperty("extruder_prime_pos_y", "value")
            # TODO: calculate everything in CuraEngine/Firmware/lower left as origin coordinates.
            # Here we transform the extruder prime pos (lower left as origin) to Cura coordinates
            # (center as origin, y from back to front)
            prime_x = extruder_prime_pos_x - machine_width / 2
            prime_y = machine_depth / 2 - extruder_prime_pos_y
            disallowed_areas.append([
                [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],
            ])

        bed_adhesion_size = self._getBedAdhesionSize(self._active_container_stack)

        if disallowed_areas:
            # Extend every area already in the disallowed_areas with the skirt size.
            for area in disallowed_areas:
                poly = Polygon(numpy.array(area, numpy.float32))
                poly = poly.getMinkowskiHull(Polygon(approximatedCircleVertices(bed_adhesion_size)))

                areas.append(poly)

        # Add the skirt areas around the borders of the build plate.
        if bed_adhesion_size > 0:
            half_machine_width = self._active_container_stack.getProperty("machine_width", "value") / 2
            half_machine_depth = self._active_container_stack.getProperty("machine_depth", "value") / 2

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

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

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

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

        self._disallowed_areas = areas
예제 #32
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
예제 #33
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])
예제 #34
0
    def run(self):
        if not self._node:
            return
        ## If the scene node is a group, use the hull of the children to calculate its hull.
        if self._node.callDecoration("isGroup"):
            hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32))
            for child in self._node.getChildren():
                child_hull = child.callDecoration("getConvexHull") 
                if child_hull:
                    hull.setPoints(numpy.append(hull.getPoints(), child_hull.getPoints(), axis = 0))

                if hull.getPoints().size < 3:
                    self._node.callDecoration("setConvexHull", None)
                    self._node.callDecoration("setConvexHullJob", None)
                    return

                Job.yieldThread()

        else: 
            if not self._node.getMeshData():
                return
            mesh = self._node.getMeshData()
            vertex_data = mesh.getTransformed(self._node.getWorldTransformation()).getVertices()
            # Don't use data below 0.
            # TODO; We need a better check for this as this gives poor results for meshes with long edges.
            vertex_data = vertex_data[vertex_data[:,1] >= 0]

            # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices
            # This is done to greatly speed up further convex hull calculations as the convex hull
            # becomes much less complex when dealing with highly detailed models.
            vertex_data = numpy.round(vertex_data, 1)

            vertex_data = vertex_data[:, [0, 2]]    # Drop the Y components to project to 2D.

            # Grab the set of unique points.
            #
            # This basically finds the unique rows in the array by treating them as opaque groups of bytes
            # which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch.
            # See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array
            vertex_byte_view = numpy.ascontiguousarray(vertex_data).view(numpy.dtype((numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1])))
            _, idx = numpy.unique(vertex_byte_view, return_index=True)
            vertex_data = vertex_data[idx]  # Select the unique rows by index.

            hull = Polygon(vertex_data)

        # First, calculate the normal convex hull around the points
        hull = hull.getConvexHull()

        # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
        # This is done because of rounding errors.
        hull = hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32)))

        global_stack = Application.getInstance().getGlobalContainerStack()
        if global_stack:
            if global_stack.getProperty("print_sequence", "value")== "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
                # Printing one at a time and it's not an object in a group
                self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull))
                head_and_fans = Polygon(numpy.array(global_stack.getProperty("machine_head_with_fans_polygon", "value"), numpy.float32))

                # Full head hull is used to actually check the order.
                full_head_hull = hull.getMinkowskiHull(head_and_fans)
                self._node.callDecoration("setConvexHullHeadFull", full_head_hull)
                mirrored = copy.deepcopy(head_and_fans)
                mirrored.mirror([0, 0], [0, 1]) #Mirror horizontally.
                mirrored.mirror([0, 0], [1, 0]) #Mirror vertically.
                head_and_fans = head_and_fans.intersectionConvexHulls(mirrored)

                # Min head hull is used for the push free
                min_head_hull = hull.getMinkowskiHull(head_and_fans)
                self._node.callDecoration("setConvexHullHead", min_head_hull)
                hull = hull.getMinkowskiHull(Polygon(numpy.array(global_stack.getProperty("machine_head_polygon","value"),numpy.float32)))
            else:
                self._node.callDecoration("setConvexHullHead", None)
        if self._node.getParent() is None:  # Node was already deleted before job is done.
            self._node.callDecoration("setConvexHullNode",None)
            self._node.callDecoration("setConvexHull", None)
            self._node.callDecoration("setConvexHullJob", None)
            return

        hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot())
        self._node.callDecoration("setConvexHullNode", hull_node)
        self._node.callDecoration("setConvexHull", hull)
        self._node.callDecoration("setConvexHullJob", None)

        if self._node.getParent() and self._node.getParent().callDecoration("isGroup"):
            job = self._node.getParent().callDecoration("getConvexHullJob")
            if job:
                job.cancel()
            self._node.getParent().callDecoration("setConvexHull", None)
            hull_node = self._node.getParent().callDecoration("getConvexHullNode")
            if hull_node:
                hull_node.setParent(None)
예제 #35
0
class BuildVolume(SceneNode):
    VolumeOutlineColor = Color(12, 169, 227, 255)

    raftThicknessChanged = Signal()

    def __init__(self, parent = None):
        super().__init__(parent)

        self._width = 0
        self._height = 0
        self._depth = 0

        self._shader = None

        self._grid_mesh = None
        self._grid_shader = None

        self._disallowed_areas = []
        self._disallowed_area_mesh = None

        self._prime_tower_area = None
        self._prime_tower_area_mesh = None

        self.setCalculateBoundingBox(False)
        self._volume_aabb = None

        self._raft_thickness = 0.0
        self._adhesion_type = None
        self._platform = Platform(self)

        self._global_container_stack = None
        Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
        self._onGlobalContainerStackChanged()

        self._active_extruder_stack = None
        ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
        self._onActiveExtruderStackChanged()

        self._has_errors = False

    def setWidth(self, width):
        if width: self._width = width

    def setHeight(self, height):
        if height: self._height = height

    def setDepth(self, depth):
        if depth: self._depth = depth

    def getDisallowedAreas(self):
        return self._disallowed_areas

    def setDisallowedAreas(self, areas):
        self._disallowed_areas = areas

    def render(self, renderer):
        if not self.getMeshData():
            return True

        if not self._shader:
            self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader"))
            self._grid_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "grid.shader"))

        renderer.queueNode(self, mode = RenderBatch.RenderMode.Lines)
        renderer.queueNode(self, mesh = self._grid_mesh, shader = self._grid_shader, backface_cull = True)
        if self._disallowed_area_mesh:
            renderer.queueNode(self, mesh = self._disallowed_area_mesh, shader = self._shader, transparent = True, backface_cull = True, sort = -9)

        if self._prime_tower_area_mesh:
            renderer.queueNode(self, mesh = self._prime_tower_area_mesh, shader = self._shader, transparent=True,
                               backface_cull=True, sort=-8)

        return True

    ##  Recalculates the build volume & disallowed areas.
    def rebuild(self):
        if not self._width or not self._height or not self._depth:
            return

        min_w = -self._width / 2
        max_w = self._width / 2
        min_h = 0.0
        max_h = self._height
        min_d = -self._depth / 2
        max_d = self._depth / 2

        mb = MeshBuilder()

        # Outline 'cube' of the build volume
        mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor)

        mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)

        mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
        mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)

        self.setMeshData(mb.build())

        mb = MeshBuilder()
        mb.addQuad(
            Vector(min_w, min_h - 0.2, min_d),
            Vector(max_w, min_h - 0.2, min_d),
            Vector(max_w, min_h - 0.2, max_d),
            Vector(min_w, min_h - 0.2, max_d)
        )

        for n in range(0, 6):
            v = mb.getVertex(n)
            mb.setVertexUVCoordinates(n, v[0], v[2])
        self._grid_mesh = mb.build()

        disallowed_area_height = 0.1
        disallowed_area_size = 0
        if self._disallowed_areas:
            mb = MeshBuilder()
            color = Color(0.0, 0.0, 0.0, 0.15)
            for polygon in self._disallowed_areas:
                points = polygon.getPoints()
                first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d))
                previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d))
                for point in points:
                    new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height, self._clamp(point[1], min_d, max_d))
                    mb.addFace(first, previous_point, new_point, color = color)
                    previous_point = new_point

                # Find the largest disallowed area to exclude it from the maximum scale bounds.
                # This is a very nasty hack. This pretty much only works for UM machines.
                # This disallowed area_size needs a -lot- of rework at some point in the future: TODO
                if numpy.min(points[:, 1]) >= 0: # This filters out all areas that have points to the left of the centre. This is done to filter the skirt area.
                    size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1]))
                else:
                    size = 0
                disallowed_area_size = max(size, disallowed_area_size)

            self._disallowed_area_mesh = mb.build()
        else:
            self._disallowed_area_mesh = None

        if self._prime_tower_area:
            mb = MeshBuilder()
            color = Color(1.0, 0.0, 0.0, 0.5)
            points = self._prime_tower_area.getPoints()
            first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height,
                           self._clamp(points[0][1], min_d, max_d))
            previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height,
                                    self._clamp(points[0][1], min_d, max_d))
            for point in points:
                new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height,
                                   self._clamp(point[1], min_d, max_d))
                mb.addFace(first, previous_point, new_point, color=color)
                previous_point = new_point

            self._prime_tower_area_mesh = mb.build()
        else:
            self._prime_tower_area_mesh = None

        self._volume_aabb = AxisAlignedBox(
            minimum = Vector(min_w, min_h - 1.0, min_d),
            maximum = Vector(max_w, max_h - self._raft_thickness, max_d))

        bed_adhesion_size = 0.0

        container_stack = Application.getInstance().getGlobalContainerStack()
        if container_stack:
            bed_adhesion_size = self._getBedAdhesionSize(container_stack)

        # As this works better for UM machines, we only add the disallowed_area_size for the z direction.
        # This is probably wrong in all other cases. TODO!
        # The +1 and -1 is added as there is always a bit of extra room required to work properly.
        scale_to_max_bounds = AxisAlignedBox(
            minimum = Vector(min_w + bed_adhesion_size + 1, min_h, min_d + disallowed_area_size - bed_adhesion_size + 1),
            maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness, max_d - disallowed_area_size + bed_adhesion_size - 1)
        )

        Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds

    def getBoundingBox(self):
        return self._volume_aabb

    def _buildVolumeMessage(self):
        Message(catalog.i18nc(
            "@info:status",
            "The build volume height has been reduced due to the value of the"
            " \"Print Sequence\" setting to prevent the gantry from colliding"
            " with printed models.")).show()

    def getRaftThickness(self):
        return self._raft_thickness

    def _updateRaftThickness(self):
        old_raft_thickness = self._raft_thickness
        self._adhesion_type = self._global_container_stack.getProperty("adhesion_type", "value")
        self._raft_thickness = 0.0
        if self._adhesion_type == "raft":
            self._raft_thickness = (
                self._global_container_stack.getProperty("raft_base_thickness", "value") +
                self._global_container_stack.getProperty("raft_interface_thickness", "value") +
                self._global_container_stack.getProperty("raft_surface_layers", "value") *
                    self._global_container_stack.getProperty("raft_surface_thickness", "value") +
                self._global_container_stack.getProperty("raft_airgap", "value"))

        # Rounding errors do not matter, we check if raft_thickness has changed at all
        if old_raft_thickness != self._raft_thickness:
            self.setPosition(Vector(0, -self._raft_thickness, 0), SceneNode.TransformSpace.World)
            self.raftThicknessChanged.emit()

    def _onGlobalContainerStackChanged(self):
        if self._global_container_stack:
            self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)

        self._global_container_stack = Application.getInstance().getGlobalContainerStack()

        if self._global_container_stack:
            self._global_container_stack.propertyChanged.connect(self._onSettingPropertyChanged)

            self._width = self._global_container_stack.getProperty("machine_width", "value")
            machine_height = self._global_container_stack.getProperty("machine_height", "value")
            if self._global_container_stack.getProperty("print_sequence", "value") == "one_at_a_time":
                self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height)
                if self._height < machine_height:
                    self._buildVolumeMessage()
            else:
                self._height = self._global_container_stack.getProperty("machine_height", "value")
            self._depth = self._global_container_stack.getProperty("machine_depth", "value")

            self._updateDisallowedAreas()
            self._updateRaftThickness()

            self.rebuild()

    def _onActiveExtruderStackChanged(self):
        if self._active_extruder_stack:
            self._active_extruder_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
        self._active_extruder_stack = ExtruderManager.getInstance().getActiveExtruderStack()
        if self._active_extruder_stack:
            self._active_extruder_stack.propertyChanged.connect(self._onSettingPropertyChanged)

    def _onSettingPropertyChanged(self, setting_key, property_name):
        if property_name != "value":
            return

        rebuild_me = False
        if setting_key == "print_sequence":
            machine_height = self._global_container_stack.getProperty("machine_height", "value")
            if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time":
                self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height)
                if self._height < machine_height:
                    self._buildVolumeMessage()
            else:
                self._height = self._global_container_stack.getProperty("machine_height", "value")
            rebuild_me = True

        if setting_key in self._skirt_settings or setting_key in self._prime_settings or setting_key in self._tower_settings or setting_key == "print_sequence":
            self._updateDisallowedAreas()
            rebuild_me = True

        if setting_key in self._raft_settings:
            self._updateRaftThickness()
            rebuild_me = True

        if rebuild_me:
            self.rebuild()

    def hasErrors(self):
        return self._has_errors

    def _updateDisallowedAreas(self):
        if not self._global_container_stack:
            return
        self._has_errors = False  # Reset.
        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")
        self._prime_tower_area = None
        # Add prime tower location as disallowed area.
        if self._global_container_stack.getProperty("prime_tower_enable", "value") == 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

            '''disallowed_areas.append([
                [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],
            ])'''

            self._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],
            ])

        # Add extruder prime locations as disallowed areas.
        # Probably needs some rework after coordinate system change.
        extruder_manager = ExtruderManager.getInstance()
        extruders = extruder_manager.getMachineExtruders(self._global_container_stack.getId())
        for single_extruder in extruders:
            extruder_prime_pos_x = single_extruder.getProperty("extruder_prime_pos_x", "value")
            extruder_prime_pos_y = single_extruder.getProperty("extruder_prime_pos_y", "value")
            # TODO: calculate everything in CuraEngine/Firmware/lower left as origin coordinates.
            # Here we transform the extruder prime pos (lower left as origin) to Cura coordinates
            # (center as origin, y from back to front)
            prime_x = extruder_prime_pos_x - machine_width / 2
            prime_y = machine_depth / 2 - extruder_prime_pos_y
            disallowed_areas.append([
                [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],
            ])

        bed_adhesion_size = self._getBedAdhesionSize(self._global_container_stack)

        if disallowed_areas:
            # Extend every area already in the disallowed_areas with the skirt size.
            for area in disallowed_areas:
                poly = Polygon(numpy.array(area, numpy.float32))
                poly = poly.getMinkowskiHull(Polygon(approximatedCircleVertices(bed_adhesion_size)))

                areas.append(poly)

        if self._prime_tower_area:
            self._prime_tower_area = self._prime_tower_area.getMinkowskiHull(Polygon(approximatedCircleVertices(bed_adhesion_size)))

        # Add the skirt areas around the borders of the build plate.
        if bed_adhesion_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 + bed_adhesion_size, half_machine_depth - bed_adhesion_size],
                [-half_machine_width + bed_adhesion_size, -half_machine_depth + bed_adhesion_size]
            ], numpy.float32)))

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

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

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

        # Check if the prime tower area intersects with any of the other areas.
        # If this is the case, keep the polygon seperate, so it can be drawn in red.
        # If not, add it back to disallowed area's, so it's rendered as normal.
        collision = False
        if self._prime_tower_area:
            for area in areas:
                if self._prime_tower_area.intersectsPolygon(area) is not None:
                    collision = True
                    break
            if not collision:
                areas.append(self._prime_tower_area)
                self._prime_tower_area = None
        self._has_errors = collision
        self._disallowed_areas = areas

    ##  Convenience function to calculate the size of the bed adhesion in directions x, y.
    def _getBedAdhesionSize(self, container_stack):
        skirt_size = 0.0

        # If we are printing one at a time, we need to add the bed adhesion size to the disallowed areas of the objects
        if container_stack.getProperty("print_sequence", "value") == "one_at_a_time":
            return 0.1  # Return a very small value, so we do draw disallowed area's near the edges.

        adhesion_type = container_stack.getProperty("adhesion_type", "value")
        if adhesion_type == "skirt":
            skirt_distance = container_stack.getProperty("skirt_gap", "value")
            skirt_line_count = container_stack.getProperty("skirt_line_count", "value")
            skirt_size = skirt_distance + (skirt_line_count * container_stack.getProperty("skirt_brim_line_width", "value"))
        elif adhesion_type == "brim":
            skirt_size = container_stack.getProperty("brim_line_count", "value") * container_stack.getProperty("skirt_brim_line_width", "value")
        elif adhesion_type == "raft":
            skirt_size = container_stack.getProperty("raft_margin", "value")

        if container_stack.getProperty("draft_shield_enabled", "value"):
            skirt_size += container_stack.getProperty("draft_shield_dist", "value")

        if container_stack.getProperty("xy_offset", "value"):
            skirt_size += container_stack.getProperty("xy_offset", "value")

        return skirt_size

    def _clamp(self, value, min_value, max_value):
        return max(min(value, max_value), min_value)

    _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"]
    _raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap"]
    _prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "extruder_prime_pos_z"]
    _tower_settings = ["prime_tower_enable", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"]
예제 #36
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
예제 #37
0
    def _updateDisallowedAreas(self):
        if not self._global_container_stack:
            return

        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")

        # Add prime tower location as disallowed area.
        if self._global_container_stack.getProperty("prime_tower_enable",
                                                    "value") == 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

            disallowed_areas.append([
                [
                    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],
            ])

        # Add extruder prime locations as disallowed areas.
        # Probably needs some rework after coordinate system change.
        extruder_manager = ExtruderManager.getInstance()
        extruders = extruder_manager.getMachineExtruders(
            self._global_container_stack.getId())
        for single_extruder in extruders:
            extruder_prime_pos_x = single_extruder.getProperty(
                "extruder_prime_pos_x", "value")
            extruder_prime_pos_y = single_extruder.getProperty(
                "extruder_prime_pos_y", "value")
            # TODO: calculate everything in CuraEngine/Firmware/lower left as origin coordinates.
            # Here we transform the extruder prime pos (lower left as origin) to Cura coordinates
            # (center as origin, y from back to front)
            prime_x = extruder_prime_pos_x - machine_width / 2
            prime_y = machine_depth / 2 - extruder_prime_pos_y
            disallowed_areas.append([
                [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],
            ])

        bed_adhesion_size = self._getBedAdhesionSize(
            self._global_container_stack)

        if disallowed_areas:
            # Extend every area already in the disallowed_areas with the skirt size.
            for area in disallowed_areas:
                poly = Polygon(numpy.array(area, numpy.float32))
                poly = poly.getMinkowskiHull(
                    Polygon(approximatedCircleVertices(bed_adhesion_size)))

                areas.append(poly)

        # Add the skirt areas around the borders of the build plate.
        if bed_adhesion_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 + bed_adhesion_size,
                                     half_machine_depth - bed_adhesion_size
                                 ],
                                 [
                                     -half_machine_width + bed_adhesion_size,
                                     -half_machine_depth + bed_adhesion_size
                                 ]], numpy.float32)))

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

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

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

        self._disallowed_areas = areas