mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Merge remote-tracking branch 'origin/5.7' into 5.7
This commit is contained in:
commit
7c00d2f0b3
3 changed files with 60 additions and 41 deletions
|
@ -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,22 @@ 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-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"},
|
||||
"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"}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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,14 +120,17 @@ 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 Snapshot.isNodeRenderable(node):
|
||||
if axis_aligned_box is None:
|
||||
axis_aligned_box = node.getBoundingBox()
|
||||
else:
|
||||
|
@ -127,7 +138,7 @@ class Snapshot:
|
|||
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!")
|
||||
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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue