예제 #1
0
    def _updateTransformation(self):
        self._transformation = Matrix.fromPositionOrientationScale(self._position, self._orientation, self._scale)

        if self._parent:
            parent_orientation = self._parent._getDerivedOrientation()
            if self._inherit_orientation:
                self._derived_orientation = parent_orientation * self._orientation
            else:
                self._derived_orientation = self._orientation

            parent_scale = self._parent._getDerivedScale()
            if self._inherit_scale:
                self._derived_scale = parent_scale.scale(self._scale)
            else:
                self._derived_scale = self._scale

            self._derived_position = parent_orientation.rotate(parent_scale.scale(self._position))
            self._derived_position += self._parent._getDerivedPosition()

            self._world_transformation = Matrix.fromPositionOrientationScale(self._derived_position, self._derived_orientation, self._derived_scale)
        else:
            self._derived_position = self._position
            self._derived_orientation = self._orientation
            self._derived_scale = self._scale
            self._world_transformation = self._transformation
예제 #2
0
    def read(self, file_name, **kwargs):
        try:
            for id, reader in self._mesh_readers.items():
                result = reader.read(file_name)
                if result is not None:
                    if kwargs.get("center", True):
                        # If the result has a mesh and no children it needs to be centered
                        if result.getMeshData() and len(result.getChildren()) == 0:
                            extents = result.getMeshData().getExtents()
                            move_vector = Vector()
                            move_vector.setX(extents.center.x)
                            move_vector.setY(extents.center.y) # Ensure that bottom is on 0 (above plate)
                            move_vector.setZ(extents.center.z)
                            result.setCenterPosition(move_vector)

                            if result.getMeshData().getExtents().bottom != 0:
                               result.translate(Vector(0,-result.getMeshData().getExtents().bottom ,0))

                        # Move all the meshes of children so that toolhandles are shown in the correct place.
                        for node in result.getChildren():
                            if node.getMeshData():
                                extents = node.getMeshData().getExtents()
                                m = Matrix()
                                m.translate(-extents.center)
                                node.setMeshData(node.getMeshData().getTransformed(m))
                                node.translate(extents.center)
                    return result

        except OSError as e:
            Logger.log("e", str(e))

        Logger.log("w", "Unable to read file %s", file_name)
        return None #unable to read
예제 #3
0
    def _rotateCamera(self, x, y):
        camera = self._scene.getActiveCamera()
        if not camera or not camera.isEnabled():
            return

        self._scene.acquireLock()

        dx = math.radians(x * 180.0)
        dy = math.radians(y * 180.0)

        diff = camera.getPosition() - self._origin

        diff_flat = Vector(diff.x, 0.0, diff.z).getNormalized()
        try:
            new_angle = math.acos(diff_flat.dot(diff.getNormalized())) + dy
        except ValueError:
            return

        m = Matrix()
        m.setByRotationAxis(dx, Vector.Unit_Y)
        if new_angle < (math.pi / 2 - 0.01):
            m.rotateByAxis(dy, Vector.Unit_Y.cross(diff).normalize())

        n = diff.multiply(m)
        n += self._origin

        camera.setPosition(n)
        camera.lookAt(self._origin)

        self._scene.releaseLock()
예제 #4
0
 def read(self, file_name, storage_device): 
     extension = os.path.splitext(file_name)[1]
     if extension.lower() == self._supported_extension:
         loaded_workspace = SceneNode()
         mesh_handler = Application.getInstance().getMeshFileHandler()
         f = storage_device.openFile(file_name, "rt")
         
         tree = ET.parse(f)
         root = tree.getroot()
         if root.tag == "MeshLabProject":
             for group in root.findall("MeshGroup"):
                 for mesh in group.findall("MLMesh"): 
                     mesh_data = mesh_handler.read(mesh.get("filename"),Application.getInstance().getStorageDevice("local"))
                     mesh_data.setName(mesh.get("label"))
                     if mesh_data.getType() is MeshType.pointcloud:
                         mesh_node = PointCloudNode(loaded_workspace)
                     else:
                         mesh_node = SceneNode(loaded_workspace)
                     mesh_lines = mesh.findall("MLMatrix44")[0].text.splitlines()
                     mesh_matrix = Matrix()
                     mesh_matrix.setColumn(0,mesh_lines[1].split())
                     mesh_matrix.setColumn(1,mesh_lines[2].split())
                     mesh_matrix.setColumn(2,mesh_lines[3].split())
                     mesh_matrix.setColumn(3,mesh_lines[4].split())
                     mesh_node.setMeshData(mesh_data)
             return loaded_workspace
         else:
             Logger.log("e", "Unable to load file. It seems to be formated wrong.")
             return None
     else:
         return None # Can't do anything with provided extention
예제 #5
0
    def addArc(self, **kwargs):
        radius = kwargs["radius"]
        axis = kwargs["axis"]

        max_angle = kwargs.get("angle", math.pi * 2)
        center = kwargs.get("center", Vector(0, 0, 0))
        sections = kwargs.get("sections", 32)
        color = kwargs.get("color", None)

        if axis == Vector.Unit_Y:
            start = axis.cross(Vector.Unit_X).normalize() * radius
        else:
            start = axis.cross(Vector.Unit_Y).normalize() * radius

        angle_increment = max_angle / sections
        angle = 0

        point = start + center
        m = Matrix()
        while angle <= max_angle:
            self._mesh_data.addVertex(point.x, point.y, point.z)
            angle += angle_increment
            m.setByRotationAxis(angle, axis)
            point = start.multiply(m) + center
            self._mesh_data.addVertex(point.x, point.y, point.z)

            if color:
                self._mesh_data.setVertexColor(self._mesh_data.getVertexCount() - 2, color)
                self._mesh_data.setVertexColor(self._mesh_data.getVertexCount() - 1, color)
예제 #6
0
    def _updateTransformation(self):
        scale_and_mirror = self._scale * self._mirror
        self._transformation = Matrix.fromPositionOrientationScale(self._position, self._orientation, scale_and_mirror)

        if self._parent:
            parent_orientation = self._parent._getDerivedOrientation()
            if self._inherit_orientation:
                self._derived_orientation = parent_orientation * self._orientation
            else:
                self._derived_orientation = self._orientation

            # Sometimes the derived orientation can be None.
            # I've not been able to locate the cause of this, but this prevents it being an issue.
            if not self._derived_orientation:
                self._derived_orientation = Quaternion()

            parent_scale = self._parent._getDerivedScale()
            if self._inherit_scale:
                self._derived_scale = parent_scale.scale(scale_and_mirror)
            else:
                self._derived_scale = scale_and_mirror

            self._derived_position = parent_orientation.rotate(parent_scale.scale(self._position))
            self._derived_position += self._parent._getDerivedPosition()

            self._world_transformation = Matrix.fromPositionOrientationScale(self._derived_position, self._derived_orientation, self._derived_scale)
        else:
            self._derived_position = self._position
            self._derived_orientation = self._orientation
            self._derived_scale = scale_and_mirror
            self._world_transformation = self._transformation
예제 #7
0
    def readerRead(self, reader, file_name, **kwargs):
        try:
            results = reader.read(file_name)
            if results is not None:
                if type(results) is not list:
                    results = [results]

                for result in results:
                    if kwargs.get("center", True):
                        # If the result has a mesh and no children it needs to be centered
                        if result.getMeshData() and len(result.getChildren()) == 0:
                            extents = result.getMeshData().getExtents()
                            move_vector = Vector(extents.center.x, extents.center.y, extents.center.z)
                            result.setCenterPosition(move_vector)

                        # Move all the meshes of children so that toolhandles are shown in the correct place.
                        for node in result.getChildren():
                            if node.getMeshData():
                                extents = node.getMeshData().getExtents()
                                m = Matrix()
                                m.translate(-extents.center)
                                node.setMeshData(node.getMeshData().getTransformed(m))
                                node.translate(extents.center)
                return results

        except OSError as e:
            Logger.log("e", str(e))

        Logger.log("w", "Unable to read file %s", file_name)
        return None  # unable to read
예제 #8
0
파일: X3DReader.py 프로젝트: cederom/Cura
 def processTransform(self, node):
     rot = readRotation(node, "rotation", (0, 0, 1, 0)) # (angle, axisVactor) tuple
     trans = readVector(node, "translation", (0, 0, 0)) # Vector
     scale = readVector(node, "scale", (1, 1, 1)) # Vector
     center = readVector(node, "center", (0, 0, 0)) # Vector
     scale_orient = readRotation(node, "scaleOrientation", (0, 0, 1, 0)) # (angle, axisVactor) tuple
     
     # Store the previous transform; in Cura, the default matrix multiplication is in place        
     prev = Matrix(self.transform.getData()) # It's deep copy, I've checked
     
     # The rest of transform manipulation will be applied in place
     got_center = (center.x != 0 or center.y != 0 or center.z != 0)
     
     T = self.transform
     if trans.x != 0 or trans.y != 0 or trans.z !=0:
         T.translate(trans)
     if got_center:
         T.translate(center)
     if rot[0] != 0:
         T.rotateByAxis(*rot)
     if scale.x != 1 or scale.y != 1 or scale.z != 1:
         got_scale_orient = scale_orient[0] != 0
         if got_scale_orient:
             T.rotateByAxis(*scale_orient)
         # No scale by vector in place operation in UM
         S = Matrix()
         S.setByScaleVector(scale)
         T.multiply(S)
         if got_scale_orient:
             T.rotateByAxis(-scale_orient[0], scale_orient[1])
     if got_center:
         T.translate(-center)
         
     self.processChildNodes(node)
     self.transform = prev
예제 #9
0
def test_getterAndSetters():
    # Pretty much all of them are super simple, but it doesn't hurt to check them.
    camera = Camera()

    camera.setAutoAdjustViewPort(False)
    assert camera.getAutoAdjustViewPort() == False

    camera.setViewportWidth(12)
    assert camera.getViewportWidth() == 12

    camera.setViewportHeight(12)
    assert camera.getViewportHeight() == 12

    camera.setViewportSize(22, 22)
    assert camera.getViewportHeight() == 22
    assert camera.getViewportWidth() == 22

    camera.setWindowSize(9001, 9002)
    assert camera.getWindowSize() == (9001, 9002)

    camera.setPerspective(False)
    assert camera.isPerspective() == False

    matrix = Matrix()
    matrix.setPerspective(10, 20, 30, 40)
    camera.setProjectionMatrix(matrix)

    assert numpy.array_equal(camera.getProjectionMatrix().getData(), matrix.getData())
예제 #10
0
    def run(self):
        loading_message = Message(i18n_catalog.i18nc("Loading mesh message, {0} is file name", "Loading {0}").format(self._filename), lifetime = 0, dismissable = False)
        loading_message.setProgress(-1)
        loading_message.show()

        mesh = self._handler.read(self._filename, self._device)

        # Scale down to maximum bounds size if that is available
        if hasattr(Application.getInstance().getController().getScene(), "_maximum_bounds"):
            max_bounds = Application.getInstance().getController().getScene()._maximum_bounds
            bbox = mesh.getExtents()

            if max_bounds.width < bbox.width or max_bounds.height < bbox.height or max_bounds.depth < bbox.depth:
                largest_dimension = max(bbox.width, bbox.height, bbox.depth)

                scale_factor = 1.0
                if largest_dimension == bbox.width:
                    scale_factor = max_bounds.width / bbox.width
                elif largest_dimension == bbox.height:
                    scale_factor = max_bounds.height / bbox.height
                else:
                    scale_factor = max_bounds.depth / bbox.depth

                matrix = Matrix()
                matrix.setByScaleFactor(scale_factor)
                mesh = mesh.getTransformed(matrix)

        self.setResult(mesh)

        loading_message.hide()
        result_message = Message(i18n_catalog.i18nc("Finished loading mesh message, {0} is file name", "Loaded {0}").format(self._filename))
        result_message.show()
예제 #11
0
 def setCenterPosition(self, center: Vector):
     if self._mesh_data:
         m = Matrix()
         m.setByTranslation(-center)
         self._mesh_data = self._mesh_data.getTransformed(m).set(center_position=center)
     for child in self._children:
         child.setCenterPosition(center)
예제 #12
0
    def test_deepcopy(self):
        matrix = Matrix()

        # Set some data
        matrix.setRow(1, [1, 2, 3])
        matrix.setColumn(2, [3, 4, 5])

        copied_matrix = copy.deepcopy(matrix)
        assert copied_matrix == matrix
예제 #13
0
    def test_compare(self):
        matrix = Matrix()
        matrix2 = Matrix()

        assert matrix == matrix
        assert not matrix == "zomg"

        matrix._data = None
        matrix2._data = None
        assert matrix == matrix2
예제 #14
0
 def test_preMultiply(self):
     temp_matrix = Matrix()
     temp_matrix.setByTranslation(Vector(10,10,10))
     temp_matrix2 = Matrix()
     temp_matrix2.setByScaleFactor(0.5)
     temp_matrix.preMultiply(temp_matrix2)
     numpy.testing.assert_array_almost_equal(temp_matrix.getData(), numpy.array([[0.5,0,0,5],[0,0.5,0,5],[0,0,0.5,5],[0,0,0,1]]))
예제 #15
0
    def addDonut(self, inner_radius, outer_radius, width, center = Vector(0, 0, 0), sections = 32, color = None, angle = 0, axis = Vector.Unit_Y):
        vertices = []
        indices = []
        colors = []

        start = self.getVertexCount() #Starting index.

        for i in range(sections):
            v1 = start + i * 3 #Indices for each of the vertices we'll add for this section.
            v2 = v1 + 1
            v3 = v1 + 2
            v4 = v1 + 3
            v5 = v1 + 4
            v6 = v1 + 5

            if i+1 >= sections: # connect the end to the start
                v4 = start
                v5 = start + 1
                v6 = start + 2

            theta = i * math.pi / (sections / 2) #Angle of this piece around torus perimeter.
            c = math.cos(theta) #X-coordinate around torus perimeter.
            s = math.sin(theta) #Y-coordinate around torus perimeter.

            #One vertex on the inside perimeter, two on the outside perimiter (up and down).
            vertices.append( [inner_radius * c, inner_radius * s, 0] )
            vertices.append( [outer_radius * c, outer_radius * s, width] )
            vertices.append( [outer_radius * c, outer_radius * s, -width] )

            #Connect the vertices to the next segment.
            indices.append( [v1, v4, v5] )
            indices.append( [v2, v1, v5] )

            indices.append( [v2, v5, v6] )
            indices.append( [v3, v2, v6] )

            indices.append( [v3, v6, v4] )
            indices.append( [v1, v3, v4] )

            if color: #If we have a colour, add it to the vertices.
                colors.append( [color.r, color.g, color.b, color.a] )
                colors.append( [color.r, color.g, color.b, color.a] )
                colors.append( [color.r, color.g, color.b, color.a] )

        #Rotate the resulting torus around the specified axis.
        matrix = Matrix()
        matrix.setByRotationAxis(angle, axis)
        vertices = numpy.asarray(vertices, dtype = numpy.float32)
        vertices = vertices.dot(matrix.getData()[0:3, 0:3])
        vertices[:] += center.getData() #And translate to the desired position.

        self.addVertices(vertices)
        self.addIndices(numpy.asarray(indices, dtype = numpy.int32))
        self.addColors(numpy.asarray(colors, dtype = numpy.float32))
예제 #16
0
def test_transformMeshData():
    transformation_matrix = Matrix()
    transformation_matrix.setByTranslation(Vector(30, 20, 10))

    vertices = numpy.zeros((1, 3), dtype=numpy.float32)
    mesh_data = MeshData(vertices)

    transformed_mesh = mesh_data.getTransformed(transformation_matrix)

    assert transformed_mesh.getVertex(0)[0] == 30.
    assert transformed_mesh.getVertex(0)[1] == 20.
    assert transformed_mesh.getVertex(0)[2] == 10.
예제 #17
0
 def setUp(self):
     # Called before the first testfunction is executed
     self._scene = Scene()
     self._scene_object = SceneNode()
     self._scene_object2 = SceneNode()
     self._scene_object.addChild(self._scene_object2)
     self._scene.getRoot().addChild(self._scene_object)
     temp_matrix = Matrix()
     temp_matrix.setByTranslation(Vector(10, 10, 10))
     self._scene_object2.setLocalTransformation(deepcopy(temp_matrix))
     temp_matrix.setByScaleFactor(0.5)
     self._scene_object.setLocalTransformation(temp_matrix)
예제 #18
0
    def test_fromMatrix(self):
        m = Matrix()
        m.setByRotationAxis(math.pi / 2, Vector.Unit_Z)

        q1 = Quaternion.fromMatrix(m)

        q2 = Quaternion()
        q2.setByAngleAxis(math.pi / 2, Vector.Unit_Z)

        self.assertTrue(Float.fuzzyCompare(q1.x, q2.x, 1e-6))
        self.assertTrue(Float.fuzzyCompare(q1.y, q2.y, 1e-6))
        self.assertTrue(Float.fuzzyCompare(q1.z, q2.z, 1e-6))
        self.assertTrue(Float.fuzzyCompare(q1.w, q2.w, 1e-6))
예제 #19
0
 def translate(self, translation, transform_space = TransformSpace.Local):
     if not self._enabled:
         return
     translation_matrix = Matrix()
     translation_matrix.setByTranslation(translation)
     if transform_space == SceneNode.TransformSpace.Local:
         self._transformation.multiply(translation_matrix)
     elif transform_space == SceneNode.TransformSpace.Parent:
         self._transformation.preMultiply(translation_matrix)
     elif transform_space == SceneNode.TransformSpace.World:
         self._transformation.multiply(self._world_transformation.getInverse())
         self._transformation.multiply(translation_matrix)
         self._transformation.multiply(self._world_transformation)
     self._transformChanged()
예제 #20
0
    def __init__(self, parent: Optional["SceneNode"] = None, visible: bool = True, name: str = "") -> None:
        super().__init__()  # Call super to make multiple inheritance work.

        self._children = []     # type: List[SceneNode]
        self._mesh_data = None  # type: Optional[MeshData]

        # Local transformation (from parent to local)
        self._transformation = Matrix()  # type: Matrix

        # Convenience "components" of the transformation
        self._position = Vector()  # type: Vector
        self._scale = Vector(1.0, 1.0, 1.0)  # type: Vector
        self._shear = Vector(0.0, 0.0, 0.0)  # type: Vector
        self._mirror = Vector(1.0, 1.0, 1.0)  # type: Vector
        self._orientation = Quaternion()  # type: Quaternion

        # World transformation (from root to local)
        self._world_transformation = Matrix()  # type: Matrix

        # Convenience "components" of the world_transformation
        self._derived_position = Vector()  # type: Vector
        self._derived_orientation = Quaternion()  # type: Quaternion
        self._derived_scale = Vector()  # type: Vector

        self._parent = parent  # type: Optional[SceneNode]

        # Can this SceneNode be modified in any way?
        self._enabled = True  # type: bool
        # Can this SceneNode be selected in any way?
        self._selectable = False  # type: bool

        # Should the AxisAlignedBoundingBox be re-calculated?
        self._calculate_aabb = True  # type: bool

        # The AxisAligned bounding box.
        self._aabb = None  # type: Optional[AxisAlignedBox]
        self._bounding_box_mesh = None  # type: Optional[MeshData]

        self._visible = visible  # type: bool
        self._name = name  # type: str
        self._decorators = []  # type: List[SceneNodeDecorator]

        # Store custom settings to be compatible with Savitar SceneNode
        self._settings = {}  # type: Dict[str, Any]

        ## Signals
        self.parentChanged.connect(self._onParentChanged)

        if parent:
            parent.addChild(self)
예제 #21
0
    def _updateViewportGeometry(self, width, height):
        view_w = width * self._viewport_rect.width()
        view_h = height * self._viewport_rect.height()

        for camera in self._app.getController().getScene().getAllCameras():
            camera.setViewportSize(view_w, view_h)
            proj = Matrix()
            if camera.isPerspective():
                proj.setPerspective(30, view_w / view_h, 1, 500)
            else:
                proj.setOrtho(-view_w / 2, view_w / 2, -view_h / 2, view_h / 2, -500, 500)
            camera.setProjectionMatrix(proj)

        self._app.getRenderer().setViewportSize(view_w, view_h)
        self._app.getRenderer().setWindowSize(width, height)
예제 #22
0
    def __init__(self, parent = None, **kwargs):
        super().__init__()  # Call super to make multiple inheritance work.

        self._children = []     # type: List[SceneNode]
        self._mesh_data = None  # type: MeshData

        # Local transformation (from parent to local)
        self._transformation = Matrix()  # type: Matrix

        # Convenience "components" of the transformation
        self._position = Vector()  # type: Vector
        self._scale = Vector(1.0, 1.0, 1.0)  # type: Vector
        self._shear = Vector(0.0, 0.0, 0.0)  # type: Vector
        self._mirror = Vector(1.0, 1.0, 1.0)  # type: Vector
        self._orientation = Quaternion()  # type: Quaternion

        # World transformation (from root to local)
        self._world_transformation = Matrix()  # type: Matrix

        # Convenience "components" of the world_transformation
        self._derived_position = Vector()  # type: Vector
        self._derived_orientation = Quaternion()  # type: Quaternion
        self._derived_scale = Vector()  # type: Vector

        self._parent = parent  # type: Optional[SceneNode]

        # Can this SceneNode be modified in any way?
        self._enabled = True  # type: bool
        # Can this SceneNode be selected in any way?
        self._selectable = False  # type: bool

        # Should the AxisAlignedBounxingBox be re-calculated?
        self._calculate_aabb = True  # type: bool

        # The AxisAligned bounding box.
        self._aabb = None  # type: Optional[AxisAlignedBox]
        self._bounding_box_mesh = None  # type: Optional[MeshData]

        self._visible = kwargs.get("visible", True)  # type: bool
        self._name = kwargs.get("name", "")  # type: str
        self._decorators = []  # type: List[SceneNodeDecorator]

        ## Signals
        self.boundingBoxChanged.connect(self.calculateBoundingBoxMesh)
        self.parentChanged.connect(self._onParentChanged)

        if parent:
            parent.addChild(self)
예제 #23
0
def test_getExtentsTransposed():
    # Create a cube mesh at position 0,0,0
    builder = MeshBuilder()
    builder.addCube(20, 20, 20)
    mesh_data = builder.build()

    transformation_matrix = Matrix()
    transformation_matrix.setByTranslation(Vector(10, 10, 10))

    extents = mesh_data.getExtents(transformation_matrix)
    assert extents.width == 20
    assert extents.height == 20
    assert extents.depth == 20

    assert extents.maximum == Vector(20, 20, 20)
    assert extents.minimum == Vector(0, 0, 0)
예제 #24
0
    def __init__(self, node, translation = None, orientation = None, scale = None, shear = None, mirror = None):
        super().__init__()
        self._node = node

        self._old_translation = node.getPosition()
        self._old_orientation = node.getOrientation()
        self._old_scale = node.getScale()
        self._old_shear = node.getShear()
        self._old_transformation = node.getWorldTransformation()

        if translation:
            self._new_translation = translation
        else:
            self._new_translation = node.getPosition()

        if orientation:
            self._new_orientation = orientation
        else:
            self._new_orientation = node.getOrientation()

        if scale:
            self._new_scale = scale
        else:
            self._new_scale = node.getScale()

        if shear:
            self._new_shear = shear
        else:
            self._new_shear = node.getShear()

        self._new_mirror = Vector(1, 1, 1)
        self._new_transformation = Matrix()

        euler_orientation = self._new_orientation.toMatrix().getEuler()
        self._new_transformation.compose(scale = self._new_scale, shear = self._new_shear, angles = euler_orientation, translate = self._new_translation, mirror = self._new_mirror)
예제 #25
0
    def scale(self, scale: Vector, transform_space: int = TransformSpace.Local):
        if not self._enabled:
            return

        scale_matrix = Matrix()
        scale_matrix.setByScaleVector(scale)
        if transform_space == SceneNode.TransformSpace.Local:
            self._transformation.multiply(scale_matrix)
        elif transform_space == SceneNode.TransformSpace.Parent:
            self._transformation.preMultiply(scale_matrix)
        elif transform_space == SceneNode.TransformSpace.World:
            self._transformation.multiply(self._world_transformation.getInverse())
            self._transformation.multiply(scale_matrix)
            self._transformation.multiply(self._world_transformation)

        self._transformChanged()
예제 #26
0
 def resizeEvent(self, event):
     super().resizeEvent(event)
     
     w = event.size().width() * self.devicePixelRatio()
     h = event.size().height() * self.devicePixelRatio()
     for camera in self._app.getController().getScene().getAllCameras():
         camera.setViewportSize(w, h)
         proj = Matrix()
         if camera.isPerspective():
             proj.setPerspective(30, w/h, 1, 500)
         else:
             proj.setOrtho(-w / 2, w / 2, -h / 2, h / 2, -500, 500)
         camera.setProjectionMatrix(proj)
     self._preferences.setValue("general/window_width", event.size().width())
     self._preferences.setValue("general/window_height", event.size().height())
     self._app.getRenderer().setViewportSize(w, h)
예제 #27
0
    def resizeEvent(self, event):
        super().resizeEvent(event)
        
        w = event.size().width() * self.devicePixelRatio()
        h = event.size().height() * self.devicePixelRatio()
        for camera in self._app.getController().getScene().getAllCameras():
            camera.setViewportSize(w, h)
            proj = Matrix()
            if camera.isPerspective():
                proj.setPerspective(30, w/h, 1, 500)
            else:
                proj.setOrtho(-w / 2, w / 2, -h / 2, h / 2, -500, 500)
            camera.setProjectionMatrix(proj)

        self._app.getRenderer().setViewportSize(w, h)

        QMetaObject.invokeMethod(self, "_onWindowGeometryChanged", Qt.QueuedConnection);
예제 #28
0
    def setOrientation(self, orientation, transform_space = TransformSpace.Local):
        if not self._enabled or orientation == self._orientation:
            return

        new_transform_matrix = Matrix()
        if transform_space == SceneNode.TransformSpace.Local:
            orientation_matrix = orientation.toMatrix()
        if transform_space == SceneNode.TransformSpace.World:
            if self.getWorldOrientation() == orientation:
                return
            new_orientation = orientation * (self.getWorldOrientation() * self._orientation.getInverse()).getInverse()
            orientation_matrix = new_orientation.toMatrix()
        euler_angles = orientation_matrix.getEuler()

        new_transform_matrix.compose(scale = self._scale, angles = euler_angles, translate = self._position, shear = self._shear)
        self._transformation = new_transform_matrix
        self._transformChanged()
예제 #29
0
    def addPyramid(self, **kwargs):
        width = kwargs["width"]
        height = kwargs["height"]
        depth = kwargs["depth"]

        angle = math.radians(kwargs.get("angle", 0))
        axis = kwargs.get("axis", Vector.Unit_Y)

        center = kwargs.get("center", Vector(0, 0, 0))

        minW = -width / 2
        maxW = width / 2
        minD = -depth / 2
        maxD = depth / 2

        start = self._mesh_data.getVertexCount()

        matrix = Matrix()
        matrix.setByRotationAxis(angle, axis)
        verts = numpy.asarray([
            [minW, 0, maxD],
            [maxW, 0, maxD],
            [minW, 0, minD],
            [maxW, 0, minD],
            [0, height, 0]
        ], dtype=numpy.float32)
        verts = verts.dot(matrix.getData()[0:3,0:3])
        verts[:] += center.getData()
        self._mesh_data.addVertices(verts)

        indices = numpy.asarray([
            [start, start + 1, start + 4],
            [start + 1, start + 3, start + 4],
            [start + 3, start + 2, start + 4],
            [start + 2, start, start + 4],
            [start, start + 3, start + 1],
            [start, start + 2, start + 3]
        ], dtype=numpy.int32)
        self._mesh_data.addIndices(indices)

        color = kwargs.get("color", None)
        if color:
            vertex_count = self._mesh_data.getVertexCount()
            for i in range(1, 6):
                self._mesh_data.setVertexColor(vertex_count - i, color)
예제 #30
0
    def _rotateCamera(self, x, y):
        camera = self._scene.getActiveCamera()
        if not camera or not camera.isEnabled():
            return

        self._scene.acquireLock()

        dx = math.radians(x * 180.0)
        dy = math.radians(y * 180.0)

        diff = camera.getPosition() - self._origin
        my = Matrix()
        my.setByRotationAxis(dx, Vector.Unit_Y)

        mx = Matrix(my.getData())
        mx.rotateByAxis(dy, Vector.Unit_Y.cross(diff).normalized())

        n = diff.multiply(mx)

        try:
            angle = math.acos(Vector.Unit_Y.dot(n.normalized()))
        except ValueError:
            return

        if angle < 0.1 or angle > math.pi - 0.1:
            n = diff.multiply(my)

        n += self._origin

        camera.setPosition(n)
        camera.lookAt(self._origin)

        self._scene.releaseLock()
예제 #31
0
    def write(self, stream, nodes, mode=MeshWriter.OutputMode.BinaryMode):
        self._archive = None  # Reset archive
        archive = zipfile.ZipFile(stream,
                                  "w",
                                  compression=zipfile.ZIP_DEFLATED)
        try:
            model_file = zipfile.ZipInfo("3D/3dmodel.model")
            # Because zipfile is stupid and ignores archive-level compression settings when writing with ZipInfo.
            model_file.compress_type = zipfile.ZIP_DEFLATED

            # Create content types file
            content_types_file = zipfile.ZipInfo("[Content_Types].xml")
            content_types_file.compress_type = zipfile.ZIP_DEFLATED
            content_types = ET.Element("Types",
                                       xmlns=self._namespaces["content-types"])
            rels_type = ET.SubElement(
                content_types,
                "Default",
                Extension="rels",
                ContentType=
                "application/vnd.openxmlformats-package.relationships+xml")
            model_type = ET.SubElement(
                content_types,
                "Default",
                Extension="model",
                ContentType=
                "application/vnd.ms-package.3dmanufacturing-3dmodel+xml")

            # Create _rels/.rels file
            relations_file = zipfile.ZipInfo("_rels/.rels")
            relations_file.compress_type = zipfile.ZIP_DEFLATED
            relations_element = ET.Element(
                "Relationships", xmlns=self._namespaces["relationships"])
            model_relation_element = ET.SubElement(
                relations_element,
                "Relationship",
                Target="/3D/3dmodel.model",
                Id="rel0",
                Type=
                "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel")

            savitar_scene = Savitar.Scene()

            metadata_to_store = CuraApplication.getInstance().getController(
            ).getScene().getMetaData()

            for key, value in metadata_to_store.items():
                savitar_scene.setMetaDataEntry(key, value)

            current_time_string = datetime.datetime.now().strftime(
                "%Y-%m-%d %H:%M:%S")
            if "Application" not in metadata_to_store:
                # This might sound a bit strange, but this field should store the original application that created
                # the 3mf. So if it was already set, leave it to whatever it was.
                savitar_scene.setMetaDataEntry(
                    "Application",
                    CuraApplication.getInstance().getApplicationDisplayName())
            if "CreationDate" not in metadata_to_store:
                savitar_scene.setMetaDataEntry("CreationDate",
                                               current_time_string)

            savitar_scene.setMetaDataEntry("ModificationDate",
                                           current_time_string)

            transformation_matrix = Matrix()
            transformation_matrix._data[1, 1] = 0
            transformation_matrix._data[1, 2] = -1
            transformation_matrix._data[2, 1] = 1
            transformation_matrix._data[2, 2] = 0

            global_container_stack = Application.getInstance(
            ).getGlobalContainerStack()
            # Second step: 3MF defines the left corner of the machine as center, whereas cura uses the center of the
            # build volume.
            if global_container_stack:
                translation_vector = Vector(
                    x=global_container_stack.getProperty(
                        "machine_width", "value") / 2,
                    y=global_container_stack.getProperty(
                        "machine_depth", "value") / 2,
                    z=0)
                translation_matrix = Matrix()
                translation_matrix.setByTranslation(translation_vector)
                transformation_matrix.preMultiply(translation_matrix)

            root_node = UM.Application.Application.getInstance().getController(
            ).getScene().getRoot()
            for node in nodes:
                if node == root_node:
                    for root_child in node.getChildren():
                        savitar_node = self._convertUMNodeToSavitarNode(
                            root_child, transformation_matrix)
                        if savitar_node:
                            savitar_scene.addSceneNode(savitar_node)
                else:
                    savitar_node = self._convertUMNodeToSavitarNode(
                        node, transformation_matrix)
                    if savitar_node:
                        savitar_scene.addSceneNode(savitar_node)

            parser = Savitar.ThreeMFParser()
            scene_string = parser.sceneToString(savitar_scene)

            archive.writestr(model_file, scene_string)
            archive.writestr(
                content_types_file,
                b'<?xml version="1.0" encoding="UTF-8"?> \n' +
                ET.tostring(content_types))
            archive.writestr(
                relations_file, b'<?xml version="1.0" encoding="UTF-8"?> \n' +
                ET.tostring(relations_element))
        except Exception as e:
            Logger.logException("e", "Error writing zip file")
            self.setInformation(
                catalog.i18nc("@error:zip", "Error writing 3mf file."))
            return False
        finally:
            if not self._store_archive:
                archive.close()
            else:
                self._archive = archive

        return True
예제 #32
0
 def _getSVDRotationFromMatrix(self, matrix):
     result = Matrix()
     rotation_data = matrix.getData()[:3, :3]
     U, s, Vh = scipy.linalg.svd(rotation_data)
     result._data[:3, :3] = U.dot(Vh)
     return result
예제 #33
0
class Camera(SceneNode.SceneNode):
    def __init__(self, name, parent=None):
        super().__init__(parent)
        self._name = name
        self._projection_matrix = Matrix()
        self._projection_matrix.setOrtho(-5, 5, 5, -5, -100, 100)
        self._perspective = False
        self._viewport_width = 0
        self._viewport_height = 0
        self._window_width = 0
        self._window_height = 0

        self.setCalculateBoundingBox(False)

    ##  Get the projection matrix of this camera.
    def getProjectionMatrix(self):
        return copy.deepcopy(self._projection_matrix)

    def getViewportWidth(self):
        return self._viewport_width

    def setViewportWidth(self, width):
        self._viewport_width = width

    def setViewPortHeight(self, height):
        self._viewport_height = height

    def setViewportSize(self, width, height):
        self._viewport_width = width
        self._viewport_height = height

    def getViewportHeight(self):
        return self._viewport_height

    def setWindowSize(self, w, h):
        self._window_width = w
        self._window_height = h

    ##  Set the projection matrix of this camera.
    #   \param matrix The projection matrix to use for this camera.
    def setProjectionMatrix(self, matrix):
        self._projection_matrix = matrix

    def isPerspective(self):
        return self._perspective

    def setPerspective(self, pers):
        self._perspective = pers

    ##  Get a ray from the camera into the world.
    #
    #   This will create a ray from the camera's origin, passing through (x, y)
    #   on the near plane and continuing based on the projection matrix.
    #
    #   \param x The X coordinate on the near plane this ray should pass through.
    #   \param y The Y coordinate on the near plane this ray should pass through.
    #
    #   \return A Ray object representing a ray from the camera origin through X, Y.
    #
    #   \note The near-plane coordinates should be in normalized form, that is within (-1, 1).
    def getRay(self, x, y):
        window_x = ((x + 1) / 2) * self._window_width
        window_y = ((y + 1) / 2) * self._window_height
        view_x = (window_x / self._viewport_width) * 2 - 1
        view_y = (window_y / self._viewport_height) * 2 - 1

        inverted_projection = numpy.linalg.inv(
            self._projection_matrix.getData().copy())
        transformation = self.getWorldTransformation().getData()

        near = numpy.array([view_x, -view_y, -1.0, 1.0], dtype=numpy.float32)
        near = numpy.dot(inverted_projection, near)
        near = numpy.dot(transformation, near)
        near = near[0:3] / near[3]

        far = numpy.array([view_x, -view_y, 1.0, 1.0], dtype=numpy.float32)
        far = numpy.dot(inverted_projection, far)
        far = numpy.dot(transformation, far)
        far = far[0:3] / far[3]

        dir = far - near
        dir /= numpy.linalg.norm(dir)

        return Ray(self.getWorldPosition(), Vector(-dir[0], -dir[1], -dir[2]))

    ##  Project a 3D position onto the 2D view plane.
    def project(self, position):
        projection = self._projection_matrix
        view = self.getWorldTransformation().getInverse()

        position = position.preMultiply(view)
        position = position.preMultiply(projection)

        return position.x / position.z / 2.0, position.y / position.z / 2.0
예제 #34
0
    def _renderItem(self, item: Dict):
        transformation = item["transformation"]
        mesh = item["mesh"]

        # Do not render if there's no vertex (empty mesh)
        if mesh.getVertexCount() == 0:
            return

        normal_matrix = None
        if mesh.hasNormals():
            normal_matrix = Matrix(transformation.getData())
            normal_matrix.setRow(3, [0, 0, 0, 1])
            normal_matrix.setColumn(3, [0, 0, 0, 1])
            normal_matrix.invert()
            normal_matrix.transpose()

        self._shader.updateBindings(model_matrix=transformation,
                                    normal_matrix=normal_matrix)

        if item["uniforms"] is not None:
            self._shader.updateBindings(**item["uniforms"])

        vertex_buffer = OpenGL.getInstance().createVertexBuffer(mesh)
        vertex_buffer.bind()

        if self._render_range is None:
            index_buffer = OpenGL.getInstance().createIndexBuffer(mesh)
        else:
            # glDrawRangeElements does not work as expected and did not get the indices field working..
            # Now we're just uploading a clipped part of the array and the start index always becomes 0.
            index_buffer = OpenGL.getInstance().createIndexBuffer(
                mesh,
                force_recreate=True,
                index_start=self._render_range[0],
                index_stop=self._render_range[1])
        if index_buffer is not None:
            index_buffer.bind()

        self._shader.enableAttribute("a_vertex", "vector3f", 0)
        offset = mesh.getVertexCount() * 3 * 4

        if mesh.hasNormals():
            self._shader.enableAttribute("a_normal", "vector3f", offset)
            offset += mesh.getVertexCount() * 3 * 4

        if mesh.hasColors():
            self._shader.enableAttribute("a_color", "vector4f", offset)
            offset += mesh.getVertexCount() * 4 * 4

        if mesh.hasUVCoordinates():
            self._shader.enableAttribute("a_uvs", "vector2f", offset)
            offset += mesh.getVertexCount() * 2 * 4

        for attribute_name in mesh.attributeNames():
            attribute = mesh.getAttribute(attribute_name)
            self._shader.enableAttribute(attribute["opengl_name"],
                                         attribute["opengl_type"], offset)
            if attribute["opengl_type"] == "vector2f":
                offset += mesh.getVertexCount() * 2 * 4
            elif attribute["opengl_type"] == "vector4f":
                offset += mesh.getVertexCount() * 4 * 4
            elif attribute["opengl_type"] == "int":
                offset += mesh.getVertexCount() * 4
            elif attribute["opengl_type"] == "float":
                offset += mesh.getVertexCount() * 4
            else:
                Logger.log(
                    "e",
                    "Attribute with name [%s] uses non implemented type [%s]."
                    % (attribute["opengl_name"], attribute["opengl_type"]))
                self._shader.disableAttribute(attribute["opengl_name"])

        if mesh.hasIndices():
            if self._render_range is None:
                if self._render_mode == self.RenderMode.Triangles:
                    self._gl.glDrawElements(self._render_mode,
                                            mesh.getFaceCount() * 3,
                                            self._gl.GL_UNSIGNED_INT, None)
                else:
                    self._gl.glDrawElements(self._render_mode,
                                            mesh.getFaceCount(),
                                            self._gl.GL_UNSIGNED_INT, None)
            else:
                if self._render_mode == self.RenderMode.Triangles:
                    self._gl.glDrawRangeElements(
                        self._render_mode, self._render_range[0],
                        self._render_range[1],
                        self._render_range[1] - self._render_range[0],
                        self._gl.GL_UNSIGNED_INT, None)
                else:
                    self._gl.glDrawElements(
                        self._render_mode,
                        self._render_range[1] - self._render_range[0],
                        self._gl.GL_UNSIGNED_INT, None)
        else:
            self._gl.glDrawArrays(self._render_mode, 0, mesh.getVertexCount())

        self._shader.disableAttribute("a_vertex")
        self._shader.disableAttribute("a_normal")
        self._shader.disableAttribute("a_color")
        self._shader.disableAttribute("a_uvs")
        for attribute_name in mesh.attributeNames():
            attribute = mesh.getAttribute(attribute_name)
            self._shader.disableAttribute(attribute.get("opengl_name"))
        vertex_buffer.release()

        if index_buffer is not None:
            index_buffer.release()
    def read(self, file_name):
        result = []
        # The base object of 3mf is a zipped archive.
        try:
            archive = zipfile.ZipFile(file_name, "r")
            self._base_name = os.path.basename(file_name)
            parser = Savitar.ThreeMFParser()
            scene_3mf = parser.parse(archive.open("3D/3dmodel.model").read())
            self._unit = scene_3mf.getUnit()
            for node in scene_3mf.getSceneNodes():
                um_node = self._convertSavitarNodeToUMNode(node)
                if um_node is None:
                    continue
                # compensate for original center position, if object(s) is/are not around its zero position

                transform_matrix = Matrix()
                mesh_data = um_node.getMeshData()
                if mesh_data is not None:
                    extents = mesh_data.getExtents()
                    center_vector = Vector(extents.center.x, extents.center.y,
                                           extents.center.z)
                    transform_matrix.setByTranslation(center_vector)
                transform_matrix.multiply(um_node.getLocalTransformation())
                um_node.setTransformation(transform_matrix)

                global_container_stack = Application.getInstance(
                ).getGlobalContainerStack()

                # Create a transformation Matrix to convert from 3mf worldspace into ours.
                # First step: flip the y and z axis.
                transformation_matrix = Matrix()
                transformation_matrix._data[1, 1] = 0
                transformation_matrix._data[1, 2] = 1
                transformation_matrix._data[2, 1] = -1
                transformation_matrix._data[2, 2] = 0

                # Second step: 3MF defines the left corner of the machine as center, whereas cura uses the center of the
                # build volume.
                if global_container_stack:
                    translation_vector = Vector(
                        x=-global_container_stack.getProperty(
                            "machine_width", "value") / 2,
                        y=-global_container_stack.getProperty(
                            "machine_depth", "value") / 2,
                        z=0)
                    translation_matrix = Matrix()
                    translation_matrix.setByTranslation(translation_vector)
                    transformation_matrix.multiply(translation_matrix)

                # Third step: 3MF also defines a unit, whereas Cura always assumes mm.
                scale_matrix = Matrix()
                scale_matrix.setByScaleVector(
                    self._getScaleFromUnit(self._unit))
                transformation_matrix.multiply(scale_matrix)

                # Pre multiply the transformation with the loaded transformation, so the data is handled correctly.
                um_node.setTransformation(
                    um_node.getLocalTransformation().preMultiply(
                        transformation_matrix))

                # Check if the model is positioned below the build plate and honor that when loading project files.
                if um_node.getMeshData() is not None:
                    minimum_z_value = um_node.getMeshData().getExtents(
                        um_node.getWorldTransformation(
                        )).minimum.y  # y is z in transformation coordinates
                    if minimum_z_value < 0:
                        um_node.addDecorator(ZOffsetDecorator())
                        um_node.callDecoration("setZOffset", minimum_z_value)

                result.append(um_node)

        except Exception:
            Logger.logException("e", "An exception occurred in 3mf reader.")
            return []

        return result
    def _createMatrixFromTransformationString(self, transformation):
        if transformation == "":
            return Matrix()

        splitted_transformation = transformation.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]

        return temp_mat
예제 #37
0
    def updateSceneFromOptimizationResult(
            self, analysis: pywim.smartslice.result.Analysis):
        our_only_node = getPrintableNodes()[0]
        active_extruder = getNodeActiveExtruder(our_only_node)

        # TODO - Move this into a common class or function to apply an am.Config to GlobalStack/ExtruderStack
        if analysis.print_config.infill:

            infill_density = analysis.print_config.infill.density
            infill_pattern = analysis.print_config.infill.pattern

            if infill_pattern is None or infill_pattern == pywim.am.InfillType.unknown:
                infill_pattern = pywim.am.InfillType.grid

            infill_pattern_name = SmartSliceJobHandler.INFILL_SMARTSLICE_CURA[
                infill_pattern]

            extruder_dict = {
                "wall_line_count": analysis.print_config.walls,
                "top_layers": analysis.print_config.top_layers,
                "bottom_layers": analysis.print_config.bottom_layers,
                "infill_sparse_density": analysis.print_config.infill.density,
                "infill_pattern": infill_pattern_name
            }

            Logger.log("d",
                       "Optimized extruder settings: {}".format(extruder_dict))

            for key, value in extruder_dict.items():
                if value is not None:
                    active_extruder.setProperty(key,
                                                "value",
                                                value,
                                                set_from_cache=True)

            Application.getInstance().getMachineManager(
            ).forceUpdateAllSettings()
            self.optimizationResultAppliedToScene.emit()

        # Remove any modifier meshes which are present from a previous result
        mod_meshes = getModifierMeshes()
        if len(mod_meshes) > 0:
            op = GroupedOperation()
            for node in mod_meshes:
                node.addDecorator(SmartSliceRemovedDecorator())
                op.addOperation(RemoveSceneNodeOperation(node))
            op.push()
            Application.getInstance().getController().getScene(
            ).sceneChanged.emit(node)

        # Add in the new modifier meshes
        for modifier_mesh in analysis.modifier_meshes:
            # Building the scene node
            modifier_mesh_node = CuraSceneNode()
            modifier_mesh_node.setName("SmartSliceMeshModifier")
            modifier_mesh_node.setSelectable(True)
            modifier_mesh_node.setCalculateBoundingBox(True)

            # Use the data from the SmartSlice engine to translate / rotate / scale the mod mesh
            parent_transformation = our_only_node.getLocalTransformation()
            modifier_mesh_transform_matrix = parent_transformation.multiply(
                Matrix(modifier_mesh.transform))
            modifier_mesh_node.setTransformation(
                modifier_mesh_transform_matrix)

            # Building the mesh

            # # Preparing the data from pywim for MeshBuilder
            modifier_mesh_vertices = [[v.x, v.y, v.z]
                                      for v in modifier_mesh.vertices]
            modifier_mesh_indices = [[triangle.v1, triangle.v2, triangle.v3]
                                     for triangle in modifier_mesh.triangles]

            # Doing the actual build
            modifier_mesh_data = MeshBuilder()
            modifier_mesh_data.setVertices(
                numpy.asarray(modifier_mesh_vertices, dtype=numpy.float32))
            modifier_mesh_data.setIndices(
                numpy.asarray(modifier_mesh_indices, dtype=numpy.int32))
            modifier_mesh_data.calculateNormals()

            modifier_mesh_node.setMeshData(modifier_mesh_data.build())
            modifier_mesh_node.calculateBoundingBoxMesh()

            active_build_plate = Application.getInstance(
            ).getMultiBuildPlateModel().activeBuildPlate
            modifier_mesh_node.addDecorator(
                BuildPlateDecorator(active_build_plate))
            modifier_mesh_node.addDecorator(SliceableObjectDecorator())
            modifier_mesh_node.addDecorator(SmartSliceAddedDecorator())

            bottom = modifier_mesh_node.getBoundingBox().bottom

            z_offset_decorator = ZOffsetDecorator()
            z_offset_decorator.setZOffset(bottom)
            modifier_mesh_node.addDecorator(z_offset_decorator)

            stack = modifier_mesh_node.callDecoration("getStack")
            settings = stack.getTop()

            modifier_mesh_node_infill_pattern = SmartSliceJobHandler.INFILL_SMARTSLICE_CURA[
                modifier_mesh.print_config.infill.pattern]
            definition_dict = {
                "infill_mesh": True,
                "infill_pattern": modifier_mesh_node_infill_pattern,
                "infill_sparse_density":
                modifier_mesh.print_config.infill.density,
                "wall_line_count": modifier_mesh.print_config.walls,
                "top_layers": modifier_mesh.print_config.top_layers,
                "bottom_layers": modifier_mesh.print_config.bottom_layers,
            }
            Logger.log(
                "d",
                "Optimized modifier mesh settings: {}".format(definition_dict))

            for key, value in definition_dict.items():
                if value is not None:
                    definition = stack.getSettingDefinition(key)
                    new_instance = SettingInstance(definition, settings)
                    new_instance.setProperty("value", value)

                    new_instance.resetState(
                    )  # Ensure that the state is not seen as a user state.
                    settings.addInstance(new_instance)

            op = GroupedOperation()
            # First add node to the scene at the correct position/scale, before parenting, so the eraser mesh does not get scaled with the parent
            op.addOperation(
                AddSceneNodeOperation(
                    modifier_mesh_node,
                    Application.getInstance().getController().getScene().
                    getRoot()))
            op.addOperation(
                SetParentOperation(
                    modifier_mesh_node,
                    Application.getInstance().getController().getScene().
                    getRoot()))
            op.push()

            # emit changes and connect error tracker
            Application.getInstance().getController().getScene(
            ).sceneChanged.emit(modifier_mesh_node)
예제 #38
0
class SetTransformOperation(Operation.Operation):
    """Operation that translates, rotates and scales a node all at once."""
    def __init__(self,
                 node,
                 translation=None,
                 orientation=None,
                 scale=None,
                 shear=None,
                 mirror=None):
        """Creates the transform operation.
        
        Careful! No real input checking is done by this function. If you'd
        provide other transformations than respectively translation, orientation
        and scale in place for the translation, orientation and scale matrices,
        it could get confused.
        
        :param node: The scene node to transform.
        :param translation: A translation matrix to move the node with.
        :param orientation: An orientation matrix to rotate the node with.
        :param scale: A scaling matrix to resize the node with.
        """

        super().__init__()
        self._node = node

        self._old_translation = node.getPosition()
        self._old_orientation = node.getOrientation()
        self._old_scale = node.getScale()
        self._old_shear = node.getShear()
        self._old_transformation = node.getWorldTransformation()

        if translation:
            self._new_translation = translation
        else:
            self._new_translation = node.getPosition()

        if orientation:
            self._new_orientation = orientation
        else:
            self._new_orientation = node.getOrientation()

        if scale:
            self._new_scale = scale
        else:
            self._new_scale = node.getScale()

        if shear:
            self._new_shear = shear
        else:
            self._new_shear = node.getShear()

        self._new_mirror = Vector(1, 1, 1)
        self._new_transformation = Matrix()

        euler_orientation = self._new_orientation.toMatrix().getEuler()
        self._new_transformation.compose(scale=self._new_scale,
                                         shear=self._new_shear,
                                         angles=euler_orientation,
                                         translate=self._new_translation,
                                         mirror=self._new_mirror)

    def undo(self):
        """Undoes the transformation, restoring the node to the old state."""

        self._node.setTransformation(self._old_transformation)

    def redo(self):
        """Re-applies the transformation after it has been undone."""

        self._node.setTransformation(self._new_transformation)

    def mergeWith(self, other):
        """Merges this operation with another TransformOperation.
        
        This prevents the user from having to undo multiple operations if they
        were not his operations.
        
        You should ONLY merge this operation with an older operation. It is NOT
        symmetric.
        
        :param other: The older operation with which to merge this operation.
        :return: A combination of the two operations, or False if the merge
        failed.
        """

        if type(other) is not SetTransformOperation:
            return False
        if other._node != self._node:  # Must be on the same node.
            return False

        op = SetTransformOperation(self._node)
        op._old_transformation = other._old_transformation
        op._new_transformation = self._new_transformation
        return op

    def __repr__(self):
        """Returns a programmer-readable representation of this operation.
        
        A programmer-readable representation of this operation.
        """

        return "SetTransformOp.(node={0})".format(self._node)
예제 #39
0
    def _convertUMNodeToSavitarNode(self, um_node, transformation=Matrix()):
        """Convenience function that converts an Uranium SceneNode object to a SavitarSceneNode

        :returns: Uranium Scene node.
        """
        if not isinstance(um_node, SceneNode):
            return None

        active_build_plate_nr = CuraApplication.getInstance(
        ).getMultiBuildPlateModel().activeBuildPlate
        if um_node.callDecoration(
                "getBuildPlateNumber") != active_build_plate_nr:
            return

        savitar_node = Savitar.SceneNode()
        savitar_node.setName(um_node.getName())

        node_matrix = um_node.getLocalTransformation()

        matrix_string = self._convertMatrixToString(
            node_matrix.preMultiply(transformation))

        savitar_node.setTransformation(matrix_string)
        mesh_data = um_node.getMeshData()
        if mesh_data is not None:
            savitar_node.getMeshData().setVerticesFromBytes(
                mesh_data.getVerticesAsByteArray())
            indices_array = mesh_data.getIndicesAsByteArray()
            if indices_array is not None:
                savitar_node.getMeshData().setFacesFromBytes(indices_array)
            else:
                savitar_node.getMeshData().setFacesFromBytes(
                    numpy.arange(mesh_data.getVertices().size / 3,
                                 dtype=numpy.int32).tostring())

        # Handle per object settings (if any)
        stack = um_node.callDecoration("getStack")
        if stack is not None:
            changed_setting_keys = stack.getTop().getAllKeys()

            # Ensure that we save the extruder used for this object in a multi-extrusion setup
            if stack.getProperty("machine_extruder_count", "value") > 1:
                changed_setting_keys.add("extruder_nr")

            # Get values for all changed settings & save them.
            for key in changed_setting_keys:
                savitar_node.setSetting("cura:" + key,
                                        str(stack.getProperty(key, "value")))

        # Store the metadata.
        for key, value in um_node.metadata.items():
            savitar_node.setSetting(key, value)

        for child_node in um_node.getChildren():
            # only save the nodes on the active build plate
            if child_node.callDecoration(
                    "getBuildPlateNumber") != active_build_plate_nr:
                continue
            savitar_child_node = self._convertUMNodeToSavitarNode(child_node)
            if savitar_child_node is not None:
                savitar_node.addChild(savitar_child_node)

        return savitar_node