Move parse/write responsibility of copy/paste to 3MFWriter/3MFReader

CURA-7913
This commit is contained in:
c.lamboo 2023-08-07 13:41:01 +02:00
parent cc1b42e2fa
commit f8b3fb3d67
3 changed files with 48 additions and 34 deletions

View file

@ -7,8 +7,6 @@ from PyQt6.QtCore import QObject, QUrl, QMimeData
from PyQt6.QtGui import QDesktopServices from PyQt6.QtGui import QDesktopServices
from PyQt6.QtWidgets import QApplication from PyQt6.QtWidgets import QApplication
import pySavitar as Savitar
from UM.Event import CallFunctionEvent from UM.Event import CallFunctionEvent
from UM.FlameProfiler import pyqtSlot from UM.FlameProfiler import pyqtSlot
from UM.Math.Vector import Vector from UM.Math.Vector import Vector
@ -194,18 +192,13 @@ class CuraActions(QObject):
@pyqtSlot() @pyqtSlot()
def copy(self) -> None: def copy(self) -> None:
# Convert all selected objects to a Savitar scene
savitar_scene = Savitar.Scene()
mesh_writer = cura.CuraApplication.CuraApplication.getInstance().getMeshFileHandler().getWriter("3MFWriter") mesh_writer = cura.CuraApplication.CuraApplication.getInstance().getMeshFileHandler().getWriter("3MFWriter")
for scene_node in Selection.getAllSelectedObjects():
savitar_node = mesh_writer._convertUMNodeToSavitarNode(scene_node)
savitar_scene.addSceneNode(savitar_node)
# Convert the scene to a string # Get the selected nodes
parser = Savitar.ThreeMFParser() selected_objects = Selection.getAllSelectedObjects()
scene_string = parser.sceneToString(savitar_scene) # Serialize the nodes to a string
scene_string = mesh_writer.sceneNodesToString(selected_objects)
# Copy the scene to the clipboard # Put the string on the clipboard
QApplication.clipboard().setText(scene_string) QApplication.clipboard().setText(scene_string)
@pyqtSlot() @pyqtSlot()
@ -214,17 +207,9 @@ class CuraActions(QObject):
# Parse the scene from the clipboard # Parse the scene from the clipboard
scene_string = QApplication.clipboard().text() scene_string = QApplication.clipboard().text()
parser = Savitar.ThreeMFParser()
scene = parser.parse(scene_string)
# Convert the scene to scene nodes
nodes = []
mesh_reader = application.getMeshFileHandler().getReaderForFile(".3mf") mesh_reader = application.getMeshFileHandler().getReaderForFile(".3mf")
for savitar_node in scene.getSceneNodes(): nodes = mesh_reader.stringToSceneNodes(scene_string)
scene_node = mesh_reader._convertSavitarNodeToUMNode(savitar_node, "file_name")
if scene_node is None:
continue
nodes.append(scene_node)
# Find all fixed nodes, these are the nodes that should be avoided when arranging # Find all fixed nodes, these are the nodes that should be avoided when arranging
fixed_nodes = [] fixed_nodes = []

View file

@ -56,7 +56,8 @@ class ThreeMFReader(MeshReader):
def emptyFileHintSet(self) -> bool: def emptyFileHintSet(self) -> bool:
return self._empty_project return self._empty_project
def _createMatrixFromTransformationString(self, transformation: str) -> Matrix: @staticmethod
def _createMatrixFromTransformationString(transformation: str) -> Matrix:
if transformation == "": if transformation == "":
return Matrix() return Matrix()
@ -90,7 +91,8 @@ class ThreeMFReader(MeshReader):
return temp_mat return temp_mat
def _convertSavitarNodeToUMNode(self, savitar_node: Savitar.SceneNode, file_name: str = "") -> Optional[SceneNode]: @staticmethod
def _convertSavitarNodeToUMNode(savitar_node: Savitar.SceneNode, file_name: str = "") -> Optional[SceneNode]:
"""Convenience function that converts a SceneNode object (as obtained from libSavitar) to a scene node. """Convenience function that converts a SceneNode object (as obtained from libSavitar) to a scene node.
:returns: Scene node. :returns: Scene node.
@ -119,7 +121,7 @@ class ThreeMFReader(MeshReader):
pass pass
um_node.setName(node_name) um_node.setName(node_name)
um_node.setId(node_id) um_node.setId(node_id)
transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation()) transformation = ThreeMFReader._createMatrixFromTransformationString(savitar_node.getTransformation())
um_node.setTransformation(transformation) um_node.setTransformation(transformation)
mesh_builder = MeshBuilder() mesh_builder = MeshBuilder()
@ -138,7 +140,7 @@ class ThreeMFReader(MeshReader):
um_node.setMeshData(mesh_data) um_node.setMeshData(mesh_data)
for child in savitar_node.getChildren(): for child in savitar_node.getChildren():
child_node = self._convertSavitarNodeToUMNode(child) child_node = ThreeMFReader._convertSavitarNodeToUMNode(child)
if child_node: if child_node:
um_node.addChild(child_node) um_node.addChild(child_node)
@ -214,7 +216,7 @@ class ThreeMFReader(MeshReader):
CuraApplication.getInstance().getController().getScene().setMetaDataEntry(key, value) CuraApplication.getInstance().getController().getScene().setMetaDataEntry(key, value)
for node in scene_3mf.getSceneNodes(): for node in scene_3mf.getSceneNodes():
um_node = self._convertSavitarNodeToUMNode(node, file_name) um_node = ThreeMFReader._convertSavitarNodeToUMNode(node, file_name)
if um_node is None: if um_node is None:
continue continue
@ -300,8 +302,23 @@ class ThreeMFReader(MeshReader):
if unit is None: if unit is None:
unit = "millimeter" unit = "millimeter"
elif unit not in conversion_to_mm: elif unit not in conversion_to_mm:
Logger.log("w", "Unrecognised unit {unit} used. Assuming mm instead.".format(unit = unit)) Logger.log("w", "Unrecognised unit {unit} used. Assuming mm instead.".format(unit=unit))
unit = "millimeter" unit = "millimeter"
scale = conversion_to_mm[unit] scale = conversion_to_mm[unit]
return Vector(scale, scale, scale) return Vector(scale, scale, scale)
@staticmethod
def stringToSceneNodes(scene_string: str) -> List[SceneNode]:
parser = Savitar.ThreeMFParser()
scene = parser.parse(scene_string)
# Convert the scene to scene nodes
nodes = []
for savitar_node in scene.getSceneNodes():
scene_node = ThreeMFReader._convertSavitarNodeToUMNode(savitar_node, "file_name")
if scene_node is None:
continue
nodes.append(scene_node)
return nodes

View file

@ -55,11 +55,12 @@ class ThreeMFWriter(MeshWriter):
"cura": "http://software.ultimaker.com/xml/cura/3mf/2015/10" "cura": "http://software.ultimaker.com/xml/cura/3mf/2015/10"
} }
self._unit_matrix_string = self._convertMatrixToString(Matrix()) self._unit_matrix_string = ThreeMFWriter._convertMatrixToString(Matrix())
self._archive: Optional[zipfile.ZipFile] = None self._archive: Optional[zipfile.ZipFile] = None
self._store_archive = False self._store_archive = False
def _convertMatrixToString(self, matrix): @staticmethod
def _convertMatrixToString(matrix):
result = "" result = ""
result += str(matrix._data[0, 0]) + " " result += str(matrix._data[0, 0]) + " "
result += str(matrix._data[1, 0]) + " " result += str(matrix._data[1, 0]) + " "
@ -83,7 +84,8 @@ class ThreeMFWriter(MeshWriter):
""" """
self._store_archive = store_archive self._store_archive = store_archive
def _convertUMNodeToSavitarNode(self, um_node, transformation = Matrix()): @staticmethod
def _convertUMNodeToSavitarNode(um_node, transformation=Matrix()):
"""Convenience function that converts an Uranium SceneNode object to a SavitarSceneNode """Convenience function that converts an Uranium SceneNode object to a SavitarSceneNode
:returns: Uranium Scene node. :returns: Uranium Scene node.
@ -100,7 +102,7 @@ class ThreeMFWriter(MeshWriter):
node_matrix = um_node.getLocalTransformation() node_matrix = um_node.getLocalTransformation()
matrix_string = self._convertMatrixToString(node_matrix.preMultiply(transformation)) matrix_string = ThreeMFWriter._convertMatrixToString(node_matrix.preMultiply(transformation))
savitar_node.setTransformation(matrix_string) savitar_node.setTransformation(matrix_string)
mesh_data = um_node.getMeshData() mesh_data = um_node.getMeshData()
@ -133,7 +135,7 @@ class ThreeMFWriter(MeshWriter):
# only save the nodes on the active build plate # only save the nodes on the active build plate
if child_node.callDecoration("getBuildPlateNumber") != active_build_plate_nr: if child_node.callDecoration("getBuildPlateNumber") != active_build_plate_nr:
continue continue
savitar_child_node = self._convertUMNodeToSavitarNode(child_node) savitar_child_node = ThreeMFWriter._convertUMNodeToSavitarNode(child_node)
if savitar_child_node is not None: if savitar_child_node is not None:
savitar_node.addChild(savitar_child_node) savitar_node.addChild(savitar_child_node)
@ -221,7 +223,7 @@ class ThreeMFWriter(MeshWriter):
for node in nodes: for node in nodes:
if node == root_node: if node == root_node:
for root_child in node.getChildren(): for root_child in node.getChildren():
savitar_node = self._convertUMNodeToSavitarNode(root_child, transformation_matrix) savitar_node = ThreeMFWriter._convertUMNodeToSavitarNode(root_child, transformation_matrix)
if savitar_node: if savitar_node:
savitar_scene.addSceneNode(savitar_node) savitar_scene.addSceneNode(savitar_node)
else: else:
@ -303,9 +305,19 @@ class ThreeMFWriter(MeshWriter):
Logger.log("w", "Can't create snapshot when renderer not initialized.") Logger.log("w", "Can't create snapshot when renderer not initialized.")
return None return None
try: try:
snapshot = Snapshot.snapshot(width = 300, height = 300) snapshot = Snapshot.snapshot(width=300, height=300)
except: except:
Logger.logException("w", "Failed to create snapshot image") Logger.logException("w", "Failed to create snapshot image")
return None return None
return snapshot return snapshot
@staticmethod
def sceneNodesToString(scene_nodes: [SceneNode]) -> str:
savitar_scene = Savitar.Scene()
for scene_node in scene_nodes:
savitar_node = ThreeMFWriter._convertUMNodeToSavitarNode(scene_node)
savitar_scene.addSceneNode(savitar_node)
parser = Savitar.ThreeMFParser()
scene_string = parser.sceneToString(savitar_scene)
return scene_string