From 1dc0edc34b6b8f278f18182abd5bd3f68bfeb18e Mon Sep 17 00:00:00 2001 From: Jeremie Knuesel Date: Thu, 28 May 2026 16:06:46 +0200 Subject: [PATCH 1/2] matrix: move transform functions to bottom of file This way they can use the other functions in the file. --- src/matrix.typ | 328 ++++++++++++++-------------- tests/transform/transform/ref/1.png | Bin 2141 -> 2852 bytes tests/transform/transform/test.typ | 7 + 3 files changed, 171 insertions(+), 164 deletions(-) diff --git a/src/matrix.typ b/src/matrix.typ index 1cc8d2fc..ff8f6c37 100644 --- a/src/matrix.typ +++ b/src/matrix.typ @@ -75,170 +75,6 @@ mat.map(r => r.map(v => _round(v, digits: precision))) } -/// Returns a $4 times 4$ translation matrix -/// - x (float): The translation in the $x$ direction. -/// - y (float): The translation in the $y$ direction. -/// - z (float): The translation in the $x$ direction. -/// -> matrix -#let transform-translate(x, y, z) = { - ((1, 0, 0, x), (0, 1, 0, y), (0, 0, 1, z), (0, 0, 0, 1)) -} - -/// Returns a $4 times 4$ x-shear matrix -/// - factor (float): The shear in the $x$ direction. -/// -> matrix -#let transform-shear-x(factor) = { - ((1, factor, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) -} - - -/// Returns a $4 times 4$ z-shear matrix -/// - factor (float): The shear in the $z$ direction. -/// -> matrix -#let transform-shear-z(factor) = { - ((1, 0, factor, 0), (0, 1, -factor, 0), (0, 0, 1, 0), (0, 0, 0, 1)) -} - -/// Returns a $4 times 4$ scale matrix -/// - f (float,array,dictionary): The scale factor(s) of the matrix. An {{array}} of at least 3 {{float}}s sets the x, y and z scale factors. A {{dictionary}} sets the scale in the direction of the corresponding x, y and z keys. A single {{float}} sets the scale for all directions. -/// -> matrix -#let transform-scale(f) = { - let (x, y, z) = if type(f) == array { - vector.as-vec(f, init: (1, 1, 1)) - } else if type(f) == dictionary { - (f.at("x", default: 1), f.at("y", default: 1), f.at("z", default: 1)) - } else { - (f, f, f) - } - return ( - (x, 0, 0, 0), - (0, y, 0, 0), - (0, 0, z, 0), - (0, 0, 0, 1), - ) -} - -/// Returns a $4 times 4$ rotation xyz matrix for a direction and up vector -/// - dir (vector): idk -/// - up (vector): idk -/// -> matrix -#let transform-rotate-dir(dir, up) = { - dir = vector.norm(dir.slice(0, 3)) - up = vector.norm(up.slice(0, 3)) - - let (dx, dy, dz) = dir - let (ux, uy, uz) = up - let (rx, ry, rz) = vector.norm(vector.cross(dir, up)) - - ((rx, dx, ux, 0), (ry, dy, uy, 0), (rz, dz, uz, 0), (0, 0, 0, 1)) -} - -// Return 4x4 rotate x matrix -/// Returns a $4 times 4$ $x$ rotation matrix -/// - angle (angle): The angle to rotate around the $x$ axis -/// -> matrix -#let transform-rotate-x(angle) = { - ((1, 0, 0, 0), (0, cos(angle), -sin(angle), 0), (0, sin(angle), cos(angle), 0), (0, 0, 0, 1)) -} - -// Return 4x4 rotate y matrix -/// Returns a $4 times 4$ $y$ rotation matrix -/// - angle (angle): The angle to rotate around the $y$ axis -/// -> matrix -#let transform-rotate-y(angle) = { - ((cos(angle), 0, -sin(angle), 0), (0, 1, 0, 0), (sin(angle), 0, cos(angle), 0), (0, 0, 0, 1)) -} - -// Return 4x4 rotate z matrix -/// Returns a $4 times 4$ $z$ rotation matrix -/// - angle (angle): The angle to rotate around the $z$ axis -/// -> matrix -#let transform-rotate-z(angle) = { - ((cos(angle), -sin(angle), 0, 0), (sin(angle), cos(angle), 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) -} - -// 3D rotation matrix around an arbitrary axis (ax, ay, az). -#let _rotate-axis-angle(ax, ay, az, angle) = { - let c = cos(angle) - let s = sin(angle) - ( - (ax * ax * (1 - c) + c, ax * ay * (1 - c) - az * s, ax * az * (1 - c) + ay * s, 0), - (ay * ax * (1 - c) + az * s, ay * ay * (1 - c) + c, ay * az * (1 - c) - ax * s, 0), - (az * ax * (1 - c) - ay * s, az * ay * (1 - c) + ax * s, az * az * (1 - c) + c, 0), - (0, 0, 0, 1), - ) -} - -/// Returns a $4 times 4$ rotation matrix from azimuth/elevation/roll. -/// Assumes the viewing convention where $z$ points up and $x$ points toward the viewer. -/// - azimuth (angle): Rotation around z. -/// - elevation (angle): Tilt above the xy plane. -/// - roll (angle): Rotation around the current viewing axis. -/// -> matrix -#let transform-rotate-aer(azimuth, elevation, roll: 0deg) = { - let rotate-z-up = transform-rotate-x(-90deg) - let rotate-azimuth = transform-rotate-z(-90deg - azimuth) - let (ax, ay, az) = (-sin(azimuth), cos(azimuth), 0) - let rotate-elevation = _rotate-axis-angle(ax, ay, az, elevation) - let base = mul-mat(rotate-z-up, rotate-azimuth, rotate-elevation) - - if roll == 0deg { - return base - } - - // Roll around the current viewing axis after azimuth/elevation. - let (vx, vy, vz) = vector.norm(mul4x4-vec3(base, (1, 0, 0), w: 0)) - let rotate-roll = _rotate-axis-angle(vx, vy, vz, roll) - mul-mat(rotate-roll, base) -} - -// Return 4x4 rotate xz matrix -/// Returns a $4 times 4$ $x z$ rotation matrix -/// - x (angle): The angle to rotate around the $x$ axis -/// - z (angle): The angle to rotate around the $z$ axis -/// -> matrix -#let transform-rotate-xz(x, z) = { - ( - (cos(z), sin(z), 0, 0), - (-cos(x) * sin(z), cos(x) * cos(z), -sin(x), 0), - (sin(x) * sin(z), -sin(x) * cos(z), cos(x), 1), - (0, 0, 0, 1), - ) -} - -/// Returns a $4 times 4$ rotation matrix - yaw-pitch-roll -/// -/// - a (angle): Yaw -/// - b (angle): Pitch -/// - c (angle): Roll -/// -> matrix -#let transform-rotate-ypr(a, b, c) = { - ( - (cos(a) * cos(b), cos(a) * sin(b) * sin(c) - sin(a) * cos(c), cos(a) * sin(b) * cos(c) + sin(a) * sin(c), 0), - (sin(a) * cos(b), sin(a) * sin(b) * sin(c) + cos(a) * cos(c), sin(a) * sin(b) * cos(c) - cos(a) * sin(c), 0), - (-sin(b), cos(b) * sin(c), cos(b) * cos(c), 1), - (0, 0, 0, 1), - ) -} - -/// Returns a $4 times 4$ rotation matrix - euler angles -/// -/// Calculates the product of the three rotation matrices -/// $R = R_z(z) R_y(y) R_x(x)$ -/// -/// - x (angle): Rotation about x -/// - y (angle): Rotation about y -/// - z (angle): Rotation about z -/// -> matrix -#let transform-rotate-xyz(x, y, z) = { - ( - (cos(y) * cos(z), sin(x) * sin(y) * cos(z) - cos(x) * sin(z), cos(x) * sin(y) * cos(z) + sin(x) * sin(z), 0), - (cos(y) * sin(z), sin(x) * sin(y) * sin(z) + cos(x) * cos(z), cos(x) * sin(y) * sin(z) - sin(x) * cos(z), 0), - (-sin(y), sin(x) * cos(y), cos(x) * cos(y), 0), - (0, 0, 0, 1), - ) -} - /// Multiplies matrices on top of each other. /// - ..matrices (matrix): The matrices to multiply from left to right. /// -> matrix @@ -464,3 +300,167 @@ return inverted } + +/// Returns a $4 times 4$ translation matrix +/// - x (float): The translation in the $x$ direction. +/// - y (float): The translation in the $y$ direction. +/// - z (float): The translation in the $x$ direction. +/// -> matrix +#let transform-translate(x, y, z) = { + ((1, 0, 0, x), (0, 1, 0, y), (0, 0, 1, z), (0, 0, 0, 1)) +} + +/// Returns a $4 times 4$ x-shear matrix +/// - factor (float): The shear in the $x$ direction. +/// -> matrix +#let transform-shear-x(factor) = { + ((1, factor, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) +} + + +/// Returns a $4 times 4$ z-shear matrix +/// - factor (float): The shear in the $z$ direction. +/// -> matrix +#let transform-shear-z(factor) = { + ((1, 0, factor, 0), (0, 1, -factor, 0), (0, 0, 1, 0), (0, 0, 0, 1)) +} + +/// Returns a $4 times 4$ scale matrix +/// - f (float,array,dictionary): The scale factor(s) of the matrix. An {{array}} of at least 3 {{float}}s sets the x, y and z scale factors. A {{dictionary}} sets the scale in the direction of the corresponding x, y and z keys. A single {{float}} sets the scale for all directions. +/// -> matrix +#let transform-scale(f) = { + let (x, y, z) = if type(f) == array { + vector.as-vec(f, init: (1, 1, 1)) + } else if type(f) == dictionary { + (f.at("x", default: 1), f.at("y", default: 1), f.at("z", default: 1)) + } else { + (f, f, f) + } + return ( + (x, 0, 0, 0), + (0, y, 0, 0), + (0, 0, z, 0), + (0, 0, 0, 1), + ) +} + +/// Returns a $4 times 4$ rotation xyz matrix for a direction and up vector +/// - dir (vector): idk +/// - up (vector): idk +/// -> matrix +#let transform-rotate-dir(dir, up) = { + dir = vector.norm(dir.slice(0, 3)) + up = vector.norm(up.slice(0, 3)) + + let (dx, dy, dz) = dir + let (ux, uy, uz) = up + let (rx, ry, rz) = vector.norm(vector.cross(dir, up)) + + ((rx, dx, ux, 0), (ry, dy, uy, 0), (rz, dz, uz, 0), (0, 0, 0, 1)) +} + +// Return 4x4 rotate x matrix +/// Returns a $4 times 4$ $x$ rotation matrix +/// - angle (angle): The angle to rotate around the $x$ axis +/// -> matrix +#let transform-rotate-x(angle) = { + ((1, 0, 0, 0), (0, cos(angle), -sin(angle), 0), (0, sin(angle), cos(angle), 0), (0, 0, 0, 1)) +} + +// Return 4x4 rotate y matrix +/// Returns a $4 times 4$ $y$ rotation matrix +/// - angle (angle): The angle to rotate around the $y$ axis +/// -> matrix +#let transform-rotate-y(angle) = { + ((cos(angle), 0, -sin(angle), 0), (0, 1, 0, 0), (sin(angle), 0, cos(angle), 0), (0, 0, 0, 1)) +} + +// Return 4x4 rotate z matrix +/// Returns a $4 times 4$ $z$ rotation matrix +/// - angle (angle): The angle to rotate around the $z$ axis +/// -> matrix +#let transform-rotate-z(angle) = { + ((cos(angle), -sin(angle), 0, 0), (sin(angle), cos(angle), 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) +} + +// 3D rotation matrix around an arbitrary axis (ax, ay, az). +#let _rotate-axis-angle(ax, ay, az, angle) = { + let c = cos(angle) + let s = sin(angle) + ( + (ax * ax * (1 - c) + c, ax * ay * (1 - c) - az * s, ax * az * (1 - c) + ay * s, 0), + (ay * ax * (1 - c) + az * s, ay * ay * (1 - c) + c, ay * az * (1 - c) - ax * s, 0), + (az * ax * (1 - c) - ay * s, az * ay * (1 - c) + ax * s, az * az * (1 - c) + c, 0), + (0, 0, 0, 1), + ) +} + +/// Returns a $4 times 4$ rotation matrix from azimuth/elevation/roll. +/// Assumes the viewing convention where $z$ points up and $x$ points toward the viewer. +/// - azimuth (angle): Rotation around z. +/// - elevation (angle): Tilt above the xy plane. +/// - roll (angle): Rotation around the current viewing axis. +/// -> matrix +#let transform-rotate-aer(azimuth, elevation, roll: 0deg) = { + let rotate-z-up = transform-rotate-x(-90deg) + let rotate-azimuth = transform-rotate-z(-90deg - azimuth) + let (ax, ay, az) = (-sin(azimuth), cos(azimuth), 0) + let rotate-elevation = _rotate-axis-angle(ax, ay, az, elevation) + let base = mul-mat(rotate-z-up, rotate-azimuth, rotate-elevation) + + if roll == 0deg { + return base + } + + // Roll around the current viewing axis after azimuth/elevation. + let (vx, vy, vz) = vector.norm(mul4x4-vec3(base, (1, 0, 0), w: 0)) + let rotate-roll = _rotate-axis-angle(vx, vy, vz, roll) + mul-mat(rotate-roll, base) +} + +// Return 4x4 rotate xz matrix +/// Returns a $4 times 4$ $x z$ rotation matrix +/// - x (angle): The angle to rotate around the $x$ axis +/// - z (angle): The angle to rotate around the $z$ axis +/// -> matrix +#let transform-rotate-xz(x, z) = { + ( + (cos(z), sin(z), 0, 0), + (-cos(x) * sin(z), cos(x) * cos(z), -sin(x), 0), + (sin(x) * sin(z), -sin(x) * cos(z), cos(x), 1), + (0, 0, 0, 1), + ) +} + +/// Returns a $4 times 4$ rotation matrix - yaw-pitch-roll +/// +/// - a (angle): Yaw +/// - b (angle): Pitch +/// - c (angle): Roll +/// -> matrix +#let transform-rotate-ypr(a, b, c) = { + ( + (cos(a) * cos(b), cos(a) * sin(b) * sin(c) - sin(a) * cos(c), cos(a) * sin(b) * cos(c) + sin(a) * sin(c), 0), + (sin(a) * cos(b), sin(a) * sin(b) * sin(c) + cos(a) * cos(c), sin(a) * sin(b) * cos(c) - cos(a) * sin(c), 0), + (-sin(b), cos(b) * sin(c), cos(b) * cos(c), 1), + (0, 0, 0, 1), + ) +} + +/// Returns a $4 times 4$ rotation matrix - euler angles +/// +/// Calculates the product of the three rotation matrices +/// $R = R_z(z) R_y(y) R_x(x)$ +/// +/// - x (angle): Rotation about x +/// - y (angle): Rotation about y +/// - z (angle): Rotation about z +/// -> matrix +#let transform-rotate-xyz(x, y, z) = { + ( + (cos(y) * cos(z), sin(x) * sin(y) * cos(z) - cos(x) * sin(z), cos(x) * sin(y) * cos(z) + sin(x) * sin(z), 0), + (cos(y) * sin(z), sin(x) * sin(y) * sin(z) + cos(x) * cos(z), cos(x) * sin(y) * sin(z) - sin(x) * cos(z), 0), + (-sin(y), sin(x) * cos(y), cos(x) * cos(y), 0), + (0, 0, 0, 1), + ) +} diff --git a/tests/transform/transform/ref/1.png b/tests/transform/transform/ref/1.png index fb0a4adfd59a3592803c21c7d538b7aef42e59da..3e1d68018f923237233066bf22d6105a518e1fa5 100644 GIT binary patch literal 2852 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 diff --git a/tests/transform/transform/test.typ b/tests/transform/transform/test.typ index 50ead3e8..65094ad9 100644 --- a/tests/transform/transform/test.typ +++ b/tests/transform/transform/test.typ @@ -19,3 +19,10 @@ transform(matrix.transform-scale((1, 0.5, 1))) arrow }) + +#test-case({ + import draw: * + + transform(matrix.transform-rotate-aer(10deg, 20deg, roll: 30deg)) + arrow +}) From c002e52aea835464a6034fad4c2176441b3421bb Mon Sep 17 00:00:00 2001 From: Jeremie Knuesel Date: Fri, 29 May 2026 09:01:15 +0200 Subject: [PATCH 2/2] Fix typos in rotation transforms --- src/matrix.typ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/matrix.typ b/src/matrix.typ index ff8f6c37..36943df8 100644 --- a/src/matrix.typ +++ b/src/matrix.typ @@ -427,7 +427,7 @@ ( (cos(z), sin(z), 0, 0), (-cos(x) * sin(z), cos(x) * cos(z), -sin(x), 0), - (sin(x) * sin(z), -sin(x) * cos(z), cos(x), 1), + (sin(x) * sin(z), -sin(x) * cos(z), cos(x), 0), (0, 0, 0, 1), ) } @@ -442,7 +442,7 @@ ( (cos(a) * cos(b), cos(a) * sin(b) * sin(c) - sin(a) * cos(c), cos(a) * sin(b) * cos(c) + sin(a) * sin(c), 0), (sin(a) * cos(b), sin(a) * sin(b) * sin(c) + cos(a) * cos(c), sin(a) * sin(b) * cos(c) - cos(a) * sin(c), 0), - (-sin(b), cos(b) * sin(c), cos(b) * cos(c), 1), + (-sin(b), cos(b) * sin(c), cos(b) * cos(c), 0), (0, 0, 0, 1), ) }