예제 #1
0
    def getTransformed(self, transformation: Matrix) -> "MeshData":
        """Transform the meshdata, center and zero position by given Matrix

        :param transformation: 4x4 homogeneous transformation matrix
        """

        if self._vertices is not None:
            transformed_vertices = transformVertices(self._vertices,
                                                     transformation)
            transformed_normals = transformNormals(
                self._normals,
                transformation) if self._normals is not None else None

            transformation_matrix = transformation.getTransposed()
            if self._center_position is not None:
                center_position = self._center_position.multiply(
                    transformation_matrix)
            else:
                center_position = Reuse
            zero_position = self._zero_position.multiply(transformation_matrix)

            return self.set(vertices=transformed_vertices,
                            normals=transformed_normals,
                            center_position=center_position,
                            zero_position=zero_position)
        else:
            return MeshData(vertices=self._vertices)
예제 #2
0
def transformNormals(normals: numpy.ndarray,
                     transformation: Matrix) -> numpy.ndarray:
    """Transform an array of normals using a matrix

    :param normals: :type{numpy.ndarray} array of 3D normals
    :param transformation: a 4x4 matrix
    :return: :type{numpy.ndarray} the transformed normals

    :note This assumes the normals are untranslated unit normals, and returns the same.
    """

    data = numpy.pad(normals, ((0, 0), (0, 1)),
                     "constant",
                     constant_values=(0.0, 0.0))

    # Get the translation from the transformation so we can cancel it later.
    translation = transformation.getTranslation()

    # Transform the normals so they get the proper rotation
    data = data.dot(transformation.getTransposed().getData())
    data += transformation.getData()[:, 3]
    data = data[:, 0:3]

    # Cancel the translation since normals should always go from origin to a point on the unit sphere.
    data[:] -= translation.getData()

    # Re-normalize the normals, since the transformation can contain scaling.
    lengths = numpy.linalg.norm(data, axis=1)
    lengths[lengths == 0] = 1
    data[:, 0] /= lengths
    data[:, 1] /= lengths
    data[:, 2] /= lengths

    return data
예제 #3
0
 def test_transposed(self):
     temp_matrix = Matrix()
     temp_matrix.setByTranslation(Vector(10, 10, 10))
     temp_matrix = temp_matrix.getTransposed()
     numpy.testing.assert_array_almost_equal(
         temp_matrix.getData(),
         numpy.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0],
                      [10, 10, 10, 1]]))
예제 #4
0
def transformVertices(vertices: numpy.ndarray,
                      transformation: Matrix) -> numpy.ndarray:
    data = numpy.pad(vertices, ((0, 0), (0, 1)),
                     "constant",
                     constant_values=(0.0, 0.0))
    data = data.dot(transformation.getTransposed().getData())
    data += transformation.getData()[:, 3]
    data = data[:, 0:3]
    return data
예제 #5
0
def transformVertices(vertices: numpy.ndarray, transformation: Matrix) -> numpy.ndarray:
    """Transform an array of vertices using a matrix

    :param vertices: :type{numpy.ndarray} array of 3D vertices
    :param transformation: a 4x4 matrix
    :return: :type{numpy.ndarray} the transformed vertices
    """

    data = numpy.pad(vertices, ((0, 0), (0, 1)), "constant", constant_values=(0.0, 0.0))
    data = data.dot(transformation.getTransposed().getData())
    data += transformation.getData()[:, 3]
    data = data[:, 0:3]
    return data
예제 #6
0
    def getTransformed(self, transformation: Matrix) -> "MeshData":
        if self._vertices is not None:
            transformed_vertices = transformVertices(self._vertices, transformation)
            transformed_normals = transformNormals(self._normals, transformation) if self._normals is not None else None

            transformation_matrix = transformation.getTransposed()
            if self._center_position is not None:
                center_position = self._center_position.multiply(transformation_matrix)
            else:
                center_position = Reuse
            zero_position = self._zero_position.multiply(transformation_matrix)

            return self.set(vertices=transformed_vertices, normals=transformed_normals, center_position=center_position, zero_position=zero_position)
        else:
            return MeshData(vertices = self._vertices)
예제 #7
0
    def getTransformed(self, transformation: Matrix) -> "MeshData":
        if self._vertices is not None:
            transformed_vertices = transformVertices(self._vertices, transformation)
            transformed_normals = transformNormals(self._normals, transformation) if self._normals is not None else None

            transformation_matrix = transformation.getTransposed()
            if self._center_position is not None:
                center_position = self._center_position.multiply(transformation_matrix)
            else:
                center_position = Reuse
            zero_position = self._zero_position.multiply(transformation_matrix)

            return self.set(vertices=transformed_vertices, normals=transformed_normals, center_position=center_position, zero_position=zero_position)
        else:
            return MeshData(vertices = self._vertices)
예제 #8
0
def transformNormals(normals: numpy.ndarray, transformation: Matrix) -> numpy.ndarray:
    data = numpy.pad(normals, ((0, 0), (0, 1)), "constant", constant_values=(0.0, 0.0))

    # Get the translation from the transformation so we can cancel it later.
    translation = transformation.getTranslation()

    # Transform the normals so they get the proper rotation
    data = data.dot(transformation.getTransposed().getData())
    data += transformation.getData()[:, 3]
    data = data[:, 0:3]

    # Cancel the translation since normals should always go from origin to a point on the unit sphere.
    data[:] -= translation.getData()

    # Re-normalize the normals, since the transformation can contain scaling.
    lengths = numpy.linalg.norm(data, axis = 1)
    data[:, 0] /= lengths
    data[:, 1] /= lengths
    data[:, 2] /= lengths

    return data
예제 #9
0
def transformNormals(normals: numpy.ndarray, transformation: Matrix) -> numpy.ndarray:
    data = numpy.pad(normals, ((0, 0), (0, 1)), "constant", constant_values=(0.0, 0.0))

    # Get the translation from the transformation so we can cancel it later.
    translation = transformation.getTranslation()

    # Transform the normals so they get the proper rotation
    data = data.dot(transformation.getTransposed().getData())
    data += transformation.getData()[:, 3]
    data = data[:, 0:3]

    # Cancel the translation since normals should always go from origin to a point on the unit sphere.
    data[:] -= translation.getData()

    # Re-normalize the normals, since the transformation can contain scaling.
    lengths = numpy.linalg.norm(data, axis = 1)
    data[:, 0] /= lengths
    data[:, 1] /= lengths
    data[:, 2] /= lengths

    return data
예제 #10
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  
예제 #11
0
 def test_transposed(self):
     temp_matrix = Matrix()  
     temp_matrix.setByTranslation(Vector(10,10,10))
     temp_matrix = temp_matrix.getTransposed()
     numpy.testing.assert_array_almost_equal(temp_matrix.getData(), numpy.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[10,10,10,1]]))
예제 #12
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
예제 #13
0
def transformVertices(vertices: numpy.ndarray, transformation: Matrix) -> numpy.ndarray:
    data = numpy.pad(vertices, ((0, 0), (0, 1)), "constant", constant_values=(0.0, 0.0))
    data = data.dot(transformation.getTransposed().getData())
    data += transformation.getData()[:, 3]
    data = data[:, 0:3]
    return data