Browse Source

CURA-4425 the thumbnail now crops correctly

Jack Ha 7 years ago
parent
commit
0e7edc3eaf
2 changed files with 24 additions and 52 deletions
  1. 23 51
      cura/Snapshot.py
  2. 1 1
      plugins/UFPWriter/UFPWriter.py

+ 23 - 51
cura/Snapshot.py

@@ -17,11 +17,16 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
 
 
 class Snapshot:
+    ##  Return a QImage of the scene
+    #   Uses PreviewPass that leaves out some elements
+    #   Aspect ratio assumes a square
     @staticmethod
     def snapshot(width = 300, height = 300):
         scene = Application.getInstance().getController().getScene()
         active_camera = scene.getActiveCamera()
         render_width, render_height = active_camera.getWindowSize()
+        render_width = int(render_width)
+        render_height = int(render_height)
         preview_pass = PreviewPass(render_width, render_height)
 
         root = scene.getRoot()
@@ -29,7 +34,6 @@ class Snapshot:
 
         # determine zoom and look at
         bbox = None
-        hulls = None
         for node in DepthFirstIterator(root):
             if type(node) == ConvexHullNode:
                 print(node)
@@ -38,16 +42,12 @@ class Snapshot:
                     bbox = node.getBoundingBox()
                 else:
                     bbox = bbox + node.getBoundingBox()
-                convex_hull = node.getMeshData().getConvexHullTransformedVertices(node.getWorldTransformation())
-                if hulls is None:
-                    hulls = convex_hull
-                else:
-                    hulls = numpy.concatenate((hulls, convex_hull), axis = 0)
 
         if bbox is None:
             bbox = AxisAlignedBox()
 
         look_at = bbox.center
+        # guessed size so the objects are hopefully big
         size = max(bbox.width, bbox.height, bbox.depth * 0.5)
 
         # Somehow the aspect ratio is also influenced in reverse by the screen width/height
@@ -56,56 +56,27 @@ class Snapshot:
         projection_matrix.setPerspective(30, render_width / render_height, 1, 500)
         camera.setProjectionMatrix(projection_matrix)
 
+        # Looking from this direction (x, y, z) in OGL coordinates
         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.3
-        camera.setViewportSize(render_width, render_height)
-        camera.setWindowSize(render_width, render_height)
         camera.setPosition(look_at + looking_from_offset)
         camera.lookAt(look_at)
 
-        # we need this for the projection calculation
-        hulls4 = numpy.ones((hulls.shape[0], 4))
-        hulls4[:, :-1] = hulls
-        #position = Vector(10, 10, 10)
-        # projected_position = camera.project(position)
-
         preview_pass.setCamera(camera)
-        preview_pass.setSize(render_width, render_height)  # texture size
         preview_pass.render()
         pixel_output = preview_pass.getOutput()
 
-        print("Calculating image coordinates...")
-        view = camera.getWorldTransformation().getInverse()
-        min_x, max_x, min_y, max_y = render_width, 0, render_height, 0
-        for hull_coords in hulls4:
-            projected_position = view.getData().dot(hull_coords)
-            projected_position2 = projection_matrix.getData().dot(projected_position)
-            #xx, yy = camera.project(Vector(data = hull_coords))
-            # xx, yy range from -1 to 1
-            xx = projected_position2[0] / projected_position2[2] / 2.0
-            yy = projected_position2[1] / projected_position2[2] / 2.0
-            # x, y 0..render_width/height
-            x = int(render_width / 2 + xx * render_width / 2)
-            y = int(render_height / 2 + yy * render_height / 2)
-            min_x = min(x, min_x)
-            max_x = max(x, max_x)
-            min_y = min(y, min_y)
-            max_y = max(y, max_y)
-        print(min_x, max_x, min_y, max_y)
-
-        # print("looping all pixels in python...")
-        # min_x_, max_x_, min_y_, max_y_ = render_width, 0, render_height, 0
-        # for y in range(int(render_height)):
-        #     for x in range(int(render_width)):
-        #         color = pixel_output.pixelColor(x, y)
-        #         if color.alpha() > 0:
-        #             min_x_ = min(x, min_x_)
-        #             max_x_ = max(x, max_x_)
-        #             min_y_ = min(y, min_y_)
-        #             max_y_ = max(y, max_y_)
-        # print(min_x_, max_x_, min_y_, max_y_)
+        # Look at the resulting image to get a good crop.
+        # Get the pixels as byte array
+        pixel_array = pixel_output.bits().asarray(pixel_output.byteCount())
+        # Convert to numpy array, assume it's 32 bit (it should always be)
+        pixels = numpy.frombuffer(pixel_array, dtype=numpy.uint8).reshape([render_height, render_width, 4])
+        # Find indices of non zero pixels
+        nonzero_pixels = numpy.nonzero(pixels)
+        min_y, min_x, min_a_ = numpy.amin(nonzero_pixels, axis=1)
+        max_y, max_x, max_a_ = numpy.amax(nonzero_pixels, axis=1)
 
         # make it a square
         if max_x - min_x >= max_y - min_y:
@@ -114,11 +85,12 @@ class Snapshot:
         else:
             # make x bigger
             min_x, max_x = int((max_x + min_x) / 2 - (max_y - min_y) / 2), int((max_x + min_x) / 2 + (max_y - min_y) / 2)
-        copy_pixel_output = pixel_output.copy(min_x, min_y, max_x - min_x, max_y - min_y)
+        cropped_image = pixel_output.copy(min_x, min_y, max_x - min_x, max_y - min_y)
 
-        # Scale it to the correct height
-        image = copy_pixel_output.scaledToHeight(height, QtCore.Qt.SmoothTransformation)
-        # Then chop of the width
-        cropped_image = image.copy(image.width() // 2 - width // 2, 0, width, height)
+        # Scale it to the correct size
+        scaled_image = cropped_image.scaled(
+            width, height,
+            aspectRatioMode = QtCore.Qt.IgnoreAspectRatio,
+            transformMode = QtCore.Qt.SmoothTransformation)
 
-        return cropped_image
+        return scaled_image

+ 1 - 1
plugins/UFPWriter/UFPWriter.py

@@ -23,7 +23,7 @@ class UFPWriter(MeshWriter):
     def _createSnapshot(self, *args):
         # must be called from the main thread because of OpenGL
         Logger.log("d", "Creating thumbnail image...")
-        self._snapshot = Snapshot.snapshot()
+        self._snapshot = Snapshot.snapshot(width = 300, height = 300)
 
     def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode):
         archive = VirtualFile()