Cura/cura/Snapshot.py

125 lines
5.1 KiB
Python

# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import numpy
from PyQt5 import QtCore
from cura.PreviewPass import PreviewPass
from cura.Scene import ConvexHullNode
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.Mesh.MeshData import transformVertices
from UM.Scene.Camera import Camera
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
class Snapshot:
@staticmethod
def snapshot(width = 300, height = 300):
scene = Application.getInstance().getController().getScene()
active_camera = scene.getActiveCamera()
render_width, render_height = active_camera.getWindowSize()
preview_pass = PreviewPass(render_width, render_height)
root = scene.getRoot()
camera = Camera("snapshot", root)
# determine zoom and look at
bbox = None
hulls = None
for node in DepthFirstIterator(root):
if type(node) == ConvexHullNode:
print(node)
if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
if bbox is None:
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
size = max(bbox.width, bbox.height, bbox.depth * 0.5)
# Somehow the aspect ratio is also influenced in reverse by the screen width/height
# So you have to set it to render_width/render_height to get 1
projection_matrix = Matrix()
projection_matrix.setPerspective(30, render_width / render_height, 1, 500)
camera.setProjectionMatrix(projection_matrix)
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_)
# make it a square
if max_x - min_x >= max_y - min_y:
# make y bigger
min_y, max_y = int((max_y + min_y) / 2 - (max_x - min_x) / 2), int((max_y + min_y) / 2 + (max_x - min_x) / 2)
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)
# 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)
return cropped_image