Skip to content

Commit d573cad

Browse files
Add matrix.transform-rotate-aer (azimuth/elevation/roll) helper (#1034)
This PR adds a convenience rotation constructor to `matrix`: `transform-rotate-aer(azimuth, elevation, roll: 0deg)` ## Behavior - Builds a 4x4 rotation matrix from azimuth/elevation with optional roll. - Uses the viewing convention: - `z` points up - `x` points toward the viewer - aligned with the common azimuth/elevation camera interpretation used by plotting tools (including matplotlib). - Optional roll around the current viewing axis after azimuth/elevation. ## Implementation - Added internal helper: - `_rotate-axis-angle(ax, ay, az, angle)` (axis-angle rotation matrix using the Rodrigues' formula) - Added public function: - `transform-rotate-aer(azimuth, elevation, roll: 0deg)`
2 parents 0d90a47 + 69d4444 commit d573cad

File tree

1 file changed

+35
-0
lines changed

1 file changed

+35
-0
lines changed

src/matrix.typ

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,41 @@
157157
((cos(angle), -sin(angle), 0, 0), (sin(angle), cos(angle), 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))
158158
}
159159

160+
// 3D rotation matrix around an arbitrary axis (ax, ay, az).
161+
#let _rotate-axis-angle(ax, ay, az, angle) = {
162+
let c = cos(angle)
163+
let s = sin(angle)
164+
(
165+
(ax * ax * (1 - c) + c, ax * ay * (1 - c) - az * s, ax * az * (1 - c) + ay * s, 0),
166+
(ay * ax * (1 - c) + az * s, ay * ay * (1 - c) + c, ay * az * (1 - c) - ax * s, 0),
167+
(az * ax * (1 - c) - ay * s, az * ay * (1 - c) + ax * s, az * az * (1 - c) + c, 0),
168+
(0, 0, 0, 1),
169+
)
170+
}
171+
172+
/// Returns a $4 times 4$ rotation matrix from azimuth/elevation/roll.
173+
/// Assumes the viewing convention where $z$ points up and $x$ points toward the viewer.
174+
/// - azimuth (angle): Rotation around z.
175+
/// - elevation (angle): Tilt above the xy plane.
176+
/// - roll (angle): Rotation around the current viewing axis.
177+
/// -> matrix
178+
#let transform-rotate-aer(azimuth, elevation, roll: 0deg) = {
179+
let rotate-z-up = transform-rotate-x(-90deg)
180+
let rotate-azimuth = transform-rotate-z(-90deg - azimuth)
181+
let (ax, ay, az) = (-sin(azimuth), cos(azimuth), 0)
182+
let rotate-elevation = _rotate-axis-angle(ax, ay, az, elevation)
183+
let base = mul-mat(rotate-z-up, rotate-azimuth, rotate-elevation)
184+
185+
if roll == 0deg {
186+
return base
187+
}
188+
189+
// Roll around the current viewing axis after azimuth/elevation.
190+
let (vx, vy, vz) = vector.norm(mul4x4-vec3(base, (1, 0, 0), w: 0))
191+
let rotate-roll = _rotate-axis-angle(vx, vy, vz, roll)
192+
mul-mat(rotate-roll, base)
193+
}
194+
160195
// Return 4x4 rotate xz matrix
161196
/// Returns a $4 times 4$ $x z$ rotation matrix
162197
/// - x (angle): The angle to rotate around the $x$ axis

0 commit comments

Comments
 (0)