mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-04-23 06:09:38 +08:00

The QImage.pixel() function should only get integers. In theory an input position of -2,-2 would get rounded to 0 now. However that shouldn't occur because the user can't click outside of the window. And if it does occur, it's not really a problem either that it's theoretically picking a position inside of the window when you click slightly next to it. Fixes #10785.
98 lines
3.8 KiB
Python
98 lines
3.8 KiB
Python
# Copyright (c) 2020 Ultimaker B.V.
|
|
# Cura is released under the terms of the LGPLv3 or higher.
|
|
|
|
from typing import Optional, TYPE_CHECKING
|
|
|
|
from UM.Qt.QtApplication import QtApplication
|
|
from UM.Logger import Logger
|
|
from UM.Math.Vector import Vector
|
|
from UM.Resources import Resources
|
|
|
|
from UM.View.RenderPass import RenderPass
|
|
from UM.View.GL.OpenGL import OpenGL
|
|
from UM.View.GL.ShaderProgram import InvalidShaderProgramError
|
|
from UM.View.RenderBatch import RenderBatch
|
|
|
|
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|
|
|
if TYPE_CHECKING:
|
|
from UM.View.GL.ShaderProgram import ShaderProgram
|
|
|
|
class PickingPass(RenderPass):
|
|
"""A :py:class:`Uranium.UM.View.RenderPass` subclass that renders a the distance of selectable objects from the
|
|
active camera to a texture.
|
|
|
|
The texture is used to map a 2d location (eg the mouse location) to a world space position
|
|
|
|
.. note:: that in order to increase precision, the 24 bit depth value is encoded into all three of the R,G & B channels
|
|
"""
|
|
|
|
def __init__(self, width: int, height: int) -> None:
|
|
super().__init__("picking", width, height)
|
|
|
|
self._renderer = QtApplication.getInstance().getRenderer()
|
|
|
|
self._shader = None #type: Optional[ShaderProgram]
|
|
self._scene = QtApplication.getInstance().getController().getScene()
|
|
|
|
def render(self) -> None:
|
|
if not self._shader:
|
|
try:
|
|
self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "camera_distance.shader"))
|
|
except InvalidShaderProgramError:
|
|
Logger.error("Unable to compile shader program: camera_distance.shader")
|
|
return
|
|
|
|
width, height = self.getSize()
|
|
self._gl.glViewport(0, 0, width, height)
|
|
self._gl.glClearColor(1.0, 1.0, 1.0, 0.0)
|
|
self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)
|
|
|
|
# Create a new batch to be rendered
|
|
batch = RenderBatch(self._shader)
|
|
|
|
# Fill up the batch with objects that can be sliced. `
|
|
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
|
|
if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
|
|
batch.addItem(node.getWorldTransformation(copy = False), node.getMeshData(), normal_transformation=node.getCachedNormalMatrix())
|
|
|
|
self.bind()
|
|
batch.render(self._scene.getActiveCamera())
|
|
self.release()
|
|
|
|
def getPickedDepth(self, x: int, y: int) -> float:
|
|
"""Get the distance in mm from the camera to at a certain pixel coordinate.
|
|
|
|
:param x: x component of coordinate vector in pixels
|
|
:param y: y component of coordinate vector in pixels
|
|
:return: distance in mm from the camera to pixel coordinate
|
|
"""
|
|
|
|
output = self.getOutput()
|
|
|
|
window_size = self._renderer.getWindowSize()
|
|
|
|
px = int((0.5 + x / 2.0) * window_size[0])
|
|
py = int((0.5 + y / 2.0) * window_size[1])
|
|
|
|
if px < 0 or px > (output.width() - 1) or py < 0 or py > (output.height() - 1):
|
|
return -1
|
|
|
|
distance = output.pixel(px, py) # distance in micron, from in r, g & b channels
|
|
distance = (distance & 0x00ffffff) / 1000. # drop the alpha channel and convert to mm
|
|
return distance
|
|
|
|
def getPickedPosition(self, x: int, y: int) -> Vector:
|
|
"""Get the world coordinates of a picked point
|
|
|
|
:param x: x component of coordinate vector in pixels
|
|
:param y: y component of coordinate vector in pixels
|
|
:return: vector of the world coordinate
|
|
"""
|
|
|
|
distance = self.getPickedDepth(x, y)
|
|
camera = self._scene.getActiveCamera()
|
|
if camera:
|
|
return camera.getRay(x, y).getPointAlongRay(distance)
|
|
return Vector()
|