Merge branch '5.7'

# Conflicts:
#	conandata.yml
This commit is contained in:
c.lamboo 2024-03-11 11:32:09 +01:00
commit b615177739
5 changed files with 46 additions and 25 deletions

View File

@ -28,6 +28,7 @@ class MaterialOutputModel(QObject):
"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"},

View File

@ -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.
@ -92,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)
@ -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:
@ -184,9 +195,14 @@ class Snapshot:
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!")
return None
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
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:

View File

@ -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.acquire()
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

View File

@ -46,7 +46,7 @@ UM.Dialog
UM.Label
{
id: descriptionLabel
text: catalog.i18nc("@action:description", "When exporting a Universal Cura Project, all the models present on the build plate will be included with their current position, orientation and scale. You can also select which per-extruder or per-model settings should be included to ensure a proper printing of the batch, even on different printers.")
text: catalog.i18nc("@action:description", "Universal Cura Project files can be printed on different 3D printers while retaining positional data and selected settings. When exported, all models present on the build plate will be included along with their current position, orientation, and scale. You can also select which per-extruder or per-model settings should be included to ensure proper printing.")
font: UM.Theme.getFont("default")
wrapMode: Text.Wrap
Layout.maximumWidth: headerColumn.width

View File

@ -6815,7 +6815,7 @@
"prime_tower_mode":
{
"label": "Prime Tower Type",
"description": "<html>How to generate the prime tower:<ul><li><b>Normal:</b> create a bucket in which secondary materials are primed</li><li><b>Interleaved:</b> create a prime tower as sparse as possible. This will save time and filament, but is only possible if the used materials adhere to each other.</li></ul></html>",
"description": "<html>How to generate the prime tower:<ul><li><b>Normal:</b> create a bucket in which secondary materials are primed</li><li><b>Interleaved:</b> create a prime tower as sparse as possible. This will save time and filament, but is only possible if the used materials adhere to each other</li></ul></html>",
"type": "enum",
"value": "'interleaved' if (all(material_type_var == extruderValues('material_type')[0] for material_type_var in extruderValues('material_type')) and all(material_brand_var == extruderValues('material_brand')[0] for material_brand_var in extruderValues('material_brand'))) else 'normal'",
"options":
@ -7301,7 +7301,7 @@
"user_defined_print_order_enabled":
{
"label": "Set Print Sequence Manually",
"description": "Allows to order the object list to set the print sequence manually. First object from the list will be printed first.",
"description": "Allows you to order the object list to manually set the print sequence. First object from the list will be printed first.",
"type": "bool",
"default_value": false,
"settable_per_mesh": false,