mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-07-04 05:45:12 +08:00
Merge pull request #17046 from Ultimaker/makerbot-iso-view-renderer
Add iso view to snapshot
This commit is contained in:
commit
2ee0769812
@ -1,7 +1,9 @@
|
|||||||
# Copyright (c) 2021 Ultimaker B.V.
|
# Copyright (c) 2023 UltiMaker
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from PyQt6 import QtCore
|
from PyQt6 import QtCore
|
||||||
from PyQt6.QtCore import QCoreApplication
|
from PyQt6.QtCore import QCoreApplication
|
||||||
from PyQt6.QtGui import QImage
|
from PyQt6.QtGui import QImage
|
||||||
@ -10,11 +12,13 @@ from UM.Logger import Logger
|
|||||||
from cura.PreviewPass import PreviewPass
|
from cura.PreviewPass import PreviewPass
|
||||||
|
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
|
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||||
from UM.Math.Matrix import Matrix
|
from UM.Math.Matrix import Matrix
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
from UM.Scene.Camera import Camera
|
from UM.Scene.Camera import Camera
|
||||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
from UM.Qt.QtRenderer import QtRenderer
|
||||||
|
|
||||||
class Snapshot:
|
class Snapshot:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -32,6 +36,87 @@ class Snapshot:
|
|||||||
|
|
||||||
return min_x, max_x, min_y, max_y
|
return min_x, max_x, min_y, max_y
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def isometric_snapshot(width: int = 300, height: int = 300) -> Optional[QImage]:
|
||||||
|
"""Create an isometric snapshot of the scene."""
|
||||||
|
|
||||||
|
root = Application.getInstance().getController().getScene().getRoot()
|
||||||
|
|
||||||
|
# the direction the camera is looking at to create the isometric view
|
||||||
|
iso_view_dir = Vector(-1, -1, -1).normalized()
|
||||||
|
|
||||||
|
bounds = Snapshot.node_bounds(root)
|
||||||
|
if bounds is None:
|
||||||
|
Logger.log("w", "There appears to be nothing to render")
|
||||||
|
return None
|
||||||
|
|
||||||
|
camera = Camera("snapshot")
|
||||||
|
|
||||||
|
# find local x and y directional vectors of the camera
|
||||||
|
s = iso_view_dir.cross(Vector.Unit_Y).normalized()
|
||||||
|
u = s.cross(iso_view_dir).normalized()
|
||||||
|
|
||||||
|
# find extreme screen space coords of the scene
|
||||||
|
x_points = [p.dot(s) for p in bounds.points]
|
||||||
|
y_points = [p.dot(u) for p in bounds.points]
|
||||||
|
min_x = min(x_points)
|
||||||
|
max_x = max(x_points)
|
||||||
|
min_y = min(y_points)
|
||||||
|
max_y = max(y_points)
|
||||||
|
camera_width = max_x - min_x
|
||||||
|
camera_height = max_y - min_y
|
||||||
|
|
||||||
|
if camera_width == 0 or camera_height == 0:
|
||||||
|
Logger.log("w", "There appears to be nothing to render")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# increase either width or height to match the aspect ratio of the image
|
||||||
|
if camera_width / camera_height > width / height:
|
||||||
|
camera_height = camera_width * height / width
|
||||||
|
else:
|
||||||
|
camera_width = camera_height * width / height
|
||||||
|
|
||||||
|
# Configure camera for isometric view
|
||||||
|
ortho_matrix = Matrix()
|
||||||
|
ortho_matrix.setOrtho(
|
||||||
|
-camera_width / 2,
|
||||||
|
camera_width / 2,
|
||||||
|
-camera_height / 2,
|
||||||
|
camera_height / 2,
|
||||||
|
-10000,
|
||||||
|
10000
|
||||||
|
)
|
||||||
|
camera.setPerspective(False)
|
||||||
|
camera.setProjectionMatrix(ortho_matrix)
|
||||||
|
camera.setPosition(bounds.center)
|
||||||
|
camera.lookAt(bounds.center + iso_view_dir)
|
||||||
|
|
||||||
|
# Render the scene
|
||||||
|
renderer = QtRenderer()
|
||||||
|
render_pass = PreviewPass(width, height)
|
||||||
|
renderer.setViewportSize(width, height)
|
||||||
|
renderer.setWindowSize(width, height)
|
||||||
|
render_pass.setCamera(camera)
|
||||||
|
renderer.addRenderPass(render_pass)
|
||||||
|
renderer.beginRendering()
|
||||||
|
renderer.render()
|
||||||
|
|
||||||
|
return render_pass.getOutput()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def node_bounds(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()
|
||||||
|
return axis_aligned_box
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def snapshot(width = 300, height = 300):
|
def snapshot(width = 300, height = 300):
|
||||||
"""Return a QImage of the scene
|
"""Return a QImage of the scene
|
||||||
@ -55,14 +140,7 @@ class Snapshot:
|
|||||||
camera = Camera("snapshot", root)
|
camera = Camera("snapshot", root)
|
||||||
|
|
||||||
# determine zoom and look at
|
# determine zoom and look at
|
||||||
bbox = None
|
bbox = Snapshot.node_bounds(root)
|
||||||
for node in DepthFirstIterator(root):
|
|
||||||
if not getattr(node, "_outside_buildarea", False):
|
|
||||||
if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible() and not node.callDecoration("isNonThumbnailVisibleMesh"):
|
|
||||||
if bbox is None:
|
|
||||||
bbox = node.getBoundingBox()
|
|
||||||
else:
|
|
||||||
bbox = bbox + node.getBoundingBox()
|
|
||||||
# If there is no bounding box, it means that there is no model in the buildplate
|
# If there is no bounding box, it means that there is no model in the buildplate
|
||||||
if bbox is None:
|
if bbox is None:
|
||||||
Logger.log("w", "Unable to create snapshot as we seem to have an empty buildplate")
|
Logger.log("w", "Unable to create snapshot as we seem to have an empty buildplate")
|
||||||
|
@ -61,7 +61,7 @@ class MakerbotWriter(MeshWriter):
|
|||||||
Logger.warning("Can't create snapshot when renderer not initialized.")
|
Logger.warning("Can't create snapshot when renderer not initialized.")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
snapshot = Snapshot.snapshot(width, height)
|
snapshot = Snapshot.isometric_snapshot(width, height)
|
||||||
except:
|
except:
|
||||||
Logger.logException("w", "Failed to create snapshot image")
|
Logger.logException("w", "Failed to create snapshot image")
|
||||||
return
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user