From e905ac9d331f0e39a14ca79fadbaed46137d16d3 Mon Sep 17 00:00:00 2001 From: Saumya Jain Date: Thu, 7 Mar 2024 16:13:19 +0100 Subject: [PATCH 1/8] adding a lock for the create snapshot CURA-11650 --- plugins/3MFWriter/ThreeMFWriter.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index 1c14c37cfd..bf6378c4c4 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import json import re +import threading from typing import Optional, cast, List, Dict, Pattern, Set @@ -65,6 +66,7 @@ class ThreeMFWriter(MeshWriter): self._unit_matrix_string = ThreeMFWriter._convertMatrixToString(Matrix()) self._archive: Optional[zipfile.ZipFile] = None self._store_archive = False + self._lock = threading.Lock() @staticmethod def _convertMatrixToString(matrix): @@ -423,6 +425,7 @@ class ThreeMFWriter(MeshWriter): @call_on_qt_thread # must be called from the main thread because of OpenGL def _createSnapshot(self): Logger.log("d", "Creating thumbnail image...") + self._lock.aquire() if not CuraApplication.getInstance().isVisible: Logger.log("w", "Can't create snapshot when renderer not initialized.") return None @@ -431,6 +434,7 @@ class ThreeMFWriter(MeshWriter): except: Logger.logException("w", "Failed to create snapshot image") return None + finally: self._lock.release() return snapshot From dc031c46748ebecae471e09e99c24a1f8b238e1e Mon Sep 17 00:00:00 2001 From: Saumya Jain Date: Thu, 7 Mar 2024 16:35:19 +0100 Subject: [PATCH 2/8] spelling of acquire CURA-11650 --- plugins/3MFWriter/ThreeMFWriter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index bf6378c4c4..d33f0e374a 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -425,7 +425,7 @@ class ThreeMFWriter(MeshWriter): @call_on_qt_thread # must be called from the main thread because of OpenGL def _createSnapshot(self): Logger.log("d", "Creating thumbnail image...") - self._lock.aquire() + self._lock.acquire() if not CuraApplication.getInstance().isVisible: Logger.log("w", "Can't create snapshot when renderer not initialized.") return None From ebe5da7f0e5aac892e811a53595f99c47f0bdcd3 Mon Sep 17 00:00:00 2001 From: Saumya Jain Date: Fri, 8 Mar 2024 13:43:43 +0100 Subject: [PATCH 3/8] added creating snapshot if not created till 10 attempts refractored the snapshot.py CURA-11650 --- cura/Snapshot.py | 66 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/cura/Snapshot.py b/cura/Snapshot.py index f94b3ff42e..77cb9ea29c 100644 --- a/cura/Snapshot.py +++ b/cura/Snapshot.py @@ -21,23 +21,31 @@ from UM.Scene.SceneNode import SceneNode from UM.Qt.QtRenderer import QtRenderer class Snapshot: + + DEFAULT_WIDTH_HEIGHT = 300 + MAX_RENDER_DISTANCE = 10000 + BOUND_BOX_FACTOR = 1.75 + CAMERA_FOVY = 30 + ATTEMPTS_FOR_SNAPSHOT = 10 + @staticmethod - def getImageBoundaries(image: QImage): - # Look at the resulting image to get a good crop. - # Get the pixels as byte array + def getNonZeroPixels(image: QImage): pixel_array = image.bits().asarray(image.sizeInBytes()) width, height = image.width(), image.height() - # Convert to numpy array, assume it's 32 bit (it should always be) pixels = numpy.frombuffer(pixel_array, dtype=numpy.uint8).reshape([height, width, 4]) # Find indices of non zero pixels - nonzero_pixels = numpy.nonzero(pixels) + return numpy.nonzero(pixels) + + @staticmethod + def getImageBoundaries(image: QImage): + nonzero_pixels = Snapshot.getNonZeroPixels(image) min_y, min_x, min_a_ = numpy.amin(nonzero_pixels, axis=1) # type: ignore max_y, max_x, max_a_ = numpy.amax(nonzero_pixels, axis=1) # type: ignore return min_x, max_x, min_y, max_y @staticmethod - def isometricSnapshot(width: int = 300, height: int = 300, *, node: Optional[SceneNode] = None) -> Optional[QImage]: + def isometricSnapshot(width: int = DEFAULT_WIDTH_HEIGHT, height: int = DEFAULT_WIDTH_HEIGHT, *, node: Optional[SceneNode] = None) -> Optional[QImage]: """ Create an isometric snapshot of the scene. @@ -112,22 +120,25 @@ class Snapshot: return render_pass.getOutput() + @staticmethod + def isNodeRenderable(node): + return not getattr(node, "_outside_buildarea", False) and node.callDecoration( + "isSliceable") and node.getMeshData() and node.isVisible() and not node.callDecoration( + "isNonThumbnailVisibleMesh") + @staticmethod def nodeBounds(root_node: SceneNode) -> Optional[AxisAlignedBox]: axis_aligned_box = None for node in DepthFirstIterator(root_node): - if not getattr(node, "_outside_buildarea", False): - if node.callDecoration( - "isSliceable") and node.getMeshData() and node.isVisible() and not node.callDecoration( - "isNonThumbnailVisibleMesh"): - if axis_aligned_box is None: - axis_aligned_box = node.getBoundingBox() - else: - axis_aligned_box = axis_aligned_box + node.getBoundingBox() + if Snapshot.isNodeRenderable(node): + if axis_aligned_box is None: + axis_aligned_box = node.getBoundingBox() + else: + axis_aligned_box = axis_aligned_box + node.getBoundingBox() return axis_aligned_box @staticmethod - def snapshot(width = 300, height = 300): + def snapshot(width = DEFAULT_WIDTH_HEIGHT, height = DEFAULT_WIDTH_HEIGHT, number_of_attempts = ATTEMPTS_FOR_SNAPSHOT): """Return a QImage of the scene Uses PreviewPass that leaves out some elements Aspect ratio assumes a square @@ -163,13 +174,13 @@ class Snapshot: looking_from_offset = Vector(-1, 1, 2) if size > 0: # determine the watch distance depending on the size - looking_from_offset = looking_from_offset * size * 1.75 + looking_from_offset = looking_from_offset * size * Snapshot.BOUND_BOX_FACTOR camera.setPosition(look_at + looking_from_offset) camera.lookAt(look_at) satisfied = False size = None - fovy = 30 + fovy = Snapshot.CAMERA_FOVY while not satisfied: if size is not None: @@ -182,11 +193,19 @@ class Snapshot: preview_pass.setCamera(camera) preview_pass.render() pixel_output = preview_pass.getOutput() - try: - min_x, max_x, min_y, max_y = Snapshot.getImageBoundaries(pixel_output) - except (ValueError, AttributeError): - Logger.logException("w", "Failed to crop the snapshot!") + if Snapshot._prereadSnapshot(pixel_output): + try: + min_x, max_x, min_y, max_y = Snapshot.getImageBoundaries(pixel_output) + except (ValueError, AttributeError) as e: + Logger.logException("w", f"Failed to crop the snapshot! {e}") + return None + elif number_of_attempts == 0: + Logger.warning( f"Failed to crop the snapshot even after 10 attempts!") return None + else: + number_of_attempts = number_of_attempts - 1 + Logger.info("Trying to get the snapshot again.") + return Snapshot.snapshot(width, height, number_of_attempts) size = max((max_x - min_x) / render_width, (max_y - min_y) / render_height) if size > 0.5 or satisfied: @@ -211,3 +230,8 @@ class Snapshot: transformMode = QtCore.Qt.TransformationMode.SmoothTransformation) return scaled_image + + @staticmethod + def _prereadSnapshot(snap = QImage): + nonzero_pixels = Snapshot.getNonZeroPixels(snap) + return all(len(nonzero_pixels[i]) != 0 for i in range(3)) \ No newline at end of file From 28997b0b14f9a0cab368e7b2dbec985d6e70161d Mon Sep 17 00:00:00 2001 From: Saumya Jain <70144862+saumyaj3@users.noreply.github.com> Date: Fri, 8 Mar 2024 16:38:57 +0100 Subject: [PATCH 4/8] Update cura/Snapshot.py Co-authored-by: Casper Lamboo --- cura/Snapshot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Snapshot.py b/cura/Snapshot.py index 77cb9ea29c..920caa5501 100644 --- a/cura/Snapshot.py +++ b/cura/Snapshot.py @@ -200,7 +200,7 @@ class Snapshot: Logger.logException("w", f"Failed to crop the snapshot! {e}") return None elif number_of_attempts == 0: - Logger.warning( f"Failed to crop the snapshot even after 10 attempts!") + Logger.warning( f"Failed to crop the snapshot even after {Snapshot.ATTEMPTS_FOR_SNAPSHOT} attempts!") return None else: number_of_attempts = number_of_attempts - 1 From c4881641c3c50add2bfe0b991bc264c82168fb25 Mon Sep 17 00:00:00 2001 From: Saumya Jain Date: Fri, 8 Mar 2024 16:44:03 +0100 Subject: [PATCH 5/8] review comments fixing CURA-11650 --- cura/Snapshot.py | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/cura/Snapshot.py b/cura/Snapshot.py index 920caa5501..9b91cdef1c 100644 --- a/cura/Snapshot.py +++ b/cura/Snapshot.py @@ -193,19 +193,16 @@ class Snapshot: preview_pass.setCamera(camera) preview_pass.render() pixel_output = preview_pass.getOutput() - if Snapshot._prereadSnapshot(pixel_output): - try: - min_x, max_x, min_y, max_y = Snapshot.getImageBoundaries(pixel_output) - except (ValueError, AttributeError) as e: - Logger.logException("w", f"Failed to crop the snapshot! {e}") + try: + min_x, max_x, min_y, max_y = Snapshot.getImageBoundaries(pixel_output) + except (ValueError, AttributeError) as e: + if number_of_attempts == 0: + Logger.warning( f"Failed to crop the snapshot even after {Snapshot.ATTEMPTS_FOR_SNAPSHOT} attempts!") return None - elif number_of_attempts == 0: - Logger.warning( f"Failed to crop the snapshot even after {Snapshot.ATTEMPTS_FOR_SNAPSHOT} attempts!") - return None - else: - number_of_attempts = number_of_attempts - 1 - Logger.info("Trying to get the snapshot again.") - return Snapshot.snapshot(width, height, number_of_attempts) + else: + number_of_attempts = number_of_attempts - 1 + Logger.info("Trying to get the snapshot again.") + return Snapshot.snapshot(width, height, number_of_attempts) size = max((max_x - min_x) / render_width, (max_y - min_y) / render_height) if size > 0.5 or satisfied: @@ -230,8 +227,3 @@ class Snapshot: transformMode = QtCore.Qt.TransformationMode.SmoothTransformation) return scaled_image - - @staticmethod - def _prereadSnapshot(snap = QImage): - nonzero_pixels = Snapshot.getNonZeroPixels(snap) - return all(len(nonzero_pixels[i]) != 0 for i in range(3)) \ No newline at end of file From 64bb69c48f46f8ebefa5e7d640b60bf2cd964f70 Mon Sep 17 00:00:00 2001 From: Saumya Jain Date: Fri, 8 Mar 2024 16:46:58 +0100 Subject: [PATCH 6/8] removing magic number Adding max render distance CURA-11650 --- cura/Snapshot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Snapshot.py b/cura/Snapshot.py index 9b91cdef1c..65a51539cc 100644 --- a/cura/Snapshot.py +++ b/cura/Snapshot.py @@ -100,8 +100,8 @@ class Snapshot: camera_width / 2, -camera_height / 2, camera_height / 2, - -10000, - 10000 + -Snapshot.MAX_RENDER_DISTANCE, + Snapshot.MAX_RENDER_DISTANCE ) camera.setPerspective(False) camera.setProjectionMatrix(ortho_matrix) From 0da0c720dd6489339dd1f08357f455cff34805bc Mon Sep 17 00:00:00 2001 From: Saumya Jain Date: Mon, 11 Mar 2024 11:04:51 +0100 Subject: [PATCH 7/8] Changed names of materials as per materials list Removed materials with GUID not found CURA-11710 --- .../Models/MaterialOutputModel.py | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/cura/PrinterOutput/Models/MaterialOutputModel.py b/cura/PrinterOutput/Models/MaterialOutputModel.py index cb5da7f8be..91210a6a0a 100644 --- a/cura/PrinterOutput/Models/MaterialOutputModel.py +++ b/cura/PrinterOutput/Models/MaterialOutputModel.py @@ -10,8 +10,8 @@ class MaterialOutputModel(QObject): def __init__(self, guid: Optional[str], type: str, color: str, brand: str, name: str, parent = None) -> None: super().__init__(parent) - name, guid = MaterialOutputModel.getMaterialFromDefinition(guid,type, brand, name) - self._guid =guid + name, guid = MaterialOutputModel.getMaterialFromDefinition(guid, type, brand, name) + self._guid = guid self._type = type self._color = color self._brand = brand @@ -24,23 +24,21 @@ class MaterialOutputModel(QObject): @staticmethod def getMaterialFromDefinition(guid, type, brand, name): - _MATERIAL_MAP = { "abs" :{"name" :"abs_175" ,"guid": "2780b345-577b-4a24-a2c5-12e6aad3e690"}, - "abs-wss1" :{"name" :"absr_175" ,"guid": "88c8919c-6a09-471a-b7b6-e801263d862d"}, - "asa" :{"name" :"asa_175" ,"guid": "416eead4-0d8e-4f0b-8bfc-a91a519befa5"}, - "nylon-cf" :{"name" :"cffpa_175" ,"guid": "85bbae0e-938d-46fb-989f-c9b3689dc4f0"}, - "nylon12-cf": {"name": "nylon12-cf_175", "guid": "3c6f2877-71cc-4760-84e6-4b89ab243e3b"}, - "nylon" :{"name" :"nylon_175" ,"guid": "283d439a-3490-4481-920c-c51d8cdecf9c"}, - "pc" :{"name" :"pc_175" ,"guid": "62414577-94d1-490d-b1e4-7ef3ec40db02"}, - "petg" :{"name" :"petg_175" ,"guid": "69386c85-5b6c-421a-bec5-aeb1fb33f060"}, - "pla" :{"name" :"pla_175" ,"guid": "0ff92885-617b-4144-a03c-9989872454bc"}, - "pva" :{"name" :"pva_175" ,"guid": "a4255da2-cb2a-4042-be49-4a83957a2f9a"}, - "wss1" :{"name" :"rapidrinse_175","guid": "a140ef8f-4f26-4e73-abe0-cfc29d6d1024"}, - "sr30" :{"name" :"sr30_175" ,"guid": "77873465-83a9-4283-bc44-4e542b8eb3eb"}, - "im-pla" :{"name" :"tough_pla_175" ,"guid": "96fca5d9-0371-4516-9e96-8e8182677f3c"}, - "bvoh" :{"name" :"bvoh_175" ,"guid": "923e604c-8432-4b09-96aa-9bbbd42207f4"}, - "cpe" :{"name" :"cpe_175" ,"guid": "da1872c1-b991-4795-80ad-bdac0f131726"}, - "hips" :{"name" :"hips_175" ,"guid": "a468d86a-220c-47eb-99a5-bbb47e514eb0"}, - "tpu" :{"name" :"tpu_175" ,"guid": "19baa6a9-94ff-478b-b4a1-8157b74358d2"} + _MATERIAL_MAP = { "abs" :{"name" :"ABS" ,"guid": "2780b345-577b-4a24-a2c5-12e6aad3e690"}, + "abs-wss1" :{"name" :"ABS-R" ,"guid": "88c8919c-6a09-471a-b7b6-e801263d862d"}, + "asa" :{"name" :"ASA" ,"guid": "416eead4-0d8e-4f0b-8bfc-a91a519befa5"}, + "nylon12-cf":{"name": "Nylon 12 CF" ,"guid": "3c6f2877-71cc-4760-84e6-4b89ab243e3b"}, + "nylon" :{"name" :"Nylon" ,"guid": "283d439a-3490-4481-920c-c51d8cdecf9c"}, + "pc" :{"name" :"PC" ,"guid": "62414577-94d1-490d-b1e4-7ef3ec40db02"}, + "petg" :{"name" :"PETG" ,"guid": "69386c85-5b6c-421a-bec5-aeb1fb33f060"}, + "pla" :{"name" :"PLA" ,"guid": "0ff92885-617b-4144-a03c-9989872454bc"}, + "pva" :{"name" :"PVA" ,"guid": "a4255da2-cb2a-4042-be49-4a83957a2f9a"}, + "wss1" :{"name" :"RapidRinse" ,"guid": "a140ef8f-4f26-4e73-abe0-cfc29d6d1024"}, + "sr30" :{"name" :"SR-30" ,"guid": "77873465-83a9-4283-bc44-4e542b8eb3eb"}, + "bvoh" :{"name" :"BVOH" ,"guid": "923e604c-8432-4b09-96aa-9bbbd42207f4"}, + "cpe" :{"name" :"CPE" ,"guid": "da1872c1-b991-4795-80ad-bdac0f131726"}, + "hips" :{"name" :"HIPS" ,"guid": "a468d86a-220c-47eb-99a5-bbb47e514eb0"}, + "tpu" :{"name" :"TPU 95A" ,"guid": "19baa6a9-94ff-478b-b4a1-8157b74358d2"} } From 77e949830a7c4a2aa9aca7abb9f825b0d07edaba Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 11 Mar 2024 13:04:26 +0100 Subject: [PATCH 8/8] Add missing ABS-CF material to material map CURA-11710 --- cura/PrinterOutput/Models/MaterialOutputModel.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/PrinterOutput/Models/MaterialOutputModel.py b/cura/PrinterOutput/Models/MaterialOutputModel.py index 91210a6a0a..f279358477 100644 --- a/cura/PrinterOutput/Models/MaterialOutputModel.py +++ b/cura/PrinterOutput/Models/MaterialOutputModel.py @@ -25,6 +25,7 @@ class MaterialOutputModel(QObject): def getMaterialFromDefinition(guid, type, brand, name): _MATERIAL_MAP = { "abs" :{"name" :"ABS" ,"guid": "2780b345-577b-4a24-a2c5-12e6aad3e690"}, + "abs-cf10": {"name": "ABS-CF", "guid": "495a0ce5-9daf-4a16-b7b2-06856d82394d"}, "abs-wss1" :{"name" :"ABS-R" ,"guid": "88c8919c-6a09-471a-b7b6-e801263d862d"}, "asa" :{"name" :"ASA" ,"guid": "416eead4-0d8e-4f0b-8bfc-a91a519befa5"}, "nylon12-cf":{"name": "Nylon 12 CF" ,"guid": "3c6f2877-71cc-4760-84e6-4b89ab243e3b"},