psychopy.tools.viewtools¶

Math functions for working with view transformations and performing visibility testing (see also mathtools).

Tools for working with view projections for 2- and 3-D rendering.

Overview¶

 visualAngle(size, distance[, degrees, out, …]) Get the visual angle for an object of size at distance. computeFrustum(scrWidth, scrAspect, scrDist) Calculate frustum parameters. computeFrustumFOV(scrFOV, scrAspect, scrDist) Compute a frustum for a given field-of-view (FOV). projectFrustum(frustum, dist[, dtype]) Project a frustum on a fronto-parallel plane and get the width and height of the required drawing area. projectFrustumToPlane(frustum, planeOrig[, …]) Project a frustum on a fronto-parallel plane and get the coordinates of the corners in physical space. Generalized derivation of projection and view matrices based on the physical configuration of the display system. orthoProjectionMatrix(left, right, bottom, top) Compute an orthographic projection matrix with provided frustum parameters. perspectiveProjectionMatrix(left, right, …) Compute an perspective projection matrix with provided frustum parameters. lookAt(eyePos, centerPos[, upVec, out, dtype]) Create a transformation matrix to orient a view towards some point. pointToNdc(wcsPos, viewMatrix, projectionMatrix) Map the position of a point in world space to normalized device coordinates/space. cursorToRay(cursorX, cursorY, winSize, …) Convert a 2D mouse coordinate to a 3D ray. visible(points, mvp[, mode, dtype]) Test if points are visible. visibleBBox(extents, mvp[, dtype]) Check if a bounding box is visible.

Details¶

psychopy.tools.viewtools.visualAngle(size, distance, degrees=True, out=None, dtype=None)[source]

Get the visual angle for an object of size at distance. Object is assumed to be fronto-parallel with the viewer.

This function supports vector inputs. Values for size and distance can be arrays or single values. If both inputs are arrays, they must have the same size.

Parameters
• size (float or array_like) – Size of the object in meters.

• distance (float or array_like) – Distance to the object in meters.

• degrees (bool) – Return result in degrees, if False result will be in radians.

• out (ndarray, optional) – Optional output array. Must be same shape and dtype as the expected output if out was not specified.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Returns

Visual angle.

Return type

float

Examples

Calculating the visual angle (vertical FOV) of a monitor screen:

monDist = 0.5  # monitor distance, 50cm
monHeight = 0.45  # monitor height, 45cm

vertFOV = visualAngle(monHeight, monDist)


Compute visual angle at multiple distances for objects with the same size:

va = visualAngle(0.20, [1.0, 2.0, 3.0])  # returns
# [11.42118627  5.72481045  3.81830487]

psychopy.tools.viewtools.computeFrustum(scrWidth, scrAspect, scrDist, convergeOffset=0.0, eyeOffset=0.0, nearClip=0.01, farClip=100.0, dtype=None)[source]

Calculate frustum parameters. If an eye offset is provided, an asymmetric frustum is returned which can be used for stereoscopic rendering.

Parameters
• scrWidth (float) – The display’s width in meters.

• scrAspect (float) – Aspect ratio of the display (width / height).

• scrDist (float) – Distance to the screen from the view in meters. Measured from the center of their eyes.

• convergeOffset (float) – Offset of the convergence plane from the screen. Objects falling on this plane will have zero disparity. For best results, the convergence plane should be set to the same distance as the screen (0.0 by default).

• eyeOffset (float) – Half the inter-ocular separation (i.e. the horizontal distance between the nose and center of the pupil) in meters. If eyeOffset is 0.0, a symmetric frustum is returned.

• nearClip (float) – Distance to the near clipping plane in meters from the viewer. Should be at least less than scrDist.

• farClip (float) – Distance to the far clipping plane from the viewer in meters. Must be >nearClip.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Returns

Array of frustum parameters. Can be directly passed to glFrustum (e.g. glFrustum(*f)).

Return type

ndarray

Notes

• The view point must be transformed for objects to appear correctly. Offsets in the X-direction must be applied +/- eyeOffset to account for inter-ocular separation. A transformation in the Z-direction must be applied to account for screen distance. These offsets MUST be applied to the GL_MODELVIEW matrix, not the GL_PROJECTION matrix! Doing so may break lighting calculations.

Examples

Creating a frustum and setting a window’s projection matrix:

scrWidth = 0.5  # screen width in meters
scrAspect = win.size[0] / win.size[1]
scrDist = win.scrDistCM * 100.0  # monitor setting, can be anything
frustum = viewtools.computeFrustum(scrWidth, scrAspect, scrDist)


Accessing frustum parameters:

left, right, bottom, top, nearVal, farVal = frustum
# ... or ...
left = frustum.left


Off-axis frustums for stereo rendering:

# compute view matrix for each eye, these value usually don't change
eyeOffset = (-0.035, 0.035)  # +/- IOD / 2.0
scrDist = 0.50  # 50cm
scrWidth = 0.53  # 53cm
scrAspect = 1.778
leftFrustum = viewtools.computeFrustum(
scrWidth, scrAspect, scrDist, eyeOffset[0])
rightFrustum = viewtools.computeFrustum(
scrWidth, scrAspect, scrDist, eyeOffset[1])
# make sure your view matrix accounts for the screen distance and eye
# offsets!


Using computed view frustums with a window:

win.projectionMatrix = viewtools.perspectiveProjectionMatrix(*frustum)
# generate a view matrix looking ahead with correct viewing distance,
# origin is at the center of the screen. Assumes eye is centered with
# the screen.
eyePos = [0.0, 0.0, scrDist]
screenPos = [0.0, 0.0, 0.0]  # look at screen center
eyeUp = [0.0, 1.0, 0.0]
win.viewMatrix = viewtools.lookAt(eyePos, screenPos, eyeUp)
win.applyViewTransform()  # call before drawing

psychopy.tools.viewtools.computeFrustumFOV(scrFOV, scrAspect, scrDist, convergeOffset=0.0, eyeOffset=0.0, nearClip=0.01, farClip=100.0, dtype=None)[source]

Compute a frustum for a given field-of-view (FOV).

Similar to computeFrustum, but computes a frustum based on FOV rather than screen dimensions.

Parameters
• scrFOV (float) – Vertical FOV in degrees (fovY).

• scrAspect (float) – Aspect between the horizontal and vertical FOV (ie. fovX / fovY).

• scrDist (float) – Distance to the screen from the view in meters. Measured from the center of the viewer’s eye(s).

• convergeOffset (float) – Offset of the convergence plane from the screen. Objects falling on this plane will have zero disparity. For best results, the convergence plane should be set to the same distance as the screen (0.0 by default).

• eyeOffset (float) – Half the inter-ocular separation (i.e. the horizontal distance between the nose and center of the pupil) in meters. If eyeOffset is 0.0, a symmetric frustum is returned.

• nearClip (float) – Distance to the near clipping plane in meters from the viewer. Should be at least less than scrDist. Never should be 0.

• farClip (float) – Distance to the far clipping plane from the viewer in meters. Must be >nearClip.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Examples

Equivalent to gluPerspective:

frustum =  computeFrustumFOV(45.0, 1.0, 0.5)
projectionMatrix = perspectiveProjectionMatrix(*frustum)

psychopy.tools.viewtools.projectFrustum(frustum, dist, dtype=None)[source]

Project a frustum on a fronto-parallel plane and get the width and height of the required drawing area.

This function can be used to determine the size of the drawing area required for a given frustum on a screen. This is useful for cases where the observer is viewing the screen through a physical aperture that limits the FOV to a sub-region of the display. You must convert the size in meters to units of your screen and apply any offsets.

Parameters
• frustum (array_like) – Frustum parameters (left, right, bottom, top, near, far), you can exclude far since it is not used in this calculation. However, the function will still succeed if given.

• dist (float) – Distance to project points to in meters.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Returns

Width and height (w, h) of the area intersected by the given frustum at dist.

Return type

ndarray

Examples

Compute the viewport required to draw in the area where the frustum intersects the screen:

# needed information
scrWidthM = 0.52
scrDistM = 0.72
scrWidthPIX = 1920
scrHeightPIX = 1080
scrAspect = scrWidthPIX / float(scrHeightPIX)
pixPerMeter = scrWidthPIX / scrWidthM

# Compute a frustum for 20 degree vertical FOV at distance of the
# screen.
frustum = computeFrustumFOV(20., scrAspect, scrDistM)

# get the dimensions of the frustum
w, h = projectFrustum(frustum, scrDistM) * pixPerMeter

# get the origin of the viewport, relative to center of screen.
x = (scrWidthPIX - w) / 2.
y = (scrHeightPIX - h) / 2.

# if there is an eye offset ...
# x = (scrWidthPIX - w + eyeOffsetM * pixPerMeter) / 2.

# viewport rectangle
rect = np.asarray((x, y, w, h), dtype=int)


You can then set the viewport/scissor rectangle of the buffer to restrict drawing to rect.

psychopy.tools.viewtools.projectFrustumToPlane(frustum, planeOrig, dtype=None)[source]

Project a frustum on a fronto-parallel plane and get the coordinates of the corners in physical space.

Parameters
• frustum (array_like) – Frustum parameters (left, right, bottom, top, near, far), you can exclude far since it is not used in this calculation. However, the function will still succeed if given.

• planeOrig (float) – Distance of plane to project points on in meters.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Returns

4x3 array of coordinates in the physical reference frame with origin at the eye.

Return type

ndarray

psychopy.tools.viewtools.generalizedPerspectiveProjection(posBottomLeft, posBottomRight, posTopLeft, eyePos, nearClip=0.01, farClip=100.0, dtype=None)[source]

Generalized derivation of projection and view matrices based on the physical configuration of the display system.

This implementation is based on Robert Kooima’s ‘Generalized Perspective Projection’ method 1.

Parameters
• posBottomLeft (list of float or ndarray) – Bottom-left 3D coordinate of the screen in meters.

• posBottomRight (list of float or ndarray) – Bottom-right 3D coordinate of the screen in meters.

• posTopLeft (list of float or ndarray) – Top-left 3D coordinate of the screen in meters.

• eyePos (list of float or ndarray) – Coordinate of the eye in meters.

• nearClip (float) – Near clipping plane distance from viewer in meters.

• farClip (float) – Far clipping plane distance from viewer in meters.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Returns

The 4x4 projection and view matrix.

Return type

tuple

computeFrustum

Compute frustum parameters.

Notes

• The resulting projection frustums are off-axis relative to the center of the display.

• The returned matrices are row-major. Values are floats with 32-bits of precision stored as a contiguous (C-order) array.

References

1

Kooima, R. (2009). Generalized perspective projection. J. Sch. Electron. Eng. Comput. Sci.

Examples

Computing a projection and view matrices for a window:

projMatrix, viewMatrix = viewtools.generalizedPerspectiveProjection(
posBottomLeft, posBottomRight, posTopLeft, eyePos)
# set the window matrices
win.projectionMatrix = projMatrix
win.viewMatrix = viewMatrix
# before rendering
win.applyEyeTransform()


Stereo-pair rendering example from Kooima (2009):

# configuration of screen and eyes
posBottomLeft = [-1.5, -0.75, -18.0]
posBottomRight = [1.5, -0.75, -18.0]
posTopLeft = [-1.5, 0.75, -18.0]
posLeftEye = [-1.25, 0.0, 0.0]
posRightEye = [1.25, 0.0, 0.0]
# create projection and view matrices
leftProjMatrix, leftViewMatrix = generalizedPerspectiveProjection(
posBottomLeft, posBottomRight, posTopLeft, posLeftEye)
rightProjMatrix, rightViewMatrix = generalizedPerspectiveProjection(
posBottomLeft, posBottomRight, posTopLeft, posRightEye)

psychopy.tools.viewtools.orthoProjectionMatrix(left, right, bottom, top, nearClip=0.01, farClip=100.0, out=None, dtype=None)[source]

Compute an orthographic projection matrix with provided frustum parameters.

Parameters
• left (float) – Left clipping plane coordinate.

• right (float) – Right clipping plane coordinate.

• bottom (float) – Bottom clipping plane coordinate.

• top (float) – Top clipping plane coordinate.

• nearClip (float) – Near clipping plane distance from viewer.

• farClip (float) – Far clipping plane distance from viewer.

• out (ndarray, optional) – Optional output array. Must be same shape and dtype as the expected output if out was not specified.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Returns

4x4 projection matrix

Return type

ndarray

perspectiveProjectionMatrix

Compute a perspective projection matrix.

Notes

• The returned matrix is row-major. Values are floats with 32-bits of precision stored as a contiguous (C-order) array.

psychopy.tools.viewtools.perspectiveProjectionMatrix(left, right, bottom, top, nearClip=0.01, farClip=100.0, out=None, dtype=None)[source]

Compute an perspective projection matrix with provided frustum parameters. The frustum can be asymmetric.

Parameters
• left (float) – Left clipping plane coordinate.

• right (float) – Right clipping plane coordinate.

• bottom (float) – Bottom clipping plane coordinate.

• top (float) – Top clipping plane coordinate.

• nearClip (float) – Near clipping plane distance from viewer.

• farClip (float) – Far clipping plane distance from viewer.

• out (ndarray, optional) – Optional output array. Must be same shape and dtype as the expected output if out was not specified.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Returns

4x4 projection matrix

Return type

ndarray

orthoProjectionMatrix

Compute a orthographic projection matrix.

Notes

• The returned matrix is row-major. Values are floats with 32-bits of precision stored as a contiguous (C-order) array.

psychopy.tools.viewtools.lookAt(eyePos, centerPos, upVec=(0.0, 1.0, 0.0), out=None, dtype=None)[source]

Create a transformation matrix to orient a view towards some point. Based on the same algorithm as ‘gluLookAt’. This does not generate a projection matrix, but rather the matrix to transform the observer’s view in the scene.

Parameters
• eyePos (list of float or ndarray) – Eye position in the scene.

• centerPos (list of float or ndarray) – Position of the object center in the scene.

• upVec (list of float or ndarray, optional) – Vector defining the up vector. Default is +Y is up.

• out (ndarray, optional) – Optional output array. Must be same shape and dtype as the expected output if out was not specified.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Returns

4x4 view matrix

Return type

ndarray

Notes

• The returned matrix is row-major. Values are floats with 32-bits of precision stored as a contiguous (C-order) array.

psychopy.tools.viewtools.pointToNdc(wcsPos, viewMatrix, projectionMatrix, out=None, dtype=None)[source]

Map the position of a point in world space to normalized device coordinates/space.

Parameters
• wcsPos (tuple, list or ndarray) – Nx3 position vector(s) (xyz) in world space coordinates.

• viewMatrix (ndarray) – 4x4 view matrix.

• projectionMatrix (ndarray) – 4x4 projection matrix.

• out (ndarray, optional) – Optional output array. Must be same shape and dtype as the expected output if out was not specified.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Returns

3x1 vector of normalized device coordinates with type ‘float32’

Return type

ndarray

Notes

• The point is not visible, falling outside of the viewing frustum, if the returned coordinates fall outside of -1 and 1 along any dimension.

• In the rare instance the point falls directly on the eye in world space where the frustum converges to a point (singularity), the divisor will be zero during perspective division. To avoid this, the divisor is ‘bumped’ to 1e-5.

• This function assumes the display area is rectilinear. Any distortion or warping applied in normalized device or viewport space is not considered.

Examples

Determine if a point is visible:

point = (0.0, 0.0, 10.0)  # behind the observer
ndc = pointToNdc(point, win.viewMatrix, win.projectionMatrix)
isVisible = not np.any((ndc > 1.0) | (ndc < -1.0))


Convert NDC to viewport (or pixel) coordinates:

scrRes = (1920, 1200)
point = (0.0, 0.0, -5.0)  # forward -5.0 from eye
x, y, z = pointToNdc(point, win.viewMatrix, win.projectionMatrix)
pixelX = ((x + 1.0) / 2.0) * scrRes[0])
pixelY = ((y + 1.0) / 2.0) * scrRes[1])
# object at point will appear at (pixelX, pixelY)

psychopy.tools.viewtools.cursorToRay(cursorX, cursorY, winSize, viewport, projectionMatrix, normalize=True, out=None, dtype=None)[source]

Convert a 2D mouse coordinate to a 3D ray.

Takes a 2D window/mouse coordinate and transforms it to a 3D direction vector from the viewpoint in eye space (vector origin is [0, 0, 0]). The center of the screen projects to vector [0, 0, -1].

Parameters
• cursorX (float or int) – Window coordinates. These need to be scaled if you are using a framebuffer that does not have 1:1 pixel mapping (i.e. retina display).

• cursorY (float or int) – Window coordinates. These need to be scaled if you are using a framebuffer that does not have 1:1 pixel mapping (i.e. retina display).

• winSize (array_like) – Size of the window client area [w, h].

• viewport (array_like) – Viewport rectangle [x, y, w, h] being used.

• projectionMatrix (ndarray) – 4x4 projection matrix being used.

• normalize (bool) – Normalize the resulting vector.

• out (ndarray, optional) – Optional output array. Must be same shape and dtype as the expected output if out was not specified.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Returns

Direction vector (x, y, z).

Return type

ndarray

Examples

Place a 3D stim at the mouse location 5.0 scene units (meters) away:

# define camera
camera = RigidBodyPose((-3.0, 5.0, 3.5))
camera.alignTo((0, 0, 0))

# in the render loop

dist = 5.0
mouseRay = vt.cursorToRay(x, y, win.size, win.viewport, win.projectionMatrix)
mouseRay *= dist  # scale the vector

# set the sphere position by transforming vector to world space
sphere.thePose.pos = camera.transform(mouseRay)

psychopy.tools.viewtools.visible(points, mvp, mode='discrete', dtype=None)[source]

Test if points are visible.

This function is useful for visibility culling, where objects are only drawn if a portion of them are visible. This test can avoid costly drawing calls and OpenGL state changes if the object is not visible.

Parameters
• points (array_like) – Point(s) or bounding box to test. Input array must be Nx3 or Nx4, where each row is a point. It is recommended that the input be Nx4 since the w component will be appended if the input is Nx3 which adds overhead.

• mvp (array_like) – 4x4 MVP matrix.

• mode (str) – Test mode. If ‘discrete’, rows of points are treated as individual points. This function will return an array of boolean values with length equal to the number of rows in points, where the value at each index corresponds to the visibility test results for points at the matching row index of points. If ‘group’ a single boolean value is returned, which is False if all points fall to one side of the frustum.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Returns

Test results. The type returned depends on mode.

Return type

bool or ndarray

Examples

Visibility culling, only a draw line connecting two points if visible:

linePoints = [[-1.0, -1.0, -1.0, 1.0],
[ 1.0,  1.0,  1.0, 1.0]]

mvp = np.matmul(win.projectionMatrix, win.viewMatrix)
if visible(linePoints, mvp, mode='group'):
# drawing commands here ...

psychopy.tools.viewtools.visibleBBox(extents, mvp, dtype=None)[source]

Check if a bounding box is visible.

This function checks if a bonding box intersects a frustum defined by the current projection matrix, after being transformed by the model-view matrix.

Parameters
• extents (array_like) – Bounding box minimum and maximum extents as a 2x3 array. The first row if the minimum extents along each axis, and the second row the maximum extents (eg. [[minX, minY, minZ], [maxX, maxY, maxZ]]).

• mvp (array_like) – 4x4 MVP matrix.

• dtype (dtype or str, optional) – Data type for arrays, can either be ‘float32’ or ‘float64’. If None is specified, the data type is inferred by out. If out is not provided, the default is ‘float64’.

Returns

Visibility test results.

Return type

ndarray or bool