camera_roll_wrt_solar_north

camera_roll_wrt_solar_north#

camera_roll_wrt_solar_north(
position,
focal_point,
view_up,
world_up=array([0, 0, 1]),
degrees=True,
)[source]#

Compute the camera roll angle relative to a world “up” direction.

The roll is the signed rotation about the view axis (from position toward focal_point) that would bring the projection of world_up onto the image plane into alignment with the projection of view_up. By default world_up is solar north \((0, 0, 1)\) (pyvisual.core.constants.SOLAR_NORTH).

Parameters:
positiontuple[float, float, float]

Camera position in world (Cartesian) coordinates, \((x_c, y_c, z_c)\).

focal_pointtuple[float, float, float]

Camera look-at target in world coordinates.

view_uptuple[float, float, float]

Camera “up” vector in world coordinates. Need not be perfectly orthogonal to the view direction; it is projected into the view plane internally.

world_uptuple[float, float, float], optional

Reference “up” direction in world coordinates. Projected into the view plane before computing the roll. Default is SOLAR_NORTH.

degreesbool, optional

If True return the roll in degrees, otherwise in radians. Default is True.

Returns:
rollfloat

Signed roll angle about the view axis. Positive sense follows the right-hand rule about \(\hat{v} = (\mathbf{f} - \mathbf{p}) / \|\mathbf{f} - \mathbf{p}\|\). Returns numpy.nan for degenerate cases (see Notes).

See also

pyvisual.core.mixins.ObserverMixin.observer_orientation()

Property that stores the result of this function for the active camera.

Notes

Algorithm:

  1. Compute the normalised view direction \(\hat{v} = \operatorname{normalize}(\mathbf{f} - \mathbf{p})\).

  2. Project view_up (\(\hat{u}\)) and world_up (\(\hat{w}\)) onto the image plane (perpendicular to \(\hat{v}\)):

    \[\mathbf{u}_\perp = \hat{u} - (\hat{u} \cdot \hat{v})\,\hat{v}, \qquad \mathbf{w}_\perp = \hat{w} - (\hat{w} \cdot \hat{v})\,\hat{v}\]
  3. Compute the signed angle from \(\mathbf{w}_\perp\) to \(\mathbf{u}_\perp\) about \(\hat{v}\):

    \[\alpha = \arctan2\!\left( \hat{v} \cdot (\mathbf{w}_\perp \times \mathbf{u}_\perp),\; \mathbf{w}_\perp \cdot \mathbf{u}_\perp \right)\]

The angle is undefined (returns numpy.nan) when the projection of world_up or view_up onto the view plane has near-zero magnitude — most commonly when the view direction is parallel or anti-parallel to world_up (a gimbal-lock-like condition).

Examples

Observer on the \(+x\) axis looking at the Sun with solar north as view_up — roll should be zero:

>>> import numpy as np
>>> roll = camera_roll_wrt_solar_north(
...     position=(10, 0, 0),
...     focal_point=(0, 0, 0),
...     view_up=(0, 0, 1),
... )
>>> np.isfinite(roll)
True