mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Use the node id as identifier
Now that libSavitar allows us to read the object id from the 3mf file, this id will be propagated as an id inside CuraSceneNodes and it will be used as an identifier to find the object that has to be refreshed. CURA-7333
This commit is contained in:
parent
5bdcc2e5ba
commit
8eb48672e1
2 changed files with 27 additions and 49 deletions
|
@ -1397,9 +1397,8 @@ class CuraApplication(QtApplication):
|
||||||
Logger.log("w", "Unable to reload data because we don't have a filename.")
|
Logger.log("w", "Unable to reload data because we don't have a filename.")
|
||||||
|
|
||||||
for file_name, nodes in objects_in_filename.items():
|
for file_name, nodes in objects_in_filename.items():
|
||||||
for object_index, node in enumerate(nodes):
|
for node in nodes:
|
||||||
job = ReadMeshJob(file_name)
|
job = ReadMeshJob(file_name)
|
||||||
job.object_to_be_reloaded = self._getObjectIndexInFile(file_name, node.getName()) # The object index in file to be loaded by this specific job
|
|
||||||
job._node = node # type: ignore
|
job._node = node # type: ignore
|
||||||
job.finished.connect(self._reloadMeshFinished)
|
job.finished.connect(self._reloadMeshFinished)
|
||||||
if has_merged_nodes:
|
if has_merged_nodes:
|
||||||
|
@ -1407,41 +1406,6 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
job.start()
|
job.start()
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _getObjectIndexInFile(file_name: str, node_name: str) -> int:
|
|
||||||
"""
|
|
||||||
This function extracts the index of the object inside a file. This is achieved by looking into the name
|
|
||||||
of the node. There are two possibilities:
|
|
||||||
* The node is named as filename.ext, filename.ext(1), filename.ext(2), etc, which maps to indices 0, 1, 2, ...
|
|
||||||
* The node is named as Object 1, Object 2, Object 3 etc, which maps to indices 0, 1, 2 ...
|
|
||||||
|
|
||||||
:param file_name: The name of the file where the node has been retrieved from
|
|
||||||
:param node_name: The name of the node as presented in the Scene
|
|
||||||
:return: The index of the node inside the file_name
|
|
||||||
"""
|
|
||||||
file_name = file_name.split("/")[-1] # Keep only the filename, without the path
|
|
||||||
node_int_index = 0
|
|
||||||
if file_name in node_name:
|
|
||||||
# if the file_name exists inside the node_name, remove it along with all parenthesis and spaces
|
|
||||||
node_str_index = re.sub(r'[() ]', '', node_name.replace(file_name, ""))
|
|
||||||
if node_str_index == "":
|
|
||||||
node_str_index = "0"
|
|
||||||
try:
|
|
||||||
node_int_index = int(node_str_index)
|
|
||||||
except ValueError:
|
|
||||||
Logger.warning("Object '{}' has an incorrect index '{}'.".format(node_name, node_str_index))
|
|
||||||
return 0
|
|
||||||
elif "Object " in node_name:
|
|
||||||
# if the nodes are named as Object 1, Object 2, etc, remove 'Object ' and keep only the number
|
|
||||||
node_str_index = node_name.replace("Object ", "")
|
|
||||||
try:
|
|
||||||
node_int_index = int(node_str_index) - 1
|
|
||||||
except ValueError:
|
|
||||||
Logger.warning("Object '{}' has an incorrect index '{}'.".format(node_name, node_str_index))
|
|
||||||
return 0
|
|
||||||
return node_int_index
|
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot("QStringList")
|
@pyqtSlot("QStringList")
|
||||||
def setExpandedCategories(self, categories: List[str]) -> None:
|
def setExpandedCategories(self, categories: List[str]) -> None:
|
||||||
categories = list(set(categories))
|
categories = list(set(categories))
|
||||||
|
@ -1616,16 +1580,29 @@ class CuraApplication(QtApplication):
|
||||||
fileLoaded = pyqtSignal(str)
|
fileLoaded = pyqtSignal(str)
|
||||||
fileCompleted = pyqtSignal(str)
|
fileCompleted = pyqtSignal(str)
|
||||||
|
|
||||||
def _reloadMeshFinished(self, job):
|
def _reloadMeshFinished(self, job: ReadMeshJob) -> None:
|
||||||
job_result = job.getResult()
|
"""
|
||||||
object_to_be_reloaded = job.object_to_be_reloaded
|
Function called whenever a ReadMeshJob finishes in the background. It reloads a specific node object in the
|
||||||
|
scene from its source file. The function gets all the nodes that exist in the file through the job result, and
|
||||||
|
then finds the scene node that it wants to refresh by its object id. Each job refreshes only one node.
|
||||||
|
|
||||||
|
:param job: The ReadMeshJob running in the background that reads all the meshes in a file
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
job_result = job.getResult() # nodes that exist inside the file read by this job
|
||||||
if len(job_result) == 0:
|
if len(job_result) == 0:
|
||||||
Logger.log("e", "Reloading the mesh failed.")
|
Logger.log("e", "Reloading the mesh failed.")
|
||||||
return
|
return
|
||||||
try: # In case the object has disappeared after reloading, log a warning and keep the old mesh in the scene
|
object_found = False
|
||||||
mesh_data = job_result[object_to_be_reloaded].getMeshData()
|
mesh_data = None
|
||||||
except IndexError:
|
# Find the node to be refreshed based on its id
|
||||||
Logger.warning("Object at index {} no longer exists! Keeping the old version in the scene.".format(object_to_be_reloaded))
|
for job_result_node in job_result:
|
||||||
|
if job_result_node.getId() == job._node.getId():
|
||||||
|
mesh_data = job_result_node.getMeshData()
|
||||||
|
object_found = True
|
||||||
|
break
|
||||||
|
if not object_found:
|
||||||
|
Logger.warning("The object with id {} no longer exists! Keeping the old version in the scene.".format(job_result_node.getId()))
|
||||||
return
|
return
|
||||||
if not mesh_data:
|
if not mesh_data:
|
||||||
Logger.log("w", "Could not find a mesh in reloaded node.")
|
Logger.log("w", "Could not find a mesh in reloaded node.")
|
||||||
|
|
|
@ -52,7 +52,6 @@ class ThreeMFReader(MeshReader):
|
||||||
self._root = None
|
self._root = None
|
||||||
self._base_name = ""
|
self._base_name = ""
|
||||||
self._unit = None
|
self._unit = None
|
||||||
self._object_count = 0 # Used to name objects as there is no node name yet.
|
|
||||||
|
|
||||||
def _createMatrixFromTransformationString(self, transformation: str) -> Matrix:
|
def _createMatrixFromTransformationString(self, transformation: str) -> Matrix:
|
||||||
if transformation == "":
|
if transformation == "":
|
||||||
|
@ -87,17 +86,20 @@ class ThreeMFReader(MeshReader):
|
||||||
## 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.
|
||||||
def _convertSavitarNodeToUMNode(self, savitar_node: Savitar.SceneNode, file_name: str = "") -> Optional[SceneNode]:
|
def _convertSavitarNodeToUMNode(self, savitar_node: Savitar.SceneNode, file_name: str = "") -> Optional[SceneNode]:
|
||||||
self._object_count += 1
|
|
||||||
|
|
||||||
node_name = savitar_node.getName()
|
node_name = savitar_node.getName()
|
||||||
|
node_id = savitar_node.getId()
|
||||||
if node_name == "":
|
if node_name == "":
|
||||||
node_name = "Object %s" % self._object_count
|
if file_name != "":
|
||||||
|
node_name = os.path.basename(file_name)
|
||||||
|
else:
|
||||||
|
node_name = "Object {}".format(node_id)
|
||||||
|
|
||||||
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
||||||
|
|
||||||
um_node = CuraSceneNode() # This adds a SettingOverrideDecorator
|
um_node = CuraSceneNode() # This adds a SettingOverrideDecorator
|
||||||
um_node.addDecorator(BuildPlateDecorator(active_build_plate))
|
um_node.addDecorator(BuildPlateDecorator(active_build_plate))
|
||||||
um_node.setName(node_name)
|
um_node.setName(node_name)
|
||||||
|
um_node.setId(node_id)
|
||||||
transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation())
|
transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation())
|
||||||
um_node.setTransformation(transformation)
|
um_node.setTransformation(transformation)
|
||||||
mesh_builder = MeshBuilder()
|
mesh_builder = MeshBuilder()
|
||||||
|
@ -169,7 +171,6 @@ class ThreeMFReader(MeshReader):
|
||||||
|
|
||||||
def _read(self, file_name: str) -> Union[SceneNode, List[SceneNode]]:
|
def _read(self, file_name: str) -> Union[SceneNode, List[SceneNode]]:
|
||||||
result = []
|
result = []
|
||||||
self._object_count = 0 # Used to name objects as there is no node name yet.
|
|
||||||
# The base object of 3mf is a zipped archive.
|
# The base object of 3mf is a zipped archive.
|
||||||
try:
|
try:
|
||||||
archive = zipfile.ZipFile(file_name, "r")
|
archive = zipfile.ZipFile(file_name, "r")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue