Browse Source

Add iso view to snapshot

c.lamboo 1 year ago
parent
commit
fe4790fe06
2 changed files with 89 additions and 3 deletions
  1. 88 2
      cura/Snapshot.py
  2. 1 1
      plugins/MakerbotWriter/MakerbotWriter.py

+ 88 - 2
cura/Snapshot.py

@@ -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,88 @@ class Snapshot:
 
         return min_x, max_x, min_y, max_y
 
+    @staticmethod
+    def isometric_snapshot(width: int = 300, height: int = 300, *, root: Optional[SceneNode] = None) -> Optional[
+        QImage]:
+        """Create an isometric snapshot of the scene."""
+
+        root = Application.getInstance().getController().getScene().getRoot() if root is None else root
+
+        # 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

+ 1 - 1
plugins/MakerbotWriter/MakerbotWriter.py

@@ -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