Beispiel #1
0
def test_getConvexHullBoundaryNotPrintingMesh(convex_hull_decorator):
    node = SceneNode()
    node.addDecorator(NonPrintingDecorator())
    with patch("UM.Application.Application.getInstance",
               MagicMock(return_value=mocked_application)):
        convex_hull_decorator.setNode(node)
    assert convex_hull_decorator.getConvexHullBoundary() is None
def test_getConvexHullPrintingMesh(convex_hull_decorator):
    node = SceneNode()
    node.addDecorator(PrintingDecorator())
    with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)):
        convex_hull_decorator.setNode(node)
    convex_hull_decorator._compute2DConvexHull = MagicMock(return_value = Polygon.approximatedCircle(10))
    assert convex_hull_decorator.getConvexHull() == Polygon.approximatedCircle(10)
def test_getConvexHulLBoundaryPrintingMesh(convex_hull_decorator):
    node = SceneNode()
    node.addDecorator(PrintingDecorator())
    with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)):
        convex_hull_decorator.setNode(node)
    # Should still be None, since print sequence is not one at a time
    assert convex_hull_decorator.getConvexHullBoundary() is None
def test_SceneNodeDecorator():
    test_node = SceneNode()
    test_decorator = SceneNodeDecorator()
    amazing_decorator = TheAmazingTestDecorator()
    less_amazing_decorator = TheLessAmazingTestDecorator()
    not_amazing_decorator = TheNotSoAmazingTestDecorator()

    # Replace emit with mock object
    test_node.decoratorsChanged.emit = MagicMock()
    test_decorator.clear = MagicMock()

    # First actual change
    test_node.addDecorator(test_decorator)
    assert len(test_node.getDecorators()) == 1
    assert test_node.decoratorsChanged.emit.call_count == 1

    # Adding a decorator of the same type (SceneNodeDecorator) again should not do anything.
    test_node.addDecorator(test_decorator)
    assert len(test_node.getDecorators()) == 1
    assert test_node.decoratorsChanged.emit.call_count == 1
    assert test_node.getDecorator(type(test_decorator)) == test_decorator

    # Remove the decorator again!
    test_node.removeDecorator(SceneNodeDecorator)
    assert len(test_node.getDecorators()) == 0
    assert test_node.decoratorsChanged.emit.call_count == 2
    assert test_decorator.clear.call_count == 1  # Ensure that the clear of the test decorator is called.

    # Add a number of decorators!
    test_node.addDecorator(amazing_decorator)
    test_node.addDecorator(less_amazing_decorator)
    test_node.addDecorator(not_amazing_decorator)
    assert test_node.decoratorsChanged.emit.call_count == 5
    assert len(test_node.getDecorators()) == 3

    assert test_node.hasDecoration("zomg") == False
    assert test_node.hasDecoration("theOkayDecoration")
    assert test_node.hasDecoration("theAmazingDecoration")

    # Calling the decorations with args / kwargs
    assert test_node.callDecoration("theOkayDecoration", None) is None
    assert test_node.callDecoration("theEvenMoreAmazingDecoration",
                                    "beep") == ("beep", "Wow", "so wow")
    assert test_node.callDecoration("theEvenMoreAmazingDecoration",
                                    "beep",
                                    much_test="Wow") == ("beep", "Wow", "Wow")

    # Calling decoration that is "double"
    assert test_node.callDecoration("theAmazingDecoration") == "Amazing!"
    test_node.removeDecorator(TheAmazingTestDecorator)
    assert test_node.callDecoration("theAmazingDecoration") == "amazing"

    not_amazing_decorator.clear = MagicMock()
    test_node.removeDecorators()
    # Also assure that removing all decorators also triggers the clear
    assert not_amazing_decorator.clear.call_count == 1
    assert len(test_node.getDecorators()) == 0
    assert test_node.decoratorsChanged.emit.call_count == 7
    assert test_node.getDecorator(type(test_decorator)) is None
Beispiel #5
0
def test_getConvexHulLBoundaryPrintingMesh(convex_hull_decorator):
    node = SceneNode()
    node.addDecorator(PrintingDecorator())
    with patch("UM.Application.Application.getInstance",
               MagicMock(return_value=mocked_application)):
        convex_hull_decorator.setNode(node)
    # Should still be None, since print sequence is not one at a time
    assert convex_hull_decorator.getConvexHullBoundary() is None
Beispiel #6
0
    def read(self, file_name):
        extension = os.path.splitext(file_name)[1]
        if extension.lower() == self._supported_extension:
            layer_data = LayerData()
            with open(file_name, "rt") as f:
                layer = ""
                current_path_type = ""

                current_layer_nr = 0
                poly_list = []
                old_position = [0, 0, 0]
                current_z = 0
                for line in f:
                    if line.startswith(';TYPE:'):
                        current_path_type = line[6:].strip()
                        #layer_data.addPolygon(current_layer_nr,3 ,None ,5 )
                    elif line.startswith(';LAYER:'):
                        current_layer_nr = int(line[7:].strip())
                        layer_data.addLayer(int(line[7:].strip()))
                    elif line.startswith(';'):
                        pass  # Ignore comments
                    else:
                        command_type = self.getCodeInt(line, 'G')
                        if command_type == 0 or command_type == 1:  #Move command
                            x = self.getCodeFloat(line, 'X')
                            y = self.getCodeFloat(line, 'Y')
                            z = self.getCodeFloat(line, 'Z')
                            if z:
                                current_z = z
                            if x and y:
                                polygon_data = numpy.zeros((4, 3))  #Square :)
                                polygon_data[0, :] = old_position
                                polygon_data[1, :] = old_position
                                polygon_data[2, :] = [x, current_z, y]
                                polygon_data[3, :] = [x, current_z, y]
                                old_position = [x, current_z, y]
                                if current_path_type == "SKIRT":
                                    layer_data.addPolygon(
                                        current_layer_nr, 5, polygon_data, 5)
                                elif current_path_type == "WALL-INNER":
                                    layer_data.addPolygon(
                                        current_layer_nr, 3, polygon_data, 5)
                                elif current_path_type == "WALL-OUTER":
                                    layer_data.addPolygon(
                                        current_layer_nr, 1, polygon_data, 5)
                                else:
                                    layer_data.addPolygon(
                                        current_layer_nr, 2, polygon_data, 5)
                            #e = self.getCodeFloat(line, 'E')
                            #print(x , " ", y , " ", z, " " , e)
                        pass
            layer_data.build()
            decorator = LayerDataDecorator()
            decorator.setLayerData(layer_data)
            new_node = SceneNode()
            new_node.setMeshData(MeshData())
            new_node.addDecorator(decorator)
            new_node.setParent(self._scene.getRoot())
def test_SceneNodeDecorator():
    test_node = SceneNode()
    test_decorator = SceneNodeDecorator()
    amazing_decorator = TheAmazingTestDecorator()
    less_amazing_decorator = TheLessAmazingTestDecorator()
    not_amazing_decorator = TheNotSoAmazingTestDecorator()

    # Replace emit with mock object
    test_node.decoratorsChanged.emit = MagicMock()
    test_decorator.clear = MagicMock()

    # First actual change
    test_node.addDecorator(test_decorator)
    assert len(test_node.getDecorators()) == 1
    assert test_node.decoratorsChanged.emit.call_count == 1

    # Adding a decorator of the same type (SceneNodeDecorator) again should not do anything.
    test_node.addDecorator(test_decorator)
    assert len(test_node.getDecorators()) == 1
    assert test_node.decoratorsChanged.emit.call_count == 1
    assert test_node.getDecorator(type(test_decorator)) == test_decorator

    # Remove the decorator again!
    test_node.removeDecorator(SceneNodeDecorator)
    assert len(test_node.getDecorators()) == 0
    assert test_node.decoratorsChanged.emit.call_count == 2
    assert test_decorator.clear.call_count == 1  # Ensure that the clear of the test decorator is called.

    # Add a number of decorators!
    test_node.addDecorator(amazing_decorator)
    test_node.addDecorator(less_amazing_decorator)
    test_node.addDecorator(not_amazing_decorator)
    assert test_node.decoratorsChanged.emit.call_count == 5
    assert len(test_node.getDecorators()) == 3

    assert test_node.hasDecoration("zomg") == False
    assert test_node.hasDecoration("theOkayDecoration")
    assert test_node.hasDecoration("theAmazingDecoration")

    # Calling the decorations with args / kwargs
    assert test_node.callDecoration("theOkayDecoration", None) is None
    assert test_node.callDecoration("theEvenMoreAmazingDecoration", "beep") == ("beep", "Wow", "so wow")
    assert test_node.callDecoration("theEvenMoreAmazingDecoration", "beep", much_test = "Wow") == ("beep", "Wow", "Wow")

    # Calling decoration that is "double"
    assert test_node.callDecoration("theAmazingDecoration") == "Amazing!"
    test_node.removeDecorator(TheAmazingTestDecorator)
    assert test_node.callDecoration("theAmazingDecoration") == "amazing"

    not_amazing_decorator.clear = MagicMock()
    test_node.removeDecorators()
    # Also assure that removing all decorators also triggers the clear
    assert not_amazing_decorator.clear.call_count == 1
    assert len(test_node.getDecorators()) == 0
    assert test_node.decoratorsChanged.emit.call_count == 7
    assert test_node.getDecorator(type(test_decorator)) is None
Beispiel #8
0
def test_getConvexHullPrintingMesh(convex_hull_decorator):
    node = SceneNode()
    node.addDecorator(PrintingDecorator())
    with patch("UM.Application.Application.getInstance",
               MagicMock(return_value=mocked_application)):
        convex_hull_decorator.setNode(node)
    convex_hull_decorator._compute2DConvexHull = MagicMock(
        return_value=Polygon.approximatedCircle(10))
    assert convex_hull_decorator.getConvexHull() == Polygon.approximatedCircle(
        10)
Beispiel #9
0
    def read(self, file_name):
        extension = os.path.splitext(file_name)[1]
        if extension.lower() == self._supported_extension:
            layer_data = LayerData()
            with open (file_name,"rt") as f:
                layer = ""
                current_path_type = ""

                current_layer_nr = 0
                poly_list = []
                old_position = [0,0,0]
                current_z = 0
                for line in f:
                    if line.startswith(';TYPE:'):
                        current_path_type = line[6:].strip()
                        #layer_data.addPolygon(current_layer_nr,3 ,None ,5 )
                    elif line.startswith(';LAYER:'):
                        current_layer_nr = int(line[7:].strip())
                        layer_data.addLayer(int(line[7:].strip()))
                    elif line.startswith(';'):
                        pass # Ignore comments
                    else:
                        command_type = self.getCodeInt(line, 'G')
                        if command_type == 0 or command_type == 1: #Move command
                            x = self.getCodeFloat(line, 'X')
                            y = self.getCodeFloat(line, 'Y')
                            z = self.getCodeFloat(line, 'Z')
                            if z:
                                current_z = z
                            if x and y:
                                polygon_data = numpy.zeros((4,3)) #Square :)
                                polygon_data[0,:] = old_position
                                polygon_data[1,:] = old_position
                                polygon_data[2,:] = [x,current_z,y]
                                polygon_data[3,:] = [x,current_z,y]
                                old_position = [x,current_z,y]
                                if current_path_type == "SKIRT":
                                    layer_data.addPolygon(current_layer_nr,5 ,polygon_data ,5 )
                                elif current_path_type == "WALL-INNER":
                                    layer_data.addPolygon(current_layer_nr,3 ,polygon_data ,5 )
                                elif current_path_type == "WALL-OUTER":
                                    layer_data.addPolygon(current_layer_nr,1 ,polygon_data ,5 )
                                else:
                                    layer_data.addPolygon(current_layer_nr,2 ,polygon_data ,5 )
                            #e = self.getCodeFloat(line, 'E')
                            #print(x , " ", y , " ", z, " " , e)
                        pass
            layer_data.build()
            decorator = LayerDataDecorator()
            decorator.setLayerData(layer_data)
            new_node = SceneNode()
            new_node.setMeshData(MeshData())
            new_node.addDecorator(decorator)
            new_node.setParent(self._scene.getRoot())
def test_getConvexHulLBoundaryPrintingMeshOneAtATime(convex_hull_decorator):
    node = SceneNode()
    node.addDecorator(PrintingDecorator())
    with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)):
        convex_hull_decorator.setNode(node)
    convex_hull_decorator._global_stack = MagicMock()
    convex_hull_decorator._global_stack.getProperty = MagicMock(return_value = "one_at_a_time")
    # In this test we don't care for the result of the function, just that the convex hull computation is called.
    convex_hull_decorator._compute2DConvexHull = MagicMock()
    convex_hull_decorator.getConvexHullBoundary()
    convex_hull_decorator._compute2DConvexHull.assert_called_once_with()
Beispiel #11
0
    def groupSelected(self):
        group_node = SceneNode()
        group_decorator = GroupDecorator()
        group_node.addDecorator(group_decorator)
        group_node.setParent(self.getController().getScene().getRoot())

        for node in Selection.getAllSelectedObjects():
            node.setParent(group_node)

        for node in group_node.getChildren():
            Selection.remove(node)

        Selection.add(group_node)
Beispiel #12
0
 def groupSelected(self):
     group_node = SceneNode()
     group_decorator = GroupDecorator()
     group_node.addDecorator(group_decorator)
     group_node.setParent(self.getController().getScene().getRoot())
     
     for node in Selection.getAllSelectedObjects():
         node.setParent(group_node)
     
     for node in group_node.getChildren():
         Selection.remove(node)
     
     Selection.add(group_node)
Beispiel #13
0
def test_getConvexHulLBoundaryPrintingMeshOneAtATime(convex_hull_decorator):
    node = SceneNode()
    node.addDecorator(PrintingDecorator())
    with patch("UM.Application.Application.getInstance",
               MagicMock(return_value=mocked_application)):
        convex_hull_decorator.setNode(node)
    convex_hull_decorator._global_stack = MagicMock()
    convex_hull_decorator._global_stack.getProperty = MagicMock(
        return_value="one_at_a_time")
    # In this test we don't care for the result of the function, just that the convex hull computation is called.
    convex_hull_decorator._compute2DConvexHull = MagicMock()
    convex_hull_decorator.getConvexHullBoundary()
    convex_hull_decorator._compute2DConvexHull.assert_called_once_with()
Beispiel #14
0
    def test_deepCopy(self):
        node_1 = SceneNode()
        node_2 = SceneNode()
        node_1.translate(Vector(1, 2, 3))
        node_1.scale(Vector(1.5, 1., 1.))
        node_1.setMeshData(MeshData())
        node_1.addChild(node_2)
        node_1.addDecorator(GroupDecorator())
        copied_node = deepcopy(node_1)

        assert copied_node.getScale() == Vector(1.5, 1, 1)
        assert copied_node.getPosition() == Vector(1, 2, 3)
        assert len(copied_node.getChildren()) == 1
        # Ensure that the decorator also got copied
        assert copied_node.callDecoration("isGroup")
Beispiel #15
0
 def groupSelected(self):
     group_node = SceneNode()
     group_decorator = GroupDecorator()
     group_node.addDecorator(group_decorator)
     group_node.setParent(self.getController().getScene().getRoot())
     
     for node in Selection.getAllSelectedObjects():
         node.setParent(group_node)
     group_node.setCenterPosition(group_node.getBoundingBox().center)
     #group_node.translate(Vector(0,group_node.getBoundingBox().center.y,0))
     group_node.translate(group_node.getBoundingBox().center)
     for node in group_node.getChildren():
         Selection.remove(node)
     
     Selection.add(group_node)
Beispiel #16
0
    def groupSelected(self):
        group_node = SceneNode()
        group_decorator = GroupDecorator()
        group_node.addDecorator(group_decorator)
        group_node.setParent(self.getController().getScene().getRoot())

        for node in Selection.getAllSelectedObjects():
            node.setParent(group_node)
        group_node.setCenterPosition(group_node.getBoundingBox().center)
        #group_node.translate(Vector(0,group_node.getBoundingBox().center.y,0))
        group_node.translate(group_node.getBoundingBox().center)
        for node in group_node.getChildren():
            Selection.remove(node)

        Selection.add(group_node)
Beispiel #17
0
    def test_deepCopy(self):
        node_1 = SceneNode()
        node_2 = SceneNode()
        node_1.translate(Vector(1, 2, 3))
        node_1.scale(Vector(1.5, 1., 1.))
        node_1.setMeshData(MeshData())
        node_1.addChild(node_2)
        node_1.addDecorator(GroupDecorator())
        copied_node = deepcopy(node_1)

        assert copied_node.getScale() == Vector(1.5, 1, 1)
        assert copied_node.getPosition() == Vector(1, 2, 3)
        assert len(copied_node.getChildren()) == 1
        # Ensure that the decorator also got copied
        assert copied_node.callDecoration("isGroup")
Beispiel #18
0
    def groupSelected(self):
        group_node = SceneNode()
        group_decorator = GroupDecorator()
        group_node.addDecorator(group_decorator)
        group_node.setParent(self.getController().getScene().getRoot())
        center = Selection.getSelectionCenter()
        group_node.setPosition(center)
        group_node.setCenterPosition(center)

        for node in Selection.getAllSelectedObjects():
            world = node.getWorldPosition()
            node.setParent(group_node)
            node.setPosition(world - center)

        for node in group_node.getChildren():
            Selection.remove(node)

        Selection.add(group_node)
Beispiel #19
0
    def groupSelected(self):
        group_node = SceneNode()
        group_decorator = GroupDecorator()
        group_node.addDecorator(group_decorator)
        group_node.setParent(self.getController().getScene().getRoot())
        center = Selection.getSelectionCenter()
        group_node.setPosition(center)
        group_node.setCenterPosition(center)

        for node in Selection.getAllSelectedObjects():
            world = node.getWorldPosition()
            node.setParent(group_node)
            node.setPosition(world - center)

        for node in group_node.getChildren():
            Selection.remove(node)

        Selection.add(group_node)
Beispiel #20
0
    def groupSelected(self):
        # Create a group-node
        group_node = SceneNode()
        group_decorator = GroupDecorator()
        group_node.addDecorator(group_decorator)
        group_node.setParent(self.getController().getScene().getRoot())
        group_node.setSelectable(True)
        center = Selection.getSelectionCenter()
        group_node.setPosition(center)
        group_node.setCenterPosition(center)

        # Move selected nodes into the group-node
        Selection.applyOperation(SetParentOperation, group_node)

        # Deselect individual nodes and select the group-node instead
        for node in group_node.getChildren():
            Selection.remove(node)
        Selection.add(group_node)
Beispiel #21
0
    def groupSelected(self):
        # Create a group-node
        group_node = SceneNode()
        group_decorator = GroupDecorator()
        group_node.addDecorator(group_decorator)
        group_node.setParent(self.getController().getScene().getRoot())
        group_node.setSelectable(True)
        center = Selection.getSelectionCenter()
        group_node.setPosition(center)
        group_node.setCenterPosition(center)

        # Move selected nodes into the group-node
        Selection.applyOperation(SetParentOperation, group_node)

        # Deselect individual nodes and select the group-node instead
        for node in group_node.getChildren():
            Selection.remove(node)
        Selection.add(group_node)
def test_compute2DConvexHullMeshDataGrouped(convex_hull_decorator):
    parent_node = SceneNode()
    parent_node.addDecorator(GroupDecorator())
    node = SceneNode()
    parent_node.addChild(node)

    mb = MeshBuilder()
    mb.addCube(10, 10, 10)
    node.setMeshData(mb.build())

    convex_hull_decorator._getSettingProperty = MagicMock(return_value=0)

    with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)):
        convex_hull_decorator.setNode(parent_node)
        with patch("cura.Settings.ExtruderManager.ExtruderManager.getInstance"):
            copied_decorator = copy.deepcopy(convex_hull_decorator)
            copied_decorator._getSettingProperty = MagicMock(return_value=0)
        node.addDecorator(copied_decorator)
    assert convex_hull_decorator._compute2DConvexHull() == Polygon([[-5.0,5.0], [5.0,5.0], [5.0,-5.0], [-5.0,-5.0]])
Beispiel #23
0
def test_compute2DConvexHullMeshDataGrouped(convex_hull_decorator):
    parent_node = SceneNode()
    parent_node.addDecorator(GroupDecorator())
    node = SceneNode()
    parent_node.addChild(node)

    mb = MeshBuilder()
    mb.addCube(10, 10, 10)
    node.setMeshData(mb.build())

    convex_hull_decorator._getSettingProperty = MagicMock(return_value=0)

    with patch("UM.Application.Application.getInstance",
               MagicMock(return_value=mocked_application)):
        convex_hull_decorator.setNode(parent_node)
        with patch(
                "cura.Settings.ExtruderManager.ExtruderManager.getInstance"):
            copied_decorator = copy.deepcopy(convex_hull_decorator)
            copied_decorator._getSettingProperty = MagicMock(return_value=0)
        node.addDecorator(copied_decorator)
    assert convex_hull_decorator._compute2DConvexHull() == Polygon(
        [[-5.0, 5.0], [5.0, 5.0], [5.0, -5.0], [-5.0, -5.0]])
Beispiel #24
0
def group_scene_node():
    node = SceneNode()
    node.addDecorator(GroupDecorator())
    return node
Beispiel #25
0
    def read(self, file_name):
        Logger.log("d", "Preparing to load %s" % file_name)
        self._cancelled = False

        scene_node = SceneNode()
        # Override getBoundingBox function of the sceneNode, as this node should return a bounding box, but there is no
        # real data to calculate it from.
        scene_node.getBoundingBox = self._getNullBoundingBox

        gcode_list = []
        self._is_layers_in_file = False

        Logger.log("d", "Opening file %s" % file_name)

        self._extruder_offsets = self._extruderOffsets()  # dict with index the extruder number. can be empty

        last_z = 0
        with open(file_name, "r") as file:
            file_lines = 0
            current_line = 0
            for line in file:
                file_lines += 1
                gcode_list.append(line)
                if not self._is_layers_in_file and line[:len(self._layer_keyword)] == self._layer_keyword:
                    self._is_layers_in_file = True
            file.seek(0)

            file_step = max(math.floor(file_lines / 100), 1)

            self._clearValues()

            self._message = Message(catalog.i18nc("@info:status", "Parsing G-code"), lifetime=0)
            self._message.setProgress(0)
            self._message.show()

            Logger.log("d", "Parsing %s..." % file_name)

            current_position = self._position(0, 0, 0, [0])
            current_path = []

            for line in file:
                if self._cancelled:
                    Logger.log("d", "Parsing %s cancelled" % file_name)
                    return None
                current_line += 1
                last_z = current_position.z

                if current_line % file_step == 0:
                    self._message.setProgress(math.floor(current_line / file_lines * 100))
                    Job.yieldThread()
                if len(line) == 0:
                    continue

                if line.find(self._type_keyword) == 0:
                    type = line[len(self._type_keyword):].strip()
                    if type == "WALL-INNER":
                        self._layer_type = LayerPolygon.InsetXType
                    elif type == "WALL-OUTER":
                        self._layer_type = LayerPolygon.Inset0Type
                    elif type == "SKIN":
                        self._layer_type = LayerPolygon.SkinType
                    elif type == "SKIRT":
                        self._layer_type = LayerPolygon.SkirtType
                    elif type == "SUPPORT":
                        self._layer_type = LayerPolygon.SupportType
                    elif type == "FILL":
                        self._layer_type = LayerPolygon.InfillType
                    else:
                        Logger.log("w", "Encountered a unknown type (%s) while parsing g-code.", type)

                if self._is_layers_in_file and line[:len(self._layer_keyword)] == self._layer_keyword:
                    try:
                        layer_number = int(line[len(self._layer_keyword):])
                        self._createPolygon(self._current_layer_thickness, current_path, self._extruder_offsets.get(self._extruder_number, [0, 0]))
                        current_path.clear()
                        self._layer_number = layer_number
                    except:
                        pass

                # This line is a comment. Ignore it (except for the layer_keyword)
                if line.startswith(";"):
                    continue

                G = self._getInt(line, "G")
                if G is not None:
                    current_position = self._processGCode(G, line, current_position, current_path)

                    # < 2 is a heuristic for a movement only, that should not be counted as a layer
                    if current_position.z > last_z and abs(current_position.z - last_z) < 2:
                        if self._createPolygon(self._current_layer_thickness, current_path, self._extruder_offsets.get(self._extruder_number, [0, 0])):
                            current_path.clear()
                            if not self._is_layers_in_file:
                                self._layer_number += 1

                    continue

                if line.startswith("T"):
                    T = self._getInt(line, "T")
                    if T is not None:
                        self._createPolygon(self._current_layer_thickness, current_path, self._extruder_offsets.get(self._extruder_number, [0, 0]))
                        current_path.clear()

                        current_position = self._processTCode(T, line, current_position, current_path)

            # "Flush" leftovers
            if not self._is_layers_in_file and len(current_path) > 1:
                if self._createPolygon(self._current_layer_thickness, current_path, self._extruder_offsets.get(self._extruder_number, [0, 0])):
                    self._layer_number += 1
                    current_path.clear()

        material_color_map = numpy.zeros((10, 4), dtype = numpy.float32)
        material_color_map[0, :] = [0.0, 0.7, 0.9, 1.0]
        material_color_map[1, :] = [0.7, 0.9, 0.0, 1.0]
        layer_mesh = self._layer_data_builder.build(material_color_map)
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_mesh)
        scene_node.addDecorator(decorator)

        gcode_list_decorator = GCodeListDecorator()
        gcode_list_decorator.setGCodeList(gcode_list)
        scene_node.addDecorator(gcode_list_decorator)

        Application.getInstance().getController().getScene().gcode_list = gcode_list

        Logger.log("d", "Finished parsing %s" % file_name)
        self._message.hide()

        if self._layer_number == 0:
            Logger.log("w", "File %s doesn't contain any valid layers" % file_name)

        settings = Application.getInstance().getGlobalContainerStack()
        machine_width = settings.getProperty("machine_width", "value")
        machine_depth = settings.getProperty("machine_depth", "value")

        if not self._center_is_zero:
            scene_node.setPosition(Vector(-machine_width / 2, 0, machine_depth / 2))

        Logger.log("d", "Loaded %s" % file_name)

        if Preferences.getInstance().getValue("gcodereader/show_caution"):
            caution_message = Message(catalog.i18nc(
                "@info:generic",
                "Make sure the g-code is suitable for your printer and printer configuration before sending the file to it. The g-code representation may not be accurate."), lifetime=0)
            caution_message.show()

        # The "save/print" button's state is bound to the backend state.
        backend = Application.getInstance().getBackend()
        backend.backendStateChange.emit(Backend.BackendState.Disabled)

        return scene_node
Beispiel #26
0
    def read(self, file_name):
        result = None
        extension = os.path.splitext(file_name)[1]
        if extension.lower() == self._supported_extension:
            result = SceneNode()
            # The base object of 3mf is a zipped archive.
            archive = zipfile.ZipFile(file_name, "r")
            try:
                root = ET.parse(archive.open("3D/3dmodel.model"))

                # There can be multiple objects, try to load all of them.
                objects = root.findall("./3mf:resources/3mf:object",
                                       self._namespaces)
                if len(objects) == 0:
                    Logger.log(
                        "w",
                        "No objects found in 3MF file %s, either the file is corrupt or you are using an outdated format",
                        file_name)
                    return None

                for object in objects:
                    mesh = MeshData()
                    node = SceneNode()
                    vertex_list = []
                    #for vertex in object.mesh.vertices.vertex:
                    for vertex in object.findall(".//3mf:vertex",
                                                 self._namespaces):
                        vertex_list.append([
                            vertex.get("x"),
                            vertex.get("y"),
                            vertex.get("z")
                        ])
                        Job.yieldThread()

                    triangles = object.findall(".//3mf:triangle",
                                               self._namespaces)

                    mesh.reserveFaceCount(len(triangles))

                    #for triangle in object.mesh.triangles.triangle:
                    for triangle in triangles:
                        v1 = int(triangle.get("v1"))
                        v2 = int(triangle.get("v2"))
                        v3 = int(triangle.get("v3"))
                        mesh.addFace(vertex_list[v1][0], vertex_list[v1][1],
                                     vertex_list[v1][2], vertex_list[v2][0],
                                     vertex_list[v2][1], vertex_list[v2][2],
                                     vertex_list[v3][0], vertex_list[v3][1],
                                     vertex_list[v3][2])
                        Job.yieldThread()

                    #TODO: We currently do not check for normals and simply recalculate them.
                    mesh.calculateNormals()
                    node.setMeshData(mesh)
                    node.setSelectable(True)

                    transformation = root.findall(
                        "./3mf:build/3mf:item[@objectid='{0}']".format(
                            object.get("id")), self._namespaces)
                    if transformation:
                        transformation = transformation[0]

                    if transformation.get("transform"):
                        splitted_transformation = transformation.get(
                            "transform").split()
                        ## Transformation is saved as:
                        ## M00 M01 M02 0.0
                        ## M10 M11 M12 0.0
                        ## M20 M21 M22 0.0
                        ## M30 M31 M32 1.0
                        ## We switch the row & cols as that is how everyone else uses matrices!
                        temp_mat = Matrix()
                        # Rotation & Scale
                        temp_mat._data[0, 0] = splitted_transformation[0]
                        temp_mat._data[1, 0] = splitted_transformation[1]
                        temp_mat._data[2, 0] = splitted_transformation[2]
                        temp_mat._data[0, 1] = splitted_transformation[3]
                        temp_mat._data[1, 1] = splitted_transformation[4]
                        temp_mat._data[2, 1] = splitted_transformation[5]
                        temp_mat._data[0, 2] = splitted_transformation[6]
                        temp_mat._data[1, 2] = splitted_transformation[7]
                        temp_mat._data[2, 2] = splitted_transformation[8]

                        # Translation
                        temp_mat._data[0, 3] = splitted_transformation[9]
                        temp_mat._data[1, 3] = splitted_transformation[10]
                        temp_mat._data[2, 3] = splitted_transformation[11]

                        node.setPosition(
                            Vector(temp_mat.at(0, 3), temp_mat.at(1, 3),
                                   temp_mat.at(2, 3)))

                        temp_quaternion = Quaternion()
                        temp_quaternion.setByMatrix(temp_mat)
                        node.setOrientation(temp_quaternion)

                        # Magical scale extraction
                        scale = temp_mat.getTransposed().multiply(temp_mat)
                        scale_x = math.sqrt(scale.at(0, 0))
                        scale_y = math.sqrt(scale.at(1, 1))
                        scale_z = math.sqrt(scale.at(2, 2))
                        node.setScale(Vector(scale_x, scale_y, scale_z))

                        # We use a different coordinate frame, so rotate.
                        #rotation = Quaternion.fromAngleAxis(-0.5 * math.pi, Vector(1,0,0))
                        #node.rotate(rotation)
                    result.addChild(node)

                    Job.yieldThread()

                #If there is more then one object, group them.
                try:
                    if len(objects) > 1:
                        group_decorator = GroupDecorator()
                        result.addDecorator(group_decorator)
                except:
                    pass
            except Exception as e:
                Logger.log("e", "exception occured in 3mf reader: %s", e)
        return result
Beispiel #27
0
    def read(self, file_name):
        result = SceneNode()
        # The base object of 3mf is a zipped archive.
        archive = zipfile.ZipFile(file_name, "r")
        try:
            root = ET.parse(archive.open("3D/3dmodel.model"))

            # There can be multiple objects, try to load all of them.
            objects = root.findall("./3mf:resources/3mf:object", self._namespaces)
            if len(objects) == 0:
                Logger.log("w", "No objects found in 3MF file %s, either the file is corrupt or you are using an outdated format", file_name)
                return None

            for entry in objects:
                mesh_builder = MeshBuilder()
                node = SceneNode()
                vertex_list = []
                #for vertex in entry.mesh.vertices.vertex:
                for vertex in entry.findall(".//3mf:vertex", self._namespaces):
                    vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")])
                    Job.yieldThread()

                triangles = entry.findall(".//3mf:triangle", self._namespaces)
                mesh_builder.reserveFaceCount(len(triangles))

                #for triangle in object.mesh.triangles.triangle:
                for triangle in triangles:
                    v1 = int(triangle.get("v1"))
                    v2 = int(triangle.get("v2"))
                    v3 = int(triangle.get("v3"))

                    mesh_builder.addFaceByPoints(vertex_list[v1][0], vertex_list[v1][1], vertex_list[v1][2],
                                                 vertex_list[v2][0], vertex_list[v2][1], vertex_list[v2][2],
                                                 vertex_list[v3][0], vertex_list[v3][1], vertex_list[v3][2])

                    Job.yieldThread()

                # Rotate the model; We use a different coordinate frame.
                rotation = Matrix()
                rotation.setByRotationAxis(-0.5 * math.pi, Vector(1,0,0))

                #TODO: We currently do not check for normals and simply recalculate them.
                mesh_builder.calculateNormals()

                node.setMeshData(mesh_builder.build().getTransformed(rotation))
                node.setSelectable(True)

                transformations = root.findall("./3mf:build/3mf:item[@objectid='{0}']".format(entry.get("id")), self._namespaces)
                transformation = transformations[0] if transformations else None
                if transformation is not None and transformation.get("transform"):
                    splitted_transformation = transformation.get("transform").split()
                    ## Transformation is saved as:
                    ## M00 M01 M02 0.0
                    ## M10 M11 M12 0.0
                    ## M20 M21 M22 0.0
                    ## M30 M31 M32 1.0
                    ## We switch the row & cols as that is how everyone else uses matrices!
                    temp_mat = Matrix()
                    # Rotation & Scale
                    temp_mat._data[0,0] = splitted_transformation[0]
                    temp_mat._data[1,0] = splitted_transformation[1]
                    temp_mat._data[2,0] = splitted_transformation[2]
                    temp_mat._data[0,1] = splitted_transformation[3]
                    temp_mat._data[1,1] = splitted_transformation[4]
                    temp_mat._data[2,1] = splitted_transformation[5]
                    temp_mat._data[0,2] = splitted_transformation[6]
                    temp_mat._data[1,2] = splitted_transformation[7]
                    temp_mat._data[2,2] = splitted_transformation[8]

                    # Translation
                    temp_mat._data[0,3] = splitted_transformation[9]
                    temp_mat._data[1,3] = splitted_transformation[10]
                    temp_mat._data[2,3] = splitted_transformation[11]

                    node.setTransformation(temp_mat)

                result.addChild(node)

                Job.yieldThread()

            #If there is more then one object, group them.
            if len(objects) > 1:
                group_decorator = GroupDecorator()
                result.addDecorator(group_decorator)
        except Exception as e:
            Logger.log("e" ,"exception occured in 3mf reader: %s" , e)

        return result  
Beispiel #28
0
    def read(self, file_name):
        result = None
        extension = os.path.splitext(file_name)[1]
        if extension.lower() == self._supported_extension:
            result = SceneNode()
            # The base object of 3mf is a zipped archive.
            archive = zipfile.ZipFile(file_name, 'r')
            try:
                root = ET.parse(archive.open("3D/3dmodel.model"))

                # There can be multiple objects, try to load all of them.
                objects = root.findall("./3mf:resources/3mf:object", self._namespaces)
                for object in objects:
                    mesh = MeshData()
                    node = SceneNode()
                    vertex_list = []
                    #for vertex in object.mesh.vertices.vertex:
                    for vertex in object.findall(".//3mf:vertex", self._namespaces):
                        vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")])

                    triangles = object.findall(".//3mf:triangle", self._namespaces)

                    mesh.reserveFaceCount(len(triangles))
                    
                    #for triangle in object.mesh.triangles.triangle:
                    for triangle in triangles:
                        v1 = int(triangle.get("v1"))
                        v2 = int(triangle.get("v2"))
                        v3 = int(triangle.get("v3"))
                        mesh.addFace(vertex_list[v1][0],vertex_list[v1][1],vertex_list[v1][2],vertex_list[v2][0],vertex_list[v2][1],vertex_list[v2][2],vertex_list[v3][0],vertex_list[v3][1],vertex_list[v3][2])
                    #TODO: We currently do not check for normals and simply recalculate them. 
                    mesh.calculateNormals()
                    node.setMeshData(mesh)
                    node.setSelectable(True)
                    
                    transformation = root.findall("./3mf:build/3mf:item[@objectid='{0}']".format(object.get("id")), self._namespaces)
                    if transformation:
                        transformation = transformation[0]

                    if transformation.get("transform"):
                        splitted_transformation = transformation.get("transform").split()
                        ## Transformation is saved as:
                        ## M00 M01 M02 0.0
                        ## M10 M11 M12 0.0
                        ## M20 M21 M22 0.0
                        ## M30 M31 M32 1.0
                        ## We switch the row & cols as that is how everyone else uses matrices!
                        temp_mat = Matrix()
                        # Rotation & Scale
                        temp_mat._data[0,0] = splitted_transformation[0]
                        temp_mat._data[1,0] = splitted_transformation[1]
                        temp_mat._data[2,0] = splitted_transformation[2]
                        temp_mat._data[0,1] = splitted_transformation[3]
                        temp_mat._data[1,1] = splitted_transformation[4]
                        temp_mat._data[2,1] = splitted_transformation[5]
                        temp_mat._data[0,2] = splitted_transformation[6]
                        temp_mat._data[1,2] = splitted_transformation[7]
                        temp_mat._data[2,2] = splitted_transformation[8]
                        
                        # Translation
                        temp_mat._data[0,3] = splitted_transformation[9]
                        temp_mat._data[1,3] = splitted_transformation[10]
                        temp_mat._data[2,3] = splitted_transformation[11]
                        
                        node.setPosition(Vector(temp_mat.at(0,3), temp_mat.at(1,3), temp_mat.at(2,3)))
                        
                        temp_quaternion = Quaternion()
                        temp_quaternion.setByMatrix(temp_mat)
                        node.setOrientation(temp_quaternion)
                        
                        # Magical scale extraction
                        S2 = temp_mat.getTransposed().multiply(temp_mat)
                        scale_x = math.sqrt(S2.at(0,0))
                        scale_y = math.sqrt(S2.at(1,1))
                        scale_z = math.sqrt(S2.at(2,2))
                        node.setScale(Vector(scale_x,scale_y,scale_z))
                        
                        # We use a different coordinate frame, so rotate.
                        rotation = Quaternion.fromAngleAxis(-0.5 * math.pi, Vector(1,0,0))
                        node.rotate(rotation)
                    result.addChild(node)

                #If there is more then one object, group them.
                try:
                    if len(objects) > 1:
                        group_decorator = GroupDecorator()
                        result.addDecorator(group_decorator)
                except:
                    pass
            except Exception as e:
                Logger.log("e" ,"exception occured in 3mf reader: %s" , e)      
        return result  
    def run(self):
        if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
            self._progress = Message(catalog.i18nc("Layers View mode", "Layers"), 0, False, 0)
            self._progress.show()

        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)

        objectIdMap = {}
        new_node = SceneNode()
        ## Put all nodes in a dict identified by ID
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is SceneNode and node.getMeshData():
                if node.callDecoration("getLayerData"):
                #if hasattr(node.getMeshData(), "layerData"):
                    self._scene.getRoot().removeChild(node)
                else:
                    objectIdMap[id(node)] = node

        settings = Application.getInstance().getActiveMachine()
        layerHeight = settings.getSettingValueByKey("layer_height")

        center = None
        if not settings.getSettingValueByKey("machine_center_is_zero"):
            center = numpy.array([settings.getSettingValueByKey("machine_width") / 2, 0.0, -settings.getSettingValueByKey("machine_depth") / 2])
        else:
            center = numpy.array([0.0, 0.0, 0.0])

        if self._progress:
            self._progress.setProgress(2)

        mesh = MeshData()
        layer_data = LayerData.LayerData()
        for object in self._message.objects:
            try:
                node = objectIdMap[object.id]
            except KeyError:
                continue

            for layer in object.layers:
                layer_data.addLayer(layer.id)
                layer_data.setLayerHeight(layer.id, layer.height)
                layer_data.setLayerThickness(layer.id, layer.thickness)
                for polygon in layer.polygons:
                    points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array
                    points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
                    points = numpy.asarray(points, dtype=numpy.float32)
                    points /= 1000
                    points = numpy.insert(points, 1, (layer.height / 1000), axis = 1)

                    points[:,2] *= -1

                    points -= numpy.array(center)

                    layer_data.addPolygon(layer.id, polygon.type, points, polygon.line_width)

        if self._progress:
            self._progress.setProgress(50)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data
        layer_data.build()

        if self._progress:
            self._progress.setProgress(100)
        
        #Add layerdata decorator to scene node to indicate that the node has layerdata
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_data)
        new_node.addDecorator(decorator)
        
        new_node.setMeshData(mesh)
        new_node.setParent(self._scene.getRoot())
        
        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress:
            self._progress.hide()
Beispiel #30
0
    def run(self):
        if Application.getInstance().getController().getActiveView(
        ).getPluginId() == "LayerView":
            self._progress = Message(
                catalog.i18nc("@info:status", "Processing Layers"), 0, False,
                -1)
            self._progress.show()
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        Application.getInstance().getController().activeViewChanged.connect(
            self._onActiveViewChanged)

        object_id_map = {}
        new_node = SceneNode()

        ## Remove old layer data (if any)
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is SceneNode and node.getMeshData():
                if node.callDecoration("getLayerData"):
                    self._scene.getRoot().removeChild(node)
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        settings = Application.getInstance().getMachineManager(
        ).getWorkingProfile()

        mesh = MeshData()
        layer_data = LayerData.LayerData()
        layer_count = len(self._layers)

        current_layer = 0

        for layer in self._layers:
            layer_data.addLayer(layer.id)
            layer_data.setLayerHeight(layer.id, layer.height)
            layer_data.setLayerThickness(layer.id, layer.thickness)

            for p in range(layer.repeatedMessageCount("polygons")):
                polygon = layer.getRepeatedMessage("polygons", p)

                points = numpy.fromstring(
                    polygon.points,
                    dtype="i8")  # Convert bytearray to numpy array
                points = points.reshape(
                    (-1, 2)
                )  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.

                # Create a new 3D-array, copy the 2D points over and insert the right height.
                # This uses manual array creation + copy rather than numpy.insert since this is
                # faster.
                new_points = numpy.empty((len(points), 3), numpy.float32)
                new_points[:, 0] = points[:, 0]
                new_points[:, 1] = layer.height
                new_points[:, 2] = -points[:, 1]

                new_points /= 1000

                layer_data.addPolygon(layer.id, polygon.type, new_points,
                                      polygon.line_width)
                Job.yieldThread()
            Job.yieldThread()
            current_layer += 1
            progress = (current_layer / layer_count) * 100
            # TODO: Rebuild the layer data mesh once the layer has been processed.
            # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return
            if self._progress:
                self._progress.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data
        layer_data.build()

        if self._abort_requested:
            if self._progress:
                self._progress.hide()
            return

        #Add layerdata decorator to scene node to indicate that the node has layerdata
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_data)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        new_node.setParent(
            self._scene.getRoot())  #Note: After this we can no longer abort!

        if not settings.getSettingValue("machine_center_is_zero"):
            new_node.setPosition(
                Vector(-settings.getSettingValue("machine_width") / 2, 0.0,
                       settings.getSettingValue("machine_depth") / 2))

        if self._progress:
            self._progress.setProgress(100)

        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress:
            self._progress.hide()
Beispiel #31
0
    def run(self):
        if Application.getInstance().getController().getActiveView(
        ).getPluginId() == "LayerView":
            self._progress = Message(
                catalog.i18nc("@info:status", "Processing Layers"), 0, False,
                -1)
            self._progress.show()
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        Application.getInstance().getController().activeViewChanged.connect(
            self._onActiveViewChanged)

        new_node = SceneNode()

        ## Remove old layer data (if any)
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is SceneNode and node.getMeshData():
                if node.callDecoration("getLayerData"):
                    self._scene.getRoot().removeChild(node)
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        mesh = MeshData()
        layer_data = LayerData.LayerData()
        layer_count = len(self._layers)

        # Find the minimum layer number
        # When using a raft, the raft layers are sent as layers < 0. Instead of allowing layers < 0, we
        # instead simply offset all other layers so the lowest layer is always 0.
        min_layer_number = 0
        for layer in self._layers:
            if (layer.id < min_layer_number):
                min_layer_number = layer.id

        current_layer = 0

        for layer in self._layers:
            abs_layer_number = layer.id + abs(min_layer_number)

            layer_data.addLayer(abs_layer_number)
            layer_data.setLayerHeight(abs_layer_number, layer.height)
            layer_data.setLayerThickness(abs_layer_number, layer.thickness)

            for p in range(layer.repeatedMessageCount("polygons")):
                polygon = layer.getRepeatedMessage("polygons", p)

                points = numpy.fromstring(
                    polygon.points,
                    dtype="i8")  # Convert bytearray to numpy array
                points = points.reshape(
                    (-1, 2)
                )  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.

                # Create a new 3D-array, copy the 2D points over and insert the right height.
                # This uses manual array creation + copy rather than numpy.insert since this is
                # faster.
                new_points = numpy.empty((len(points), 3), numpy.float32)
                new_points[:, 0] = points[:, 0]
                new_points[:, 1] = layer.height
                new_points[:, 2] = -points[:, 1]

                new_points /= 1000

                layer_data.addPolygon(abs_layer_number, polygon.type,
                                      new_points, polygon.line_width)
                Job.yieldThread()
            Job.yieldThread()
            current_layer += 1
            progress = (current_layer / layer_count) * 100
            # TODO: Rebuild the layer data mesh once the layer has been processed.
            # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return
            if self._progress:
                self._progress.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data
        layer_data.build()

        if self._abort_requested:
            if self._progress:
                self._progress.hide()
            return

        # Add LayerDataDecorator to scene node to indicate that the node has layer data
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_data)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        new_node.setParent(
            self._scene.getRoot())  # Note: After this we can no longer abort!

        settings = Application.getInstance().getGlobalContainerStack()
        if not settings.getProperty("machine_center_is_zero", "value"):
            new_node.setPosition(
                Vector(-settings.getProperty("machine_width", "value") / 2,
                       0.0,
                       settings.getProperty("machine_depth", "value") / 2))

        if self._progress:
            self._progress.setProgress(100)

        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress:
            self._progress.hide()

        # Clear the unparsed layers. This saves us a bunch of memory if the Job does not get destroyed.
        self._layers = None
Beispiel #32
0
    def run(self):
        if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
            self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
            self._progress.show()
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)

        new_node = SceneNode()

        ## Remove old layer data (if any)
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is SceneNode and node.getMeshData():
                if node.callDecoration("getLayerData"):
                    self._scene.getRoot().removeChild(node)
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        settings = Application.getInstance().getMachineManager().getWorkingProfile()

        mesh = MeshData()
        layer_data = LayerData.LayerData()
        layer_count = len(self._layers)

        # Find the minimum layer number
        # When using a raft, the raft layers are sent as layers < 0. Instead of allowing layers < 0, we
        # instead simply offset all other layers so the lowest layer is always 0.
        min_layer_number = 0
        for layer in self._layers:
            if(layer.id < min_layer_number):
                min_layer_number = layer.id

        current_layer = 0

        for layer in self._layers:
            abs_layer_number = layer.id + abs(min_layer_number)

            layer_data.addLayer(abs_layer_number)
            layer_data.setLayerHeight(abs_layer_number, layer.height)
            layer_data.setLayerThickness(abs_layer_number, layer.thickness)

            for p in range(layer.repeatedMessageCount("polygons")):
                polygon = layer.getRepeatedMessage("polygons", p)

                points = numpy.fromstring(polygon.points, dtype="i8")  # Convert bytearray to numpy array
                points = points.reshape((-1,2))  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.

                # Create a new 3D-array, copy the 2D points over and insert the right height.
                # This uses manual array creation + copy rather than numpy.insert since this is
                # faster.
                new_points = numpy.empty((len(points), 3), numpy.float32)
                new_points[:,0] = points[:,0]
                new_points[:,1] = layer.height
                new_points[:,2] = -points[:,1]

                new_points /= 1000

                layer_data.addPolygon(abs_layer_number, polygon.type, new_points, polygon.line_width)
                Job.yieldThread()
            Job.yieldThread()
            current_layer += 1
            progress = (current_layer / layer_count) * 100
            # TODO: Rebuild the layer data mesh once the layer has been processed.
            # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return
            if self._progress:
                self._progress.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data
        layer_data.build()

        if self._abort_requested:
            if self._progress:
                self._progress.hide()
            return

        # Add LayerDataDecorator to scene node to indicate that the node has layer data
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_data)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        new_node.setParent(self._scene.getRoot())  # Note: After this we can no longer abort!

        if not settings.getSettingValue("machine_center_is_zero"):
            new_node.setPosition(Vector(-settings.getSettingValue("machine_width") / 2, 0.0, settings.getSettingValue("machine_depth") / 2))

        if self._progress:
            self._progress.setProgress(100)

        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress:
            self._progress.hide()

        # Clear the unparsed layers. This saves us a bunch of memory if the Job does not get destroyed.
        self._layers = None
Beispiel #33
0
    def run(self):
        start_time = time()
        if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
            self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
            self._progress.show()
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)

        new_node = SceneNode()

        ## Remove old layer data (if any)
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is SceneNode and node.getMeshData():
                if node.callDecoration("getLayerData"):
                    self._scene.getRoot().removeChild(node)
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        mesh = MeshData()
        layer_data = LayerDataBuilder.LayerDataBuilder()
        layer_count = len(self._layers)

        # Find the minimum layer number
        # When using a raft, the raft layers are sent as layers < 0. Instead of allowing layers < 0, we
        # instead simply offset all other layers so the lowest layer is always 0.
        min_layer_number = 0
        for layer in self._layers:
            if(layer.id < min_layer_number):
                min_layer_number = layer.id

        current_layer = 0

        for layer in self._layers:
            abs_layer_number = layer.id + abs(min_layer_number)

            layer_data.addLayer(abs_layer_number)
            this_layer = layer_data.getLayer(abs_layer_number)
            layer_data.setLayerHeight(abs_layer_number, layer.height)
            layer_data.setLayerThickness(abs_layer_number, layer.thickness)

            for p in range(layer.repeatedMessageCount("path_segment")):
                polygon = layer.getRepeatedMessage("path_segment", p)

                extruder = polygon.extruder

                line_types = numpy.fromstring(polygon.line_type, dtype="u1")  # Convert bytearray to numpy array
                line_types = line_types.reshape((-1,1))

                points = numpy.fromstring(polygon.points, dtype="f4")  # Convert bytearray to numpy array
                if polygon.point_type == 0: # Point2D
                    points = points.reshape((-1,2))  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
                else: # Point3D
                    points = points.reshape((-1,3))

                line_widths = numpy.fromstring(polygon.line_width, dtype="f4")  # Convert bytearray to numpy array
                line_widths = line_widths.reshape((-1,1))  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
                
                # Create a new 3D-array, copy the 2D points over and insert the right height.
                # This uses manual array creation + copy rather than numpy.insert since this is
                # faster.
                new_points = numpy.empty((len(points), 3), numpy.float32)
                if polygon.point_type == 0: # Point2D
                    new_points[:,0] = points[:,0]
                    new_points[:,1] = layer.height/1000 # layer height value is in backend representation
                    new_points[:,2] = -points[:,1]
                else: # Point3D
                    new_points[:,0] = points[:,0]
                    new_points[:,1] = points[:,2]
                    new_points[:,2] = -points[:,1]
                    

                this_poly = LayerPolygon.LayerPolygon(layer_data, extruder, line_types, new_points, line_widths)
                this_poly.buildCache()
                
                this_layer.polygons.append(this_poly)

                Job.yieldThread()
            Job.yieldThread()
            current_layer += 1
            progress = (current_layer / layer_count) * 99
            # TODO: Rebuild the layer data mesh once the layer has been processed.
            # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return
            if self._progress:
                self._progress.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data
        layer_mesh = layer_data.build()

        if self._abort_requested:
            if self._progress:
                self._progress.hide()
            return

        # Add LayerDataDecorator to scene node to indicate that the node has layer data
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_mesh)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        # Set build volume as parent, the build volume can move as a result of raft settings.
        # It makes sense to set the build volume as parent: the print is actually printed on it.
        new_node_parent = Application.getInstance().getBuildVolume()
        new_node.setParent(new_node_parent)  # Note: After this we can no longer abort!

        settings = Application.getInstance().getGlobalContainerStack()
        if not settings.getProperty("machine_center_is_zero", "value"):
            new_node.setPosition(Vector(-settings.getProperty("machine_width", "value") / 2, 0.0, settings.getProperty("machine_depth", "value") / 2))

        if self._progress:
            self._progress.setProgress(100)

        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress:
            self._progress.hide()

        # Clear the unparsed layers. This saves us a bunch of memory if the Job does not get destroyed.
        self._layers = None

        Logger.log("d", "Processing layers took %s seconds", time() - start_time)
Beispiel #34
0
    def read(self, file_name):
        Logger.log("d", "Preparing to load %s" % file_name)
        self._cancelled = False

        scene_node = SceneNode()
        scene_node.getBoundingBox = self._getNullBoundingBox  # Manually set bounding box, because mesh doesn't have mesh data

        glist = []
        self._is_layers_in_file = False

        Logger.log("d", "Opening file %s" % file_name)

        with open(file_name, "r") as file:
            file_lines = 0
            current_line = 0
            for line in file:
                file_lines += 1
                glist.append(line)
                if not self._is_layers_in_file and line[:len(
                        self._layer_keyword)] == self._layer_keyword:
                    self._is_layers_in_file = True
            file.seek(0)

            file_step = max(math.floor(file_lines / 100), 1)

            self._clearValues()

            self._message = Message(catalog.i18nc("@info:status",
                                                  "Parsing G-code"),
                                    lifetime=0)
            self._message.setProgress(0)
            self._message.show()

            Logger.log("d", "Parsing %s" % file_name)

            current_position = self._position(0, 0, 0, [0])
            current_path = []

            for line in file:
                if self._cancelled:
                    Logger.log("d", "Parsing %s cancelled" % file_name)
                    return None
                current_line += 1
                if current_line % file_step == 0:
                    self._message.setProgress(
                        math.floor(current_line / file_lines * 100))
                if len(line) == 0:
                    continue
                if line.find(self._type_keyword) == 0:
                    type = line[len(self._type_keyword):].strip()
                    if type == "WALL-INNER":
                        self._layer_type = LayerPolygon.InsetXType
                    elif type == "WALL-OUTER":
                        self._layer_type = LayerPolygon.Inset0Type
                    elif type == "SKIN":
                        self._layer_type = LayerPolygon.SkinType
                    elif type == "SKIRT":
                        self._layer_type = LayerPolygon.SkirtType
                    elif type == "SUPPORT":
                        self._layer_type = LayerPolygon.SupportType
                    elif type == "FILL":
                        self._layer_type = LayerPolygon.InfillType
                if self._is_layers_in_file and line[:len(
                        self._layer_keyword)] == self._layer_keyword:
                    try:
                        layer_number = int(line[len(self._layer_keyword):])
                        self._createPolygon(current_position[2], current_path)
                        current_path.clear()
                        self._layer = layer_number
                    except:
                        pass
                if line[0] == ";":
                    continue

                G = self._getInt(line, "G")
                if G is not None:
                    current_position = self._processGCode(
                        G, line, current_position, current_path)
                T = self._getInt(line, "T")
                if T is not None:
                    current_position = self._processTCode(
                        T, line, current_position, current_path)

            if not self._is_layers_in_file and len(
                    current_path) > 1 and current_position[2] > 0:
                if self._createPolygon(current_position[2], current_path):
                    self._layer += 1
                current_path.clear()

        layer_mesh = self._layer_data_builder.build()
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_mesh)
        scene_node.addDecorator(decorator)

        gcode_list_decorator = GCodeListDecorator()
        gcode_list_decorator.setGCodeList(glist)
        scene_node.addDecorator(gcode_list_decorator)

        Logger.log("d", "Finished parsing %s" % file_name)
        self._message.hide()

        if self._layer == 0:
            Logger.log("w",
                       "File %s doesn't contain any valid layers" % file_name)

        settings = Application.getInstance().getGlobalContainerStack()
        machine_width = settings.getProperty("machine_width", "value")
        machine_depth = settings.getProperty("machine_depth", "value")

        if not self._center_is_zero:
            scene_node.setPosition(
                Vector(-machine_width / 2, 0, machine_depth / 2))

        Logger.log("d", "Loaded %s" % file_name)

        return scene_node
Beispiel #35
0
def group_scene_node():
    node = SceneNode()
    node.addDecorator(GroupDecorator())
    return node
Beispiel #36
0
    def _convertSavitarNodeToUMNode(self, savitar_node):
        um_node = SceneNode()
        transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation())
        um_node.setTransformation(transformation)
        mesh_builder = MeshBuilder()

        data = numpy.fromstring(savitar_node.getMeshData().getFlatVerticesAsBytes(), dtype=numpy.float32)

        vertices = numpy.resize(data, (int(data.size / 3), 3))
        mesh_builder.setVertices(vertices)
        mesh_builder.calculateNormals(fast=True)
        mesh_data = mesh_builder.build()

        if len(mesh_data.getVertices()):
            um_node.setMeshData(mesh_data)

        for child in savitar_node.getChildren():
            child_node = self._convertSavitarNodeToUMNode(child)
            if child_node:
                um_node.addChild(child_node)

        if um_node.getMeshData() is None and len(um_node.getChildren()) == 0:
            return None

        settings = savitar_node.getSettings()

        # Add the setting override decorator, so we can add settings to this node.
        if settings:
            um_node.addDecorator(SettingOverrideDecorator())

            global_container_stack = Application.getInstance().getGlobalContainerStack()
            # Ensure the correct next container for the SettingOverride decorator is set.
            if global_container_stack:
                multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1

                # Ensure that all extruder data is reset
                if not multi_extrusion:
                    default_stack_id = global_container_stack.getId()
                else:
                    default_stack = ExtruderManager.getInstance().getExtruderStack(0)
                    if default_stack:
                        default_stack_id = default_stack.getId()
                    else:
                        default_stack_id = global_container_stack.getId()
                um_node.callDecoration("setActiveExtruder", default_stack_id)

                # Get the definition & set it
                definition = QualityManager.getInstance().getParentMachineDefinition(global_container_stack.getBottom())
                um_node.callDecoration("getStack").getTop().setDefinition(definition)

            setting_container = um_node.callDecoration("getStack").getTop()

            for key in settings:
                setting_value = settings[key]

                # Extruder_nr is a special case.
                if key == "extruder_nr":
                    extruder_stack = ExtruderManager.getInstance().getExtruderStack(int(setting_value))
                    if extruder_stack:
                        um_node.callDecoration("setActiveExtruder", extruder_stack.getId())
                    else:
                        Logger.log("w", "Unable to find extruder in position %s", setting_value)
                    continue
                setting_container.setProperty(key,"value", setting_value)

        if len(um_node.getChildren()) > 0:
            group_decorator = GroupDecorator()
            um_node.addDecorator(group_decorator)
        um_node.setSelectable(True)
        if um_node.getMeshData():
            # Assuming that all nodes with mesh data are printable objects
            # affects (auto) slicing
            sliceable_decorator = SliceableObjectDecorator()
            um_node.addDecorator(sliceable_decorator)
        return um_node
Beispiel #37
0
def slicable_scene_node():
    node = SceneNode()
    node.addDecorator(SliceableObjectDecorator())
    return node
Beispiel #38
0
    def _createNodeFromObject(self, object, name = ""):
        node = SceneNode()
        node.setName(name)
        mesh_builder = MeshBuilder()
        vertex_list = []

        components = object.find(".//3mf:components", self._namespaces)
        if components:
            for component in components:
                id = component.get("objectid")
                new_object = self._root.find("./3mf:resources/3mf:object[@id='{0}']".format(id), self._namespaces)
                new_node = self._createNodeFromObject(new_object, self._base_name + "_" + str(id))
                node.addChild(new_node)
                transform = component.get("transform")
                if transform is not None:
                    new_node.setTransformation(self._createMatrixFromTransformationString(transform))

        # for vertex in entry.mesh.vertices.vertex:
        for vertex in object.findall(".//3mf:vertex", self._namespaces):
            vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")])
            Job.yieldThread()

        xml_settings = list(object.findall(".//cura:setting", self._namespaces))

        # Add the setting override decorator, so we can add settings to this node.
        if xml_settings:
            node.addDecorator(SettingOverrideDecorator())

            global_container_stack = Application.getInstance().getGlobalContainerStack()
            # Ensure the correct next container for the SettingOverride decorator is set.
            if global_container_stack:
                multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
                # Ensure that all extruder data is reset
                if not multi_extrusion:
                    default_stack_id = global_container_stack.getId()
                else:
                    default_stack = ExtruderManager.getInstance().getExtruderStack(0)
                    if default_stack:
                        default_stack_id = default_stack.getId()
                    else:
                        default_stack_id = global_container_stack.getId()
                node.callDecoration("setActiveExtruder", default_stack_id)

                # Get the definition & set it
                definition = QualityManager.getInstance().getParentMachineDefinition(global_container_stack.getBottom())
                node.callDecoration("getStack").getTop().setDefinition(definition)

            setting_container = node.callDecoration("getStack").getTop()
            for setting in xml_settings:
                setting_key = setting.get("key")
                setting_value = setting.text

                # Extruder_nr is a special case.
                if setting_key == "extruder_nr":
                    extruder_stack = ExtruderManager.getInstance().getExtruderStack(int(setting_value))
                    if extruder_stack:
                        node.callDecoration("setActiveExtruder", extruder_stack.getId())
                    else:
                        Logger.log("w", "Unable to find extruder in position %s", setting_value)
                    continue
                setting_container.setProperty(setting_key,"value", setting_value)

        if len(node.getChildren()) > 0:
            group_decorator = GroupDecorator()
            node.addDecorator(group_decorator)

        triangles = object.findall(".//3mf:triangle", self._namespaces)
        mesh_builder.reserveFaceCount(len(triangles))

        for triangle in triangles:
            v1 = int(triangle.get("v1"))
            v2 = int(triangle.get("v2"))
            v3 = int(triangle.get("v3"))

            mesh_builder.addFaceByPoints(vertex_list[v1][0], vertex_list[v1][1], vertex_list[v1][2],
                                         vertex_list[v2][0], vertex_list[v2][1], vertex_list[v2][2],
                                         vertex_list[v3][0], vertex_list[v3][1], vertex_list[v3][2])

            Job.yieldThread()

        # TODO: We currently do not check for normals and simply recalculate them.
        mesh_builder.calculateNormals()
        mesh_builder.setFileName(name)
        mesh_data = mesh_builder.build()

        if len(mesh_data.getVertices()):
            node.setMeshData(mesh_data)

        node.setSelectable(True)
        return node
def test_getConvexHullBoundaryNotPrintingMesh(convex_hull_decorator):
    node = SceneNode()
    node.addDecorator(NonPrintingDecorator())
    with patch("UM.Application.Application.getInstance", MagicMock(return_value=mocked_application)):
        convex_hull_decorator.setNode(node)
    assert convex_hull_decorator.getConvexHullBoundary() is None
    def run(self):
        if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
            self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
            self._progress.show()

        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)

        object_id_map = {}
        new_node = SceneNode()
        ## Put all nodes in a dict identified by ID
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is SceneNode and node.getMeshData():
                if node.callDecoration("getLayerData"):
                    self._scene.getRoot().removeChild(node)
                else:
                    object_id_map[id(node)] = node
            Job.yieldThread()

        settings = Application.getInstance().getMachineManager().getActiveProfile()

        center = None
        if not settings.getSettingValue("machine_center_is_zero"):
            center = numpy.array([settings.getSettingValue("machine_width") / 2, 0.0, -settings.getSettingValue("machine_depth") / 2])
        else:
            center = numpy.array([0.0, 0.0, 0.0])

        mesh = MeshData()
        layer_data = LayerData.LayerData()

        layer_count = 0
        for object in self._message.objects:
            layer_count += len(object.layers)

        current_layer = 0
        for object in self._message.objects:
            try:
                node = object_id_map[object.id]
            except KeyError:
                continue

            for layer in object.layers:
                layer_data.addLayer(layer.id)
                layer_data.setLayerHeight(layer.id, layer.height)
                layer_data.setLayerThickness(layer.id, layer.thickness)
                for polygon in layer.polygons:
                    points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array
                    points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
                    points = numpy.asarray(points, dtype=numpy.float32)
                    points /= 1000
                    points = numpy.insert(points, 1, (layer.height / 1000), axis = 1)

                    points[:,2] *= -1

                    points -= center

                    layer_data.addPolygon(layer.id, polygon.type, points, polygon.line_width)

                Job.yieldThread()

                current_layer += 1
                progress = (current_layer / layer_count) * 100
                # TODO: Rebuild the layer data mesh once the layer has been processed.
                # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

                if self._progress:
                    self._progress.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data
        layer_data.build()

        #Add layerdata decorator to scene node to indicate that the node has layerdata
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_data)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        new_node.setParent(self._scene.getRoot())

        if self._progress:
            self._progress.setProgress(100)

        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress:
            self._progress.hide()
Beispiel #41
0
    def processGCodeFile(self, file_name):
        Logger.log("d", "Preparing to load %s" % file_name)
        self._cancelled = False
        # We obtain the filament diameter from the selected printer to calculate line widths
        self._filament_diameter = Application.getInstance(
        ).getGlobalContainerStack().getProperty("material_diameter", "value")

        scene_node = SceneNode()
        # Override getBoundingBox function of the sceneNode, as this node should return a bounding box, but there is no
        # real data to calculate it from.
        scene_node.getBoundingBox = self._getNullBoundingBox

        gcode_list = []
        self._is_layers_in_file = False

        Logger.log("d", "Opening file %s" % file_name)

        self._extruder_offsets = self._extruderOffsets(
        )  # dict with index the extruder number. can be empty

        with open(file_name, "r") as file:
            file_lines = 0
            current_line = 0
            for line in file:
                file_lines += 1
                gcode_list.append(line)
                if not self._is_layers_in_file and line[:len(
                        self._layer_keyword)] == self._layer_keyword:
                    self._is_layers_in_file = True
            file.seek(0)

            file_step = max(math.floor(file_lines / 100), 1)

            self._clearValues()

            self._message = Message(catalog.i18nc("@info:status",
                                                  "Parsing G-code"),
                                    lifetime=0,
                                    title=catalog.i18nc(
                                        "@info:title", "G-code Details"))

            self._message.setProgress(0)
            self._message.show()

            Logger.log("d", "Parsing %s..." % file_name)

            current_position = self._position(0, 0, 0, 0, [0])
            current_path = []
            min_layer_number = 0
            negative_layers = 0
            previous_layer = 0

            for line in file:
                if self._cancelled:
                    Logger.log("d", "Parsing %s cancelled" % file_name)
                    return None
                current_line += 1

                if current_line % file_step == 0:
                    self._message.setProgress(
                        math.floor(current_line / file_lines * 100))
                    Job.yieldThread()
                if len(line) == 0:
                    continue

                if line.find(self._type_keyword) == 0:
                    type = line[len(self._type_keyword):].strip()
                    if type == "WALL-INNER":
                        self._layer_type = LayerPolygon.InsetXType
                    elif type == "WALL-OUTER":
                        self._layer_type = LayerPolygon.Inset0Type
                    elif type == "SKIN":
                        self._layer_type = LayerPolygon.SkinType
                    elif type == "SKIRT":
                        self._layer_type = LayerPolygon.SkirtType
                    elif type == "SUPPORT":
                        self._layer_type = LayerPolygon.SupportType
                    elif type == "FILL":
                        self._layer_type = LayerPolygon.InfillType
                    else:
                        Logger.log(
                            "w",
                            "Encountered a unknown type (%s) while parsing g-code.",
                            type)

                # When the layer change is reached, the polygon is computed so we have just one layer per layer per extruder
                if self._is_layers_in_file and line[:len(
                        self._layer_keyword)] == self._layer_keyword:
                    try:
                        layer_number = int(line[len(self._layer_keyword):])
                        self._createPolygon(
                            self._current_layer_thickness, current_path,
                            self._extruder_offsets.get(self._extruder_number,
                                                       [0, 0]))
                        current_path.clear()

                        # When using a raft, the raft layers are stored as layers < 0, it mimics the same behavior
                        # as in ProcessSlicedLayersJob
                        if layer_number < min_layer_number:
                            min_layer_number = layer_number
                        if layer_number < 0:
                            layer_number += abs(min_layer_number)
                            negative_layers += 1
                        else:
                            layer_number += negative_layers

                        # In case there is a gap in the layer count, empty layers are created
                        for empty_layer in range(previous_layer + 1,
                                                 layer_number):
                            self._createEmptyLayer(empty_layer)

                        self._layer_number = layer_number
                        previous_layer = layer_number
                    except:
                        pass

                # This line is a comment. Ignore it (except for the layer_keyword)
                if line.startswith(";"):
                    continue

                G = self._getInt(line, "G")
                if G is not None:
                    # When find a movement, the new posistion is calculated and added to the current_path, but
                    # don't need to create a polygon until the end of the layer
                    current_position = self.processGCode(
                        G, line, current_position, current_path)
                    continue

                # When changing the extruder, the polygon with the stored paths is computed
                if line.startswith("T"):
                    T = self._getInt(line, "T")
                    if T is not None:
                        self._createPolygon(
                            self._current_layer_thickness, current_path,
                            self._extruder_offsets.get(self._extruder_number,
                                                       [0, 0]))
                        current_path.clear()

                        current_position = self.processTCode(
                            T, line, current_position, current_path)

                if line.startswith("M"):
                    M = self._getInt(line, "M")
                    self.processMCode(M, line, current_position, current_path)

            # "Flush" leftovers. Last layer paths are still stored
            if len(current_path) > 1:
                if self._createPolygon(
                        self._current_layer_thickness, current_path,
                        self._extruder_offsets.get(self._extruder_number,
                                                   [0, 0])):
                    self._layer_number += 1
                    current_path.clear()

        material_color_map = numpy.zeros((10, 4), dtype=numpy.float32)
        material_color_map[0, :] = [0.0, 0.7, 0.9, 1.0]
        material_color_map[1, :] = [0.7, 0.9, 0.0, 1.0]
        layer_mesh = self._layer_data_builder.build(material_color_map)
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_mesh)
        scene_node.addDecorator(decorator)

        gcode_list_decorator = GCodeListDecorator()
        gcode_list_decorator.setGCodeList(gcode_list)
        scene_node.addDecorator(gcode_list_decorator)

        Application.getInstance().getController().getScene(
        ).gcode_list = gcode_list

        Logger.log("d", "Finished parsing %s" % file_name)
        self._message.hide()

        if self._layer_number == 0:
            Logger.log("w",
                       "File %s doesn't contain any valid layers" % file_name)

        settings = Application.getInstance().getGlobalContainerStack()
        machine_width = settings.getProperty("machine_width", "value")
        machine_depth = settings.getProperty("machine_depth", "value")

        if not self._center_is_zero:
            scene_node.setPosition(
                Vector(-machine_width / 2, 0, machine_depth / 2))

        Logger.log("d", "Loaded %s" % file_name)

        if Preferences.getInstance().getValue("gcodereader/show_caution"):
            caution_message = Message(catalog.i18nc(
                "@info:generic",
                "Make sure the g-code is suitable for your printer and printer configuration before sending the file to it. The g-code representation may not be accurate."
            ),
                                      lifetime=0,
                                      title=catalog.i18nc(
                                          "@info:title", "G-code Details"))
            caution_message.show()

        # The "save/print" button's state is bound to the backend state.
        backend = Application.getInstance().getBackend()
        backend.backendStateChange.emit(Backend.BackendState.Disabled)

        return scene_node
Beispiel #42
0
    def read(self, file_name):
        Logger.log("d", "Preparing to load %s" % file_name)
        self._cancelled = False

        scene_node = SceneNode()
        # Override getBoundingBox function of the sceneNode, as this node should return a bounding box, but there is no
        # real data to calculate it from.
        scene_node.getBoundingBox = self._getNullBoundingBox

        gcode_list = []
        self._is_layers_in_file = False

        Logger.log("d", "Opening file %s" % file_name)

        self._extruder_offsets = self._extruderOffsets(
        )  # dict with index the extruder number. can be empty

        last_z = 0
        with open(file_name, "r") as file:
            file_lines = 0
            current_line = 0
            for line in file:
                file_lines += 1
                gcode_list.append(line)
                if not self._is_layers_in_file and line[:len(
                        self._layer_keyword)] == self._layer_keyword:
                    self._is_layers_in_file = True
            file.seek(0)

            file_step = max(math.floor(file_lines / 100), 1)

            self._clearValues()

            self._message = Message(catalog.i18nc("@info:status",
                                                  "Parsing G-code"),
                                    lifetime=0)
            self._message.setProgress(0)
            self._message.show()

            Logger.log("d", "Parsing %s..." % file_name)

            current_position = self._position(0, 0, 0, [0])
            current_path = []

            for line in file:
                if self._cancelled:
                    Logger.log("d", "Parsing %s cancelled" % file_name)
                    return None
                current_line += 1
                last_z = current_position.z

                if current_line % file_step == 0:
                    self._message.setProgress(
                        math.floor(current_line / file_lines * 100))
                    Job.yieldThread()
                if len(line) == 0:
                    continue

                if line.find(self._type_keyword) == 0:
                    type = line[len(self._type_keyword):].strip()
                    if type == "WALL-INNER":
                        self._layer_type = LayerPolygon.InsetXType
                    elif type == "WALL-OUTER":
                        self._layer_type = LayerPolygon.Inset0Type
                    elif type == "SKIN":
                        self._layer_type = LayerPolygon.SkinType
                    elif type == "SKIRT":
                        self._layer_type = LayerPolygon.SkirtType
                    elif type == "SUPPORT":
                        self._layer_type = LayerPolygon.SupportType
                    elif type == "FILL":
                        self._layer_type = LayerPolygon.InfillType
                    else:
                        Logger.log(
                            "w",
                            "Encountered a unknown type (%s) while parsing g-code.",
                            type)

                if self._is_layers_in_file and line[:len(
                        self._layer_keyword)] == self._layer_keyword:
                    try:
                        layer_number = int(line[len(self._layer_keyword):])
                        self._createPolygon(
                            self._current_layer_thickness, current_path,
                            self._extruder_offsets.get(self._extruder_number,
                                                       [0, 0]))
                        current_path.clear()
                        self._layer_number = layer_number
                    except:
                        pass

                # This line is a comment. Ignore it (except for the layer_keyword)
                if line.startswith(";"):
                    continue

                G = self._getInt(line, "G")
                if G is not None:
                    current_position = self._processGCode(
                        G, line, current_position, current_path)

                    # < 2 is a heuristic for a movement only, that should not be counted as a layer
                    if current_position.z > last_z and abs(current_position.z -
                                                           last_z) < 2:
                        if self._createPolygon(
                                self._current_layer_thickness, current_path,
                                self._extruder_offsets.get(
                                    self._extruder_number, [0, 0])):
                            current_path.clear()
                            if not self._is_layers_in_file:
                                self._layer_number += 1

                    continue

                if line.startswith("T"):
                    T = self._getInt(line, "T")
                    if T is not None:
                        self._createPolygon(
                            self._current_layer_thickness, current_path,
                            self._extruder_offsets.get(self._extruder_number,
                                                       [0, 0]))
                        current_path.clear()

                        current_position = self._processTCode(
                            T, line, current_position, current_path)

            # "Flush" leftovers
            if not self._is_layers_in_file and len(current_path) > 1:
                if self._createPolygon(
                        self._current_layer_thickness, current_path,
                        self._extruder_offsets.get(self._extruder_number,
                                                   [0, 0])):
                    self._layer_number += 1
                    current_path.clear()

        material_color_map = numpy.zeros((10, 4), dtype=numpy.float32)
        material_color_map[0, :] = [0.0, 0.7, 0.9, 1.0]
        material_color_map[1, :] = [0.7, 0.9, 0.0, 1.0]
        layer_mesh = self._layer_data_builder.build(material_color_map)
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_mesh)
        scene_node.addDecorator(decorator)

        gcode_list_decorator = GCodeListDecorator()
        gcode_list_decorator.setGCodeList(gcode_list)
        scene_node.addDecorator(gcode_list_decorator)

        Application.getInstance().getController().getScene(
        ).gcode_list = gcode_list

        Logger.log("d", "Finished parsing %s" % file_name)
        self._message.hide()

        if self._layer_number == 0:
            Logger.log("w",
                       "File %s doesn't contain any valid layers" % file_name)

        settings = Application.getInstance().getGlobalContainerStack()
        machine_width = settings.getProperty("machine_width", "value")
        machine_depth = settings.getProperty("machine_depth", "value")

        if not self._center_is_zero:
            scene_node.setPosition(
                Vector(-machine_width / 2, 0, machine_depth / 2))

        Logger.log("d", "Loaded %s" % file_name)

        if Preferences.getInstance().getValue("gcodereader/show_caution"):
            caution_message = Message(catalog.i18nc(
                "@info:generic",
                "Make sure the g-code is suitable for your printer and printer configuration before sending the file to it. The g-code representation may not be accurate."
            ),
                                      lifetime=0)
            caution_message.show()

        # The "save/print" button's state is bound to the backend state.
        backend = Application.getInstance().getBackend()
        backend.backendStateChange.emit(Backend.BackendState.Disabled)

        return scene_node
Beispiel #43
0
    def _convertSavitarNodeToUMNode(self, savitar_node):
        um_node = SceneNode()
        transformation = self._createMatrixFromTransformationString(
            savitar_node.getTransformation())
        um_node.setTransformation(transformation)
        mesh_builder = MeshBuilder()

        data = numpy.fromstring(
            savitar_node.getMeshData().getFlatVerticesAsBytes(),
            dtype=numpy.float32)

        vertices = numpy.resize(data, (int(data.size / 3), 3))
        mesh_builder.setVertices(vertices)
        mesh_builder.calculateNormals(fast=True)
        mesh_data = mesh_builder.build()

        if len(mesh_data.getVertices()):
            um_node.setMeshData(mesh_data)

        for child in savitar_node.getChildren():
            child_node = self._convertSavitarNodeToUMNode(child)
            if child_node:
                um_node.addChild(child_node)

        if um_node.getMeshData() is None and len(um_node.getChildren()) == 0:
            return None

        settings = savitar_node.getSettings()

        # Add the setting override decorator, so we can add settings to this node.
        if settings:
            um_node.addDecorator(SettingOverrideDecorator())

            global_container_stack = Application.getInstance(
            ).getGlobalContainerStack()

            # Ensure the correct next container for the SettingOverride decorator is set.
            if global_container_stack:
                default_stack = ExtruderManager.getInstance().getExtruderStack(
                    0)

                if default_stack:
                    um_node.callDecoration("setActiveExtruder",
                                           default_stack.getId())

                # Get the definition & set it
                definition = QualityManager.getInstance(
                ).getParentMachineDefinition(
                    global_container_stack.getBottom())
                um_node.callDecoration("getStack").getTop().setDefinition(
                    definition.getId())

            setting_container = um_node.callDecoration("getStack").getTop()

            for key in settings:
                setting_value = settings[key]

                # Extruder_nr is a special case.
                if key == "extruder_nr":
                    extruder_stack = ExtruderManager.getInstance(
                    ).getExtruderStack(int(setting_value))
                    if extruder_stack:
                        um_node.callDecoration("setActiveExtruder",
                                               extruder_stack.getId())
                    else:
                        Logger.log("w",
                                   "Unable to find extruder in position %s",
                                   setting_value)
                    continue
                setting_container.setProperty(key, "value", setting_value)

        if len(um_node.getChildren()) > 0:
            group_decorator = GroupDecorator()
            um_node.addDecorator(group_decorator)
        um_node.setSelectable(True)
        if um_node.getMeshData():
            # Assuming that all nodes with mesh data are printable objects
            # affects (auto) slicing
            sliceable_decorator = SliceableObjectDecorator()
            um_node.addDecorator(sliceable_decorator)
        return um_node
Beispiel #44
0
    def _createNodeFromObject(self, object, name=""):
        node = SceneNode()
        node.setName(name)
        mesh_builder = MeshBuilder()
        vertex_list = []

        components = object.find(".//3mf:components", self._namespaces)
        if components:
            for component in components:
                id = component.get("objectid")
                new_object = self._root.find(
                    "./3mf:resources/3mf:object[@id='{0}']".format(id),
                    self._namespaces)
                new_node = self._createNodeFromObject(
                    new_object, self._base_name + "_" + str(id))
                node.addChild(new_node)
                transform = component.get("transform")
                if transform is not None:
                    new_node.setTransformation(
                        self._createMatrixFromTransformationString(transform))

        # for vertex in entry.mesh.vertices.vertex:
        for vertex in object.findall(".//3mf:vertex", self._namespaces):
            vertex_list.append(
                [vertex.get("x"),
                 vertex.get("y"),
                 vertex.get("z")])
            Job.yieldThread()

        xml_settings = list(object.findall(".//cura:setting",
                                           self._namespaces))

        # Add the setting override decorator, so we can add settings to this node.
        if xml_settings:
            node.addDecorator(SettingOverrideDecorator())

            global_container_stack = Application.getInstance(
            ).getGlobalContainerStack()
            # Ensure the correct next container for the SettingOverride decorator is set.
            if global_container_stack:
                multi_extrusion = global_container_stack.getProperty(
                    "machine_extruder_count", "value") > 1
                # Ensure that all extruder data is reset
                if not multi_extrusion:
                    default_stack_id = global_container_stack.getId()
                else:
                    default_stack = ExtruderManager.getInstance(
                    ).getExtruderStack(0)
                    if default_stack:
                        default_stack_id = default_stack.getId()
                    else:
                        default_stack_id = global_container_stack.getId()
                node.callDecoration("setActiveExtruder", default_stack_id)

                # Get the definition & set it
                definition = QualityManager.getInstance(
                ).getParentMachineDefinition(
                    global_container_stack.getBottom())
                node.callDecoration("getStack").getTop().setDefinition(
                    definition)

            setting_container = node.callDecoration("getStack").getTop()
            for setting in xml_settings:
                setting_key = setting.get("key")
                setting_value = setting.text

                # Extruder_nr is a special case.
                if setting_key == "extruder_nr":
                    extruder_stack = ExtruderManager.getInstance(
                    ).getExtruderStack(int(setting_value))
                    if extruder_stack:
                        node.callDecoration("setActiveExtruder",
                                            extruder_stack.getId())
                    else:
                        Logger.log("w",
                                   "Unable to find extruder in position %s",
                                   setting_value)
                    continue
                setting_container.setProperty(setting_key, "value",
                                              setting_value)

        if len(node.getChildren()) > 0:
            group_decorator = GroupDecorator()
            node.addDecorator(group_decorator)

        triangles = object.findall(".//3mf:triangle", self._namespaces)
        mesh_builder.reserveFaceCount(len(triangles))

        for triangle in triangles:
            v1 = int(triangle.get("v1"))
            v2 = int(triangle.get("v2"))
            v3 = int(triangle.get("v3"))

            mesh_builder.addFaceByPoints(
                vertex_list[v1][0], vertex_list[v1][1], vertex_list[v1][2],
                vertex_list[v2][0], vertex_list[v2][1], vertex_list[v2][2],
                vertex_list[v3][0], vertex_list[v3][1], vertex_list[v3][2])

            Job.yieldThread()

        # TODO: We currently do not check for normals and simply recalculate them.
        mesh_builder.calculateNormals(fast=True)
        mesh_builder.setFileName(name)
        mesh_data = mesh_builder.build()

        if len(mesh_data.getVertices()):
            node.setMeshData(mesh_data)

        node.setSelectable(True)
        return node
Beispiel #45
0
    def read(self, file_name):
        result = SceneNode()
        # The base object of 3mf is a zipped archive.
        archive = zipfile.ZipFile(file_name, "r")
        try:
            root = ET.parse(archive.open("3D/3dmodel.model"))

            # There can be multiple objects, try to load all of them.
            objects = root.findall("./3mf:resources/3mf:object",
                                   self._namespaces)
            if len(objects) == 0:
                Logger.log(
                    "w",
                    "No objects found in 3MF file %s, either the file is corrupt or you are using an outdated format",
                    file_name)
                return None

            for entry in objects:
                mesh_builder = MeshBuilder()
                node = SceneNode()
                vertex_list = []
                #for vertex in entry.mesh.vertices.vertex:
                for vertex in entry.findall(".//3mf:vertex", self._namespaces):
                    vertex_list.append(
                        [vertex.get("x"),
                         vertex.get("y"),
                         vertex.get("z")])
                    Job.yieldThread()

                triangles = entry.findall(".//3mf:triangle", self._namespaces)
                mesh_builder.reserveFaceCount(len(triangles))

                for triangle in triangles:
                    v1 = int(triangle.get("v1"))
                    v2 = int(triangle.get("v2"))
                    v3 = int(triangle.get("v3"))

                    mesh_builder.addFaceByPoints(
                        vertex_list[v1][0], vertex_list[v1][1],
                        vertex_list[v1][2], vertex_list[v2][0],
                        vertex_list[v2][1], vertex_list[v2][2],
                        vertex_list[v3][0], vertex_list[v3][1],
                        vertex_list[v3][2])

                    Job.yieldThread()

                # Rotate the model; We use a different coordinate frame.
                rotation = Matrix()
                rotation.setByRotationAxis(-0.5 * math.pi, Vector(1, 0, 0))

                # TODO: We currently do not check for normals and simply recalculate them.
                mesh_builder.calculateNormals()
                mesh_builder.setFileName(file_name)
                node.setMeshData(mesh_builder.build().getTransformed(rotation))
                node.setSelectable(True)

                transformations = root.findall(
                    "./3mf:build/3mf:item[@objectid='{0}']".format(
                        entry.get("id")), self._namespaces)
                transformation = transformations[0] if transformations else None
                if transformation is not None and transformation.get(
                        "transform"):
                    splitted_transformation = transformation.get(
                        "transform").split()
                    ## Transformation is saved as:
                    ## M00 M01 M02 0.0
                    ## M10 M11 M12 0.0
                    ## M20 M21 M22 0.0
                    ## M30 M31 M32 1.0
                    ## We switch the row & cols as that is how everyone else uses matrices!
                    temp_mat = Matrix()
                    # Rotation & Scale
                    temp_mat._data[0, 0] = splitted_transformation[0]
                    temp_mat._data[1, 0] = splitted_transformation[1]
                    temp_mat._data[2, 0] = splitted_transformation[2]
                    temp_mat._data[0, 1] = splitted_transformation[3]
                    temp_mat._data[1, 1] = splitted_transformation[4]
                    temp_mat._data[2, 1] = splitted_transformation[5]
                    temp_mat._data[0, 2] = splitted_transformation[6]
                    temp_mat._data[1, 2] = splitted_transformation[7]
                    temp_mat._data[2, 2] = splitted_transformation[8]

                    # Translation
                    temp_mat._data[0, 3] = splitted_transformation[9]
                    temp_mat._data[1, 3] = splitted_transformation[10]
                    temp_mat._data[2, 3] = splitted_transformation[11]

                    node.setTransformation(temp_mat)

                result.addChild(node)

                Job.yieldThread()

            # If there is more then one object, group them.
            if len(objects) > 1:
                group_decorator = GroupDecorator()
                result.addDecorator(group_decorator)
            elif len(objects) == 1:
                result = result.getChildren()[
                    0]  # Only one object found, return that.
        except Exception as e:
            Logger.log("e", "exception occured in 3mf reader: %s", e)

        try:  # Selftest - There might be more functions that should fail
            boundingBox = result.getBoundingBox()
            boundingBox.isValid()
        except:
            return None

        return result
    def run(self):
        if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
            self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
            self._progress.show()
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)

        object_id_map = {}
        new_node = SceneNode()
        ## Put all nodes in a dictionary identified by ID
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is SceneNode and node.getMeshData():
                if node.callDecoration("getLayerData"):
                    self._scene.getRoot().removeChild(node)
                else:
                    object_id_map[id(node)] = node
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        settings = Application.getInstance().getMachineManager().getWorkingProfile()

        mesh = MeshData()
        layer_data = LayerData.LayerData()

        layer_count = 0
        for i in range(self._message.repeatedMessageCount("objects")):
            layer_count += self._message.getRepeatedMessage("objects", i).repeatedMessageCount("layers")

        current_layer = 0
        for object_position in range(self._message.repeatedMessageCount("objects")):
            current_object = self._message.getRepeatedMessage("objects", object_position)
            try:
                node = object_id_map[current_object.id]
            except KeyError:
                continue

            for l in range(current_object.repeatedMessageCount("layers")):
                layer = current_object.getRepeatedMessage("layers", l)

                layer_data.addLayer(layer.id)
                layer_data.setLayerHeight(layer.id, layer.height)
                layer_data.setLayerThickness(layer.id, layer.thickness)

                for p in range(layer.repeatedMessageCount("polygons")):
                    polygon = layer.getRepeatedMessage("polygons", p)

                    points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array
                    points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.

                    # Create a new 3D-array, copy the 2D points over and insert the right height.
                    # This uses manual array creation + copy rather than numpy.insert since this is
                    # faster.
                    new_points = numpy.empty((len(points), 3), numpy.float32)
                    new_points[:,0] = points[:,0]
                    new_points[:,1] = layer.height
                    new_points[:,2] = -points[:,1]

                    new_points /= 1000

                    layer_data.addPolygon(layer.id, polygon.type, new_points, polygon.line_width)
                    Job.yieldThread()
                Job.yieldThread()
                current_layer += 1
                progress = (current_layer / layer_count) * 100
                # TODO: Rebuild the layer data mesh once the layer has been processed.
                # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

                if self._abort_requested:
                    if self._progress:
                        self._progress.hide()
                    return
                if self._progress:
                    self._progress.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data
        layer_data.build()

        if self._abort_requested:
            if self._progress:
                self._progress.hide()
            return

        #Add layerdata decorator to scene node to indicate that the node has layerdata
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_data)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        new_node.setParent(self._scene.getRoot()) #Note: After this we can no longer abort!

        if not settings.getSettingValue("machine_center_is_zero"):
            new_node.setPosition(Vector(-settings.getSettingValue("machine_width") / 2, 0.0, settings.getSettingValue("machine_depth") / 2))

        if self._progress:
            self._progress.setProgress(100)

        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress:
            self._progress.hide()
Beispiel #47
0
def slicable_scene_node():
    node = SceneNode()
    node.addDecorator(SliceableObjectDecorator())
    return node
Beispiel #48
0
    def read(self, file_name):
        Logger.log("d", "Preparing to load %s" % file_name)
        self._cancelled = False

        scene_node = SceneNode()
        # Override getBoundingBox function of the sceneNode, as this node should return a bounding box, but there is no
        # real data to calculate it from.
        scene_node.getBoundingBox = self._getNullBoundingBox

        gcode_list = []
        self._is_layers_in_file = False

        Logger.log("d", "Opening file %s" % file_name)

        with open(file_name, "r") as file:
            file_lines = 0
            current_line = 0
            for line in file:
                file_lines += 1
                gcode_list.append(line)
                if not self._is_layers_in_file and line[:len(
                        self._layer_keyword)] == self._layer_keyword:
                    self._is_layers_in_file = True
            file.seek(0)

            file_step = max(math.floor(file_lines / 100), 1)

            self._clearValues()

            self._message = Message(catalog.i18nc("@info:status",
                                                  "Parsing G-code"),
                                    lifetime=0)
            self._message.setProgress(0)
            self._message.show()

            Logger.log("d", "Parsing %s" % file_name)

            current_position = self._position(0, 0, 0, [0])
            current_path = []

            for line in file:
                if self._cancelled:
                    Logger.log("d", "Parsing %s cancelled" % file_name)
                    return None
                current_line += 1
                if current_line % file_step == 0:
                    self._message.setProgress(
                        math.floor(current_line / file_lines * 100))
                if len(line) == 0:
                    continue

                if line.find(self._type_keyword) == 0:
                    type = line[len(self._type_keyword):].strip()
                    if type == "WALL-INNER":
                        self._layer_type = LayerPolygon.InsetXType
                    elif type == "WALL-OUTER":
                        self._layer_type = LayerPolygon.Inset0Type
                    elif type == "SKIN":
                        self._layer_type = LayerPolygon.SkinType
                    elif type == "SKIRT":
                        self._layer_type = LayerPolygon.SkirtType
                    elif type == "SUPPORT":
                        self._layer_type = LayerPolygon.SupportType
                    elif type == "FILL":
                        self._layer_type = LayerPolygon.InfillType
                    else:
                        Logger.log(
                            "w",
                            "Encountered a unknown type (%s) while parsing g-code.",
                            type)

                if self._is_layers_in_file and line[:len(
                        self._layer_keyword)] == self._layer_keyword:
                    try:
                        layer_number = int(line[len(self._layer_keyword):])
                        self._createPolygon(current_position[2], current_path)
                        current_path.clear()
                        self._layer_number = layer_number
                    except:
                        pass

                # This line is a comment. Ignore it.
                if line.startswith(";"):
                    continue

                G = self._getInt(line, "G")
                if G is not None:
                    current_position = self._processGCode(
                        G, line, current_position, current_path)

                T = self._getInt(line, "T")
                if T is not None:
                    current_position = self._processTCode(
                        T, line, current_position, current_path)

            if not self._is_layers_in_file and len(
                    current_path) > 1 and current_position[2] > 0:
                if self._createPolygon(current_position[2], current_path):
                    self._layer_number += 1
                current_path.clear()

        material_color_map = numpy.zeros((10, 4), dtype=numpy.float32)
        material_color_map[0, :] = [0.0, 0.7, 0.9, 1.0]
        material_color_map[1, :] = [0.7, 0.9, 0.0, 1.0]
        layer_mesh = self._layer_data_builder.build(material_color_map)
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_mesh)
        scene_node.addDecorator(decorator)

        gcode_list_decorator = GCodeListDecorator()
        gcode_list_decorator.setGCodeList(gcode_list)
        scene_node.addDecorator(gcode_list_decorator)

        Logger.log("d", "Finished parsing %s" % file_name)
        self._message.hide()

        if self._layer_number == 0:
            Logger.log("w",
                       "File %s doesn't contain any valid layers" % file_name)

        settings = Application.getInstance().getGlobalContainerStack()
        machine_width = settings.getProperty("machine_width", "value")
        machine_depth = settings.getProperty("machine_depth", "value")

        if not self._center_is_zero:
            scene_node.setPosition(
                Vector(-machine_width / 2, 0, machine_depth / 2))

        Logger.log("d", "Loaded %s" % file_name)

        return scene_node
Beispiel #49
0
    def run(self):
        start_time = time()
        if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
            self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
            self._progress.show()
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)

        new_node = SceneNode()

        ## Remove old layer data (if any)
        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.callDecoration("getLayerData"):
                node.getParent().removeChild(node)
                break
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        # Force garbage collection.
        # For some reason, Python has a tendency to keep the layer data
        # in memory longer than needed. Forcing the GC to run here makes
        # sure any old layer data is really cleaned up before adding new.
        gc.collect()

        mesh = MeshData()
        layer_data = LayerDataBuilder.LayerDataBuilder()
        layer_count = len(self._layers)

        # Find the minimum layer number
        # When using a raft, the raft layers are sent as layers < 0. Instead of allowing layers < 0, we
        # instead simply offset all other layers so the lowest layer is always 0.
        min_layer_number = 0
        for layer in self._layers:
            if layer.id < min_layer_number:
                min_layer_number = layer.id

        current_layer = 0

        for layer in self._layers:
            abs_layer_number = layer.id + abs(min_layer_number)

            layer_data.addLayer(abs_layer_number)
            this_layer = layer_data.getLayer(abs_layer_number)
            layer_data.setLayerHeight(abs_layer_number, layer.height)

            for p in range(layer.repeatedMessageCount("path_segment")):
                polygon = layer.getRepeatedMessage("path_segment", p)

                extruder = polygon.extruder

                line_types = numpy.fromstring(polygon.line_type, dtype="u1")  # Convert bytearray to numpy array
                line_types = line_types.reshape((-1,1))

                points = numpy.fromstring(polygon.points, dtype="f4")  # Convert bytearray to numpy array
                if polygon.point_type == 0: # Point2D
                    points = points.reshape((-1,2))  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
                else:  # Point3D
                    points = points.reshape((-1,3))

                line_widths = numpy.fromstring(polygon.line_width, dtype="f4")  # Convert bytearray to numpy array
                line_widths = line_widths.reshape((-1,1))  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.

                # In the future, line_thicknesses should be given by CuraEngine as well.
                # Currently the infill layer thickness also translates to line width
                line_thicknesses = numpy.zeros(line_widths.shape, dtype="f4")
                line_thicknesses[:] = layer.thickness / 1000  # from micrometer to millimeter

                # Create a new 3D-array, copy the 2D points over and insert the right height.
                # This uses manual array creation + copy rather than numpy.insert since this is
                # faster.
                new_points = numpy.empty((len(points), 3), numpy.float32)
                if polygon.point_type == 0:  # Point2D
                    new_points[:, 0] = points[:, 0]
                    new_points[:, 1] = layer.height / 1000  # layer height value is in backend representation
                    new_points[:, 2] = -points[:, 1]
                else: # Point3D
                    new_points[:, 0] = points[:, 0]
                    new_points[:, 1] = points[:, 2]
                    new_points[:, 2] = -points[:, 1]

                this_poly = LayerPolygon.LayerPolygon(extruder, line_types, new_points, line_widths, line_thicknesses)
                this_poly.buildCache()

                this_layer.polygons.append(this_poly)

                Job.yieldThread()
            Job.yieldThread()
            current_layer += 1
            progress = (current_layer / layer_count) * 99
            # TODO: Rebuild the layer data mesh once the layer has been processed.
            # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return
            if self._progress:
                self._progress.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data

        # Find out colors per extruder
        global_container_stack = Application.getInstance().getGlobalContainerStack()
        manager = ExtruderManager.getInstance()
        extruders = list(manager.getMachineExtruders(global_container_stack.getId()))
        if extruders:
            material_color_map = numpy.zeros((len(extruders), 4), dtype=numpy.float32)
            for extruder in extruders:
                position = int(extruder.getMetaDataEntry("position", default="0"))  # Get the position
                try:
                    default_color = ExtrudersModel.defaultColors[position]
                except KeyError:
                    default_color = "#e0e000"
                color_code = extruder.material.getMetaDataEntry("color_code", default=default_color)
                color = colorCodeToRGBA(color_code)
                material_color_map[position, :] = color
        else:
            # Single extruder via global stack.
            material_color_map = numpy.zeros((1, 4), dtype=numpy.float32)
            color_code = global_container_stack.material.getMetaDataEntry("color_code", default="#e0e000")
            color = colorCodeToRGBA(color_code)
            material_color_map[0, :] = color

        # We have to scale the colors for compatibility mode
        if OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode")):
            line_type_brightness = 0.5  # for compatibility mode
        else:
            line_type_brightness = 1.0
        layer_mesh = layer_data.build(material_color_map, line_type_brightness)

        if self._abort_requested:
            if self._progress:
                self._progress.hide()
            return

        # Add LayerDataDecorator to scene node to indicate that the node has layer data
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_mesh)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        # Set build volume as parent, the build volume can move as a result of raft settings.
        # It makes sense to set the build volume as parent: the print is actually printed on it.
        new_node_parent = Application.getInstance().getBuildVolume()
        new_node.setParent(new_node_parent)  # Note: After this we can no longer abort!

        settings = Application.getInstance().getGlobalContainerStack()
        if not settings.getProperty("machine_center_is_zero", "value"):
            new_node.setPosition(Vector(-settings.getProperty("machine_width", "value") / 2, 0.0, settings.getProperty("machine_depth", "value") / 2))

        if self._progress:
            self._progress.setProgress(100)

        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress:
            self._progress.hide()

        # Clear the unparsed layers. This saves us a bunch of memory if the Job does not get destroyed.
        self._layers = None

        Logger.log("d", "Processing layers took %s seconds", time() - start_time)
Beispiel #50
0
    def run(self):
        if Application.getInstance().getController().getActiveView(
        ).getPluginId() == "LayerView":
            self._progress = Message(
                catalog.i18nc("@info:status", "Processing Layers"), 0, False,
                -1)
            self._progress.show()

        Application.getInstance().getController().activeViewChanged.connect(
            self._onActiveViewChanged)

        objectIdMap = {}
        new_node = SceneNode()
        ## Put all nodes in a dict identified by ID
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is SceneNode and node.getMeshData():
                if node.callDecoration("getLayerData"):
                    self._scene.getRoot().removeChild(node)
                else:
                    objectIdMap[id(node)] = node
            Job.yieldThread()

        settings = Application.getInstance().getMachineManager(
        ).getActiveProfile()
        layerHeight = settings.getSettingValue("layer_height")

        center = None
        if not settings.getSettingValue("machine_center_is_zero"):
            center = numpy.array([
                settings.getSettingValue("machine_width") / 2, 0.0,
                -settings.getSettingValue("machine_depth") / 2
            ])
        else:
            center = numpy.array([0.0, 0.0, 0.0])

        mesh = MeshData()
        layer_data = LayerData.LayerData()

        layer_count = 0
        for object in self._message.objects:
            layer_count += len(object.layers)

        current_layer = 0
        for object in self._message.objects:
            try:
                node = objectIdMap[object.id]
            except KeyError:
                continue

            for layer in object.layers:
                layer_data.addLayer(layer.id)
                layer_data.setLayerHeight(layer.id, layer.height)
                layer_data.setLayerThickness(layer.id, layer.thickness)
                for polygon in layer.polygons:
                    points = numpy.fromstring(
                        polygon.points,
                        dtype="i8")  # Convert bytearray to numpy array
                    points = points.reshape(
                        (-1, 2)
                    )  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
                    points = numpy.asarray(points, dtype=numpy.float32)
                    points /= 1000
                    points = numpy.insert(points,
                                          1, (layer.height / 1000),
                                          axis=1)

                    points[:, 2] *= -1

                    points -= center

                    layer_data.addPolygon(layer.id, polygon.type, points,
                                          polygon.line_width)

                Job.yieldThread()

                current_layer += 1
                progress = (current_layer / layer_count) * 100
                # TODO: Rebuild the layer data mesh once the layer has been processed.
                # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

                if self._progress:
                    self._progress.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data
        layer_data.build()

        #Add layerdata decorator to scene node to indicate that the node has layerdata
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_data)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        new_node.setParent(self._scene.getRoot())

        if self._progress:
            self._progress.setProgress(100)

        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress:
            self._progress.hide()
Beispiel #51
0
    def run(self):
        start_time = time()
        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "SimulationView":
            view.resetLayerData()
            self._progress_message.show()
            Job.yieldThread()
            if self._abort_requested:
                if self._progress_message:
                    self._progress_message.hide()
                return

        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)

        new_node = SceneNode()

        ## Remove old layer data (if any)
        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.callDecoration("getLayerData"):
                node.getParent().removeChild(node)
                break
            if self._abort_requested:
                if self._progress_message:
                    self._progress_message.hide()
                return

        # Force garbage collection.
        # For some reason, Python has a tendency to keep the layer data
        # in memory longer than needed. Forcing the GC to run here makes
        # sure any old layer data is really cleaned up before adding new.
        gc.collect()

        mesh = MeshData()
        layer_data = LayerDataBuilder.LayerDataBuilder()
        layer_count = len(self._layers)

        # Find the minimum layer number
        # When using a raft, the raft layers are sent as layers < 0. Instead of allowing layers < 0, we
        # instead simply offset all other layers so the lowest layer is always 0. It could happens that
        # the first raft layer has value -8 but there are just 4 raft (negative) layers.
        min_layer_number = 0
        negative_layers = 0
        for layer in self._layers:
            if layer.id < min_layer_number:
                min_layer_number = layer.id
            if layer.id < 0:
                negative_layers += 1

        current_layer = 0

        for layer in self._layers:
            # Negative layers are offset by the minimum layer number, but the positive layers are just
            # offset by the number of negative layers so there is no layer gap between raft and model
            abs_layer_number = layer.id + abs(min_layer_number) if layer.id < 0 else layer.id + negative_layers

            layer_data.addLayer(abs_layer_number)
            this_layer = layer_data.getLayer(abs_layer_number)
            layer_data.setLayerHeight(abs_layer_number, layer.height)
            layer_data.setLayerThickness(abs_layer_number, layer.thickness)

            for p in range(layer.repeatedMessageCount("path_segment")):
                polygon = layer.getRepeatedMessage("path_segment", p)

                extruder = polygon.extruder

                line_types = numpy.fromstring(polygon.line_type, dtype="u1")  # Convert bytearray to numpy array
                line_types = line_types.reshape((-1,1))

                points = numpy.fromstring(polygon.points, dtype="f4")  # Convert bytearray to numpy array
                if polygon.point_type == 0: # Point2D
                    points = points.reshape((-1,2))  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
                else:  # Point3D
                    points = points.reshape((-1,3))

                line_widths = numpy.fromstring(polygon.line_width, dtype="f4")  # Convert bytearray to numpy array
                line_widths = line_widths.reshape((-1,1))  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.

                line_thicknesses = numpy.fromstring(polygon.line_thickness, dtype="f4")  # Convert bytearray to numpy array
                line_thicknesses = line_thicknesses.reshape((-1,1))  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.

                line_feedrates = numpy.fromstring(polygon.line_feedrate, dtype="f4")  # Convert bytearray to numpy array
                line_feedrates = line_feedrates.reshape((-1,1))  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.

                # Create a new 3D-array, copy the 2D points over and insert the right height.
                # This uses manual array creation + copy rather than numpy.insert since this is
                # faster.
                new_points = numpy.empty((len(points), 3), numpy.float32)
                if polygon.point_type == 0:  # Point2D
                    new_points[:, 0] = points[:, 0]
                    new_points[:, 1] = layer.height / 1000  # layer height value is in backend representation
                    new_points[:, 2] = -points[:, 1]
                else: # Point3D
                    new_points[:, 0] = points[:, 0]
                    new_points[:, 1] = points[:, 2]
                    new_points[:, 2] = -points[:, 1]

                this_poly = LayerPolygon.LayerPolygon(extruder, line_types, new_points, line_widths, line_thicknesses, line_feedrates)
                this_poly.buildCache()

                this_layer.polygons.append(this_poly)

                Job.yieldThread()
            Job.yieldThread()
            current_layer += 1
            progress = (current_layer / layer_count) * 99
            # TODO: Rebuild the layer data mesh once the layer has been processed.
            # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

            if self._abort_requested:
                if self._progress_message:
                    self._progress_message.hide()
                return
            if self._progress_message:
                self._progress_message.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data

        # Find out colors per extruder
        global_container_stack = Application.getInstance().getGlobalContainerStack()
        manager = ExtruderManager.getInstance()
        extruders = list(manager.getMachineExtruders(global_container_stack.getId()))
        if extruders:
            material_color_map = numpy.zeros((len(extruders), 4), dtype=numpy.float32)
            for extruder in extruders:
                position = int(extruder.getMetaDataEntry("position", default="0"))  # Get the position
                try:
                    default_color = ExtrudersModel.defaultColors[position]
                except IndexError:
                    default_color = "#e0e000"
                color_code = extruder.material.getMetaDataEntry("color_code", default=default_color)
                color = colorCodeToRGBA(color_code)
                material_color_map[position, :] = color
        else:
            # Single extruder via global stack.
            material_color_map = numpy.zeros((1, 4), dtype=numpy.float32)
            color_code = global_container_stack.material.getMetaDataEntry("color_code", default="#e0e000")
            color = colorCodeToRGBA(color_code)
            material_color_map[0, :] = color

        # We have to scale the colors for compatibility mode
        if OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode")):
            line_type_brightness = 0.5  # for compatibility mode
        else:
            line_type_brightness = 1.0
        layer_mesh = layer_data.build(material_color_map, line_type_brightness)

        if self._abort_requested:
            if self._progress_message:
                self._progress_message.hide()
            return

        # Add LayerDataDecorator to scene node to indicate that the node has layer data
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_mesh)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        # Set build volume as parent, the build volume can move as a result of raft settings.
        # It makes sense to set the build volume as parent: the print is actually printed on it.
        new_node_parent = Application.getInstance().getBuildVolume()
        new_node.setParent(new_node_parent)  # Note: After this we can no longer abort!

        settings = Application.getInstance().getGlobalContainerStack()
        if not settings.getProperty("machine_center_is_zero", "value"):
            new_node.setPosition(Vector(-settings.getProperty("machine_width", "value") / 2, 0.0, settings.getProperty("machine_depth", "value") / 2))

        if self._progress_message:
            self._progress_message.setProgress(100)

        if self._progress_message:
            self._progress_message.hide()

        # Clear the unparsed layers. This saves us a bunch of memory if the Job does not get destroyed.
        self._layers = None

        Logger.log("d", "Processing layers took %s seconds", time() - start_time)