visualdynamics.rotate¶
rotate
¶
Turning a coordinate system by dragging a ring around one of its axes.
The geometry and the angle arithmetic live here, apart from Qt and VTK, so the fiddly part — which way a drag turns the frame, and by how much — can be tested without a window.
A coordinate system is stored as four rows: three orthonormal basis vectors and an origin. Rotation only ever touches the basis; the origin stays put.
Sliding is the other gesture (2026-10-02): an arrow along each axis of the frame, dragged, moves the origin along that axis and snaps it to a grid in the geometry's own axes — a tenth of an inch for the inch family of units, a centimetre for the metric one — so a frame or a block lands on round coordinates whichever way it is turned. The arrow arithmetic lives here beside the ring arithmetic, for the same reason.
Functions:
| Name | Description |
|---|---|
rotation_about |
The 3x3 rotation of |
ring_points |
A closed ring of points around one principal axis of a frame. |
rotate_frame |
A copy of |
angle_in_plane |
Where a world point sits around one principal axis, in radians. |
wrapped |
An angle folded into [-pi, pi), so a drag past the seam stays small. |
ring_under_cursor |
Which ring a pixel is over, or None. |
plane_hit |
Where a ray meets the plane through |
identity_frame |
The frame with its rotation undone, keeping where it sits. |
grid_step |
The grid a slide lands on, in the display |
arrow_points |
Points along the arrow for one principal axis of a frame, from |
translate_frame |
A copy of |
axis_hit |
The point on the axis line nearest the cursor's ray, or None when |
distance_along |
How far a world point sits from the frame's origin along one of |
snapped |
|
frame_from_angles |
A frame turned by |
angles_of |
The fixed-axis X, Y, Z angles in degrees that turn the geometry's |
Functions:¶
rotation_about
¶
The 3x3 rotation of angle radians about a unit axis.
Rodrigues' formula, written out rather than pulled from a library: scipy is not a dependency and this is four lines.
Source code in src/visualdynamics/rotate.py
ring_points
¶
A closed ring of points around one principal axis of a frame.
The ring for the X axis lies in the frame's own Y-Z plane, so it is the circle you would sweep by turning about X.
Source code in src/visualdynamics/rotate.py
rotate_frame
¶
A copy of matrix with its basis turned about one of its own axes.
The axis travels with the frame — turning about X twice by 45 degrees is turning about that same X by 90, not about the world's X.
Source code in src/visualdynamics/rotate.py
angle_in_plane
¶
Where a world point sits around one principal axis, in radians.
Measured in the ring's own plane from the first of the two axes it
spans, so it pairs with ring_points.
Source code in src/visualdynamics/rotate.py
wrapped
¶
An angle folded into [-pi, pi), so a drag past the seam stays small.
A drag from 179 to -179 degrees is two degrees, not 358.
ring_under_cursor
¶
ring_under_cursor(rings: Sequence[tuple[int, ndarray]], cursor: ArrayLike, tolerance: float = 14.0) -> int | None
Which ring a pixel is over, or None.
rings is a list of (axis, screen points). The nearest ring within the
tolerance wins; the rings cross each other, so ties go to whichever is
genuinely closer rather than to whichever was drawn first.
Source code in src/visualdynamics/rotate.py
plane_hit
¶
plane_hit(origin: ArrayLike, normal: ArrayLike, eye: ArrayLike, direction: ArrayLike) -> ndarray | None
Where a ray meets the plane through origin, or None if parallel.
Dragging a ring means following the cursor around the plane the ring lies in, which is what this finds.
Source code in src/visualdynamics/rotate.py
identity_frame
¶
The frame with its rotation undone, keeping where it sits.
Source code in src/visualdynamics/rotate.py
grid_step
¶
The grid a slide lands on, in the display unit: a tenth of an
inch for the inch family, a centimetre for the metric one.
Source code in src/visualdynamics/rotate.py
arrow_points
¶
Points along the arrow for one principal axis of a frame, from
the origin out to length along it — a polyline the way a ring is,
so ring_under_cursor picks an arrow the same way.
Source code in src/visualdynamics/rotate.py
translate_frame
¶
A copy of matrix with its origin slid distance along one of
its own axes; the basis stays put, the mirror of rotate_frame.
Source code in src/visualdynamics/rotate.py
axis_hit
¶
The point on the axis line nearest the cursor's ray, or None when the two are parallel.
Dragging an arrow means following the cursor along a line the cursor can only ever be near, never on: the closest approach of the two lines is where the drag is read, which is what this finds.
Source code in src/visualdynamics/rotate.py
distance_along
¶
How far a world point sits from the frame's origin along one of its axes: the drag's reading, before the snap.
Source code in src/visualdynamics/rotate.py
snapped
¶
point moved to the nearest grid line in each coordinate — the
geometry's own axes, so a turned frame still lands on round
coordinates. A step of zero or less is no snap.
Source code in src/visualdynamics/rotate.py
frame_from_angles
¶
A frame turned by angles, in degrees about the geometry's fixed
X, then Y, then Z axes — the order a person types them in — at
origin. The rows are the frame's axes, as a coordinate system's
are.
Source code in src/visualdynamics/rotate.py
angles_of
¶
The fixed-axis X, Y, Z angles in degrees that turn the geometry's
axes into the frame's (frame_from_angles undone), each in
(-180, 180]. At a quarter turn about Y the X and Z turns are one
turn, and it is said as Z with X at zero.