mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-04-23 22:29:41 +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.
|
||||
import numpy
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from PyQt6 import QtCore
|
||||
from PyQt6.QtCore import QCoreApplication
|
||||
from PyQt6.QtGui import QImage
|
||||
@ -10,11 +12,13 @@ from UM.Logger import Logger
|
||||
from cura.PreviewPass import PreviewPass
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||
from UM.Math.Matrix import Matrix
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Scene.Camera import Camera
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Qt.QtRenderer import QtRenderer
|
||||
|
||||
class Snapshot:
|
||||
@staticmethod
|
||||
@ -32,6 +36,87 @@ class Snapshot:
|
||||
|
||||
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
|
||||
def snapshot(width = 300, height = 300):
|
||||
"""Return a QImage of the scene
|
||||
@ -55,14 +140,7 @@ class Snapshot:
|
||||
camera = Camera("snapshot", root)
|
||||
|
||||
# determine zoom and look at
|
||||
bbox = None
|
||||
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()
|
||||
bbox = Snapshot.node_bounds(root)
|
||||
# If there is no bounding box, it means that there is no model in the buildplate
|
||||
if bbox is None:
|
||||
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.")
|
||||
return
|
||||
try:
|
||||
snapshot = Snapshot.snapshot(width, height)
|
||||
snapshot = Snapshot.isometric_snapshot(width, height)
|
||||
except:
|
||||
Logger.logException("w", "Failed to create snapshot image")
|
||||
return
|
||||
|
Loading…
x
Reference in New Issue
Block a user