def test_toMatrix(self): q1 = Quaternion() q1.setByAngleAxis(math.pi / 2, Vector.Unit_Z) m1 = q1.toMatrix() m2 = Matrix() m2.setByRotationAxis(math.pi / 2, Vector.Unit_Z) self.assertTrue(Float.fuzzyCompare(m1.at(0, 0), m2.at(0, 0), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(0, 1), m2.at(0, 1), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(0, 2), m2.at(0, 2), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(0, 3), m2.at(0, 3), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(1, 0), m2.at(1, 0), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(1, 1), m2.at(1, 1), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(1, 2), m2.at(1, 2), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(1, 3), m2.at(1, 3), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(2, 0), m2.at(2, 0), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(2, 1), m2.at(2, 1), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(2, 2), m2.at(2, 2), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(2, 3), m2.at(2, 3), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(3, 0), m2.at(3, 0), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(3, 1), m2.at(3, 1), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(3, 2), m2.at(3, 2), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(3, 3), m2.at(3, 3), 1e-6))
def _createPolygon(self, layer_thickness: float, path: List[List[Union[float, int]]], extruder_offsets: List[float]) -> bool: countvalid = 0 for point in path: if point[8] > 0: countvalid += 1 if countvalid >= 2: # we know what to do now, no need to count further continue if countvalid < 2: return False try: self._layer_data_builder.addLayer(self._layer_number) self._layer_data_builder.setLayerHeight(self._layer_number, path[0][2]) self._layer_data_builder.setLayerThickness(self._layer_number, layer_thickness) this_layer = self._layer_data_builder.getLayer(self._layer_number) except ValueError: return False count = len(path) line_types = numpy.empty((count - 1, 1), numpy.int32) line_widths = numpy.empty((count - 1, 1), numpy.float32) line_thicknesses = numpy.empty((count - 1, 1), numpy.float32) line_feedrates = numpy.empty((count - 1, 1), numpy.float32) line_widths[:, 0] = 0.35 # Just a guess line_thicknesses[:, 0] = layer_thickness points = numpy.empty((count, 6), numpy.float32) extrusion_values = numpy.empty((count, 1), numpy.float32) i = 0 for point in path: matrix = Matrix([[point[0], point[1], point[2], 1]]) vector_matrix = Matrix([[0,0,1,1]]) matrix.rotateByAxis(radians(point[3]), Vector.Unit_X) matrix.rotateByAxis(radians(point[4]), Vector.Unit_Y) matrix.rotateByAxis(radians(point[5]), Vector.Unit_Z) vector_matrix.rotateByAxis(radians(point[3]), Vector.Unit_X) vector_matrix.rotateByAxis(radians(point[4]), Vector.Unit_Y) vector_matrix.rotateByAxis(radians(point[5]), Vector.Unit_Z) #points[i, :] = [point[0] + extruder_offsets[0], point[2], -point[1] - extruder_offsets[1]] points[i, :] = [matrix.at(0,0) + extruder_offsets[0], matrix.at(0,2), -matrix.at(0,1)-extruder_offsets[1], -vector_matrix.at(0,1), vector_matrix.at(0,2), -vector_matrix.at(0,0)] extrusion_values[i] = point[7] if i > 0: line_feedrates[i - 1] = point[6] line_types[i - 1] = point[8] if point[8] in [LayerPolygon.MoveCombingType, LayerPolygon.MoveRetractionType]: line_widths[i - 1] = 0.1 line_thicknesses[i - 1] = 0.0 # Travels are set as zero thickness lines else: line_widths[i - 1] = self._calculateLineWidth(points[i], points[i-1], extrusion_values[i], extrusion_values[i-1], layer_thickness) i += 1 this_poly = LayerPolygon(self._extruder_number, line_types, points, line_widths, line_thicknesses, line_feedrates) this_poly.buildCache() this_layer.polygons.append(this_poly) return True
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 setByMatrix(self, matrix: Matrix, ensure_unit_length: bool = False) -> None: """Set quaternion by providing a homogeneous (4x4) rotation matrix. :param matrix: 4x4 Matrix object :param ensure_unit_length: """ trace = matrix.at(0, 0) + matrix.at(1, 1) + matrix.at(2, 2) if trace > 0.0: self._data[0] = matrix.at(2, 1) - matrix.at(1, 2) self._data[1] = matrix.at(0, 2) - matrix.at(2, 0) self._data[2] = matrix.at(1, 0) - matrix.at(0, 1) self._data[3] = trace + 1 else: i = 0 if matrix.at(1, 1) > matrix.at(0, 0): i = 1 if matrix.at(2, 2) > matrix.at(i, i): i = 2 # Yes, this is repeated code. Writing it out however makes the code way # more readable than any magical index shifting. if i == 0: self._data[0] = matrix.at(0, 0) - matrix.at(1, 1) - matrix.at(2, 2) + 1.0 self._data[1] = matrix.at(0, 1) + matrix.at(1, 0) self._data[2] = matrix.at(0, 2) + matrix.at(2, 0) self._data[3] = matrix.at(2, 1) - matrix.at(1, 2) elif i == 1: self._data[0] = matrix.at(0, 1) + matrix.at(1, 0) self._data[1] = matrix.at(1, 1) - matrix.at(0, 0) - matrix.at(2, 2) + 1.0 self._data[2] = matrix.at(1, 2) + matrix.at(2, 1) self._data[3] = matrix.at(0, 2) - matrix.at(2, 0) else: self._data[0] = matrix.at(0, 2) + matrix.at(2, 0) self._data[1] = matrix.at(2, 1) + matrix.at(1, 2) self._data[1] = matrix.at(2, 2) - matrix.at(0, 0) - matrix.at(1, 1) + 1.0 self._data[3] = matrix.at(1, 0) - matrix.at(0, 1) if ensure_unit_length: self.normalize()
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
def test_invalidAt(self): matrix = Matrix() with pytest.raises(IndexError): matrix.at(12, 13)
def setByMatrix(self, matrix: Matrix, ensure_unit_length: bool = False) -> None: trace = matrix.at(0, 0) + matrix.at(1, 1) + matrix.at(2, 2) if trace > 0.0: self._data[0] = matrix.at(2, 1) - matrix.at(1, 2) self._data[1] = matrix.at(0, 2) - matrix.at(2, 0) self._data[2] = matrix.at(1, 0) - matrix.at(0, 1) self._data[3] = trace + 1 else: i = 0 if matrix.at(1, 1) > matrix.at(0, 0): i = 1 if matrix.at(2, 2) > matrix.at(i, i): i = 2 # Yes, this is repeated code. Writing it out however makes the code way # more readable than any magical index shifting. if i == 0: self._data[0] = matrix.at(0, 0) - matrix.at(1, 1) - matrix.at( 2, 2) + 1.0 self._data[1] = matrix.at(0, 1) + matrix.at(1, 0) self._data[2] = matrix.at(0, 2) + matrix.at(2, 0) self._data[3] = matrix.at(2, 1) - matrix.at(1, 2) elif i == 1: self._data[0] = matrix.at(0, 1) + matrix.at(1, 0) self._data[1] = matrix.at(1, 1) - matrix.at(0, 0) - matrix.at( 2, 2) + 1.0 self._data[2] = matrix.at(1, 2) + matrix.at(2, 1) self._data[3] = matrix.at(0, 2) - matrix.at(2, 0) else: self._data[0] = matrix.at(0, 2) + matrix.at(2, 0) self._data[1] = matrix.at(2, 1) + matrix.at(1, 2) self._data[1] = matrix.at(2, 2) - matrix.at(0, 0) - matrix.at( 1, 1) + 1.0 self._data[3] = matrix.at(1, 0) - matrix.at(0, 1) if ensure_unit_length: self.normalize()