Skip to content

Commit

Permalink
transform: Add option for setting the origin
Browse files Browse the repository at this point in the history
  • Loading branch information
johannes-wolf committed Dec 15, 2023
1 parent c16a555 commit 2e9b213
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/draw/shapes.typ
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@
y *= y-step
y += from.at(1)
drawable.path(
path-util.line-segment(((from.at(0), y), (to.at(1), y))),
path-util.line-segment(((from.at(0), y), (to.at(0), y))),
stroke: style.stroke
)
})
Expand Down
30 changes: 26 additions & 4 deletions src/draw/transformations.typ
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@
#import "/src/vector.typ"
#import "/src/util.typ"

// Utility for applying translation to and from
// the origin to apply a transformation matrix to.
//
// - transform (matrix): Transformation matrix
// - origin (vector): Origin vector or none
#let _transform-around-origin(ctx, transform, origin) = {
if origin != none {
let (_, origin) = coordinate.resolve(ctx, origin, update: false)
let a = matrix.transform-translate(..origin)
let b = matrix.transform-translate(..vector.scale(origin, -1))

matrix.mul-mat(a, matrix.mul-mat(transform, b))
} else {
transform
}
}

/// Sets the transformation matrix.
///
/// - mat (none, matrix): The 4x4 transformation matrix to set. If `none` is passed, the transformation matrix is set to the identity matrix (`matrix.ident()`).
Expand Down Expand Up @@ -43,7 +60,8 @@
/// - ..angles (angle): A single angle as a positional argument to rotate on the z-axis by.
/// Named arguments of `x`, `y` or `z` can be given to rotate on their respective axis.
/// You can give named arguments of `yaw`, `pitch` or `roll`, too.
#let rotate(..angles) = {
/// - origin (none,coordinate): Origin to rotate around, or (0, 0, 0) if set to `none`.
#let rotate(..angles, origin: none) = {
assert(angles.pos().len() == 1 or angles.named().len() > 0,
message: "Rotate takes a single z-angle or angles " +
"(x, y, z or yaw, pitch, roll) as named arguments, got: " + repr(angles))
Expand All @@ -68,7 +86,8 @@
}

(ctx => {
ctx.transform = matrix.mul-mat(ctx.transform, mat)
ctx.transform = matrix.mul-mat(ctx.transform,
_transform-around-origin(ctx, mat, origin))
return (ctx: ctx)
},)
}
Expand Down Expand Up @@ -131,7 +150,8 @@
/// - ..args (float, ratio): A single value to scale the transformation matrix by or per axis
/// scaling factors. Accepts a single float or ratio value or any combination of the named arguments
/// `x`, `y` and `z` to set per axis scaling factors. A ratio of 100% is the same as the value $1$.
#let scale(..args) = {
/// - origin (none,coordinate): Origin to rotate around, or (0, 0, 0) if set to `none`.
#let scale(..args, origin: none) = {
assert((args.pos().len() == 1 and args.named() == (:)) or
(args.pos() == () and args.named() != (:)),
message: "Expected a single positional argument or one or more named arguments, got: " + repr(args))
Expand All @@ -156,7 +176,9 @@
})

(ctx => {
ctx.transform = matrix.mul-mat(ctx.transform, matrix.transform-scale(vec))
let mat = matrix.transform-scale(vec)
ctx.transform = matrix.mul-mat(ctx.transform,
_transform-around-origin(ctx, mat, origin))
return (ctx: ctx)
},)
}
Expand Down
75 changes: 75 additions & 0 deletions tests/rotation/around/test.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#set page(width: auto, height: auto)
#import "/src/lib.typ": *
#import "/tests/helper.typ": *

#let draw-shapes() = {
import draw: *

rect((-2,-2), (-1,-1))
circle((-1.5,1.5), radius: .5)
line((1,1), (1.5,2), (2,1), close: true)
}

#test-case({
import draw: *
grid((-3,-3), (3,3))

set-style(fill: gray)
draw-shapes()

rotate(45deg, origin: (0,0))
set-style(fill: blue)
draw-shapes()
})

#test-case({
import draw: *
grid((-3,-3), (3,5))

set-style(fill: gray)
draw-shapes()

rotate(45deg, origin: (-1.5,1.5))
set-style(fill: blue)
draw-shapes()
})

#test-case({
import draw: *
grid((-5,-5), (1,1))

scale(2, origin: (-2,-2))
set-style(fill: blue)
rect((-3,-3), (rel: (1,1)))
rect((-2,-3), (rel: (1,1)))
rect((-2,-2), (rel: (1,1)))
rect((-3,-2), (rel: (1,1)))
})

#test-case({
import draw: *
grid((-4,-4), (2,2))

set-style(fill: gray)
rect((-3,-3), (rel: (2,2)))

rotate(45deg, origin: (-2,-2))
scale(.5, origin: (-2,-2))
set-style(fill: blue)
rect((-3,-3), (rel: (2,2)))
})

#test-case({
import draw: *
grid((-4,-4), (2,2))

set-transform(none)
scale(y: -1)

set-style(fill: gray)
rect((-3,-3), (rel: (2,2)))

rotate(x: 60deg, y: 45deg, origin: (-2,-2))
set-style(fill: blue)
rect((-3,-3), (rel: (2,2)))
})
2 changes: 1 addition & 1 deletion tests/rotation/test.typ
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#set page(width: auto, height: auto)
#import "../../src/lib.typ": *
#import "/src/lib.typ": *

#box(stroke: 2pt + red, canvas({
import draw: *
Expand Down

0 comments on commit 2e9b213

Please sign in to comment.