Skip to content

Commit

Permalink
grouping: Handle none as empty array (#491)
Browse files Browse the repository at this point in the history
Allows passing `none` or `ctx => {}` to grouping functions (`group`,
`on-layer`, ...).
This is very useful when having conditional draw commands insides a
group.

Example:
```
group(ctx => {
  if .. {
    // If this does not get evaluated, cetz currently fails because the group returned `none`.
    // Current workaround is writing () at the end of such groups to auto-join an empty array.
  }
})
```

This PR is an improved implementation of the one rejected from #479.
  • Loading branch information
johannes-wolf authored Feb 11, 2024
1 parent b36ffd0 commit 5bc5fc3
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 18 deletions.
24 changes: 12 additions & 12 deletions src/draw/grouping.typ
Original file line number Diff line number Diff line change
Expand Up @@ -192,21 +192,20 @@
/// - name (none, string):
/// - ..style (style):
#let group(body, name: none, anchor: none, ..style) = {
assert(type(body) in (array, function), message: "Incorrect type for body, expected an array or function. Instead got: " + repr(body))
// No extra positional arguments from the style sink
assert.eq(
style.pos(),
(),
message: "Unexpected positional arguments: " + repr(style.pos()),
)
assert.eq(style.pos(), (),
message: "Unexpected positional arguments: " + repr(style.pos()),)
util.assert-body(body)

(ctx => {
let style = styles.resolve(ctx.style, merge: style.named(), root: "group")

let bounds = none
let drawables = ()
let group-ctx = ctx
group-ctx.groups.push((anchors: (:)))
(ctx: group-ctx, drawables, bounds) = process.many(group-ctx, if type(body) == function {body(ctx)} else {body})

(ctx: group-ctx, drawables, bounds) = process.many(group-ctx, util.resolve-body(group-ctx, body))

// Apply bounds padding
let bounds = if bounds != none {
Expand Down Expand Up @@ -432,13 +431,14 @@
/// ```)
///
/// - layer (float, integer): The layer to place the elements on. Elements placed without `on-layer` are always placed on layer 0.
/// - body (elements): Elements to draw on the layer specified.
/// - body (elements, function): Elements to draw on the layer specified. A function that accepts `ctx` and returns elements is also accepted.
#let on-layer(layer, body) = {
assert(type(layer) in (int, float), message: "Layer must be a float or integer, 0 being the default layer. Got: " + repr(layer))
assert(type(body) in (function, array))
return (ctx => {
let (ctx, drawables, ..) = process.many(ctx, if type(body) == function { body(ctx) } else { body })
util.assert-body(body)
assert(type(layer) in (int, float),
message: "Layer must be a float or integer, 0 being the default layer. Got: " + repr(layer))

return (ctx => {
let (ctx, drawables, ..) = process.many(ctx, util.resolve-body(ctx, body))
drawables = drawables.map(d => {
if d.at("z-index", default: none) == none {
d.z-index = layer
Expand Down
22 changes: 22 additions & 0 deletions src/util.typ
Original file line number Diff line number Diff line change
Expand Up @@ -363,3 +363,25 @@
}
return stroke
}

// Function for early checking the type of
// a body (element) argument.
#let assert-body(body) = {
assert(body == none or type(body) in (array, function),
message: "Body must be of type none, array or function")
}

// Returns body if of type array, an
// empty array if body is none or
// the result of body called with ctx if of type
// function. A function result of none will return
// an empty array.
#let resolve-body(ctx, body) = {
if type(body) == function {
body = body(ctx)
}
if body == none {
body = ()
}
return body
}
Binary file added tests/group/empty/ref/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions tests/group/empty/test.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#import "/src/lib.typ": *
#import "/tests/helper.typ": *

#test-case({
import draw: *

group(none)
group(ctx => none)
})
Binary file modified tests/layer/ref/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 13 additions & 6 deletions tests/layer/test.typ
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#set page(width: auto, height: auto)
#import "/src/lib.typ": *
#import "/tests/helper.typ": *

#box(stroke: 2pt + red, canvas({
#test-case({
import draw: *

circle((0,0), fill: red, stroke: none)
Expand All @@ -11,9 +12,9 @@
on-layer(1, {
circle((2, 0), fill: blue, stroke: none)
})
}))
})

#box(stroke: 2pt + red, canvas({
#test-case({
import draw: *

on-layer(2, {
Expand All @@ -23,10 +24,10 @@
circle((1, 0), fill: green, stroke: none)
})
circle((0,0), fill: red, stroke: none)
}))
})

// Test nested layers
#box(stroke: 2pt + red, canvas({
#test-case({
import draw: *

on-layer(1, {
Expand All @@ -40,4 +41,10 @@
content("c2.center", [Green])
})
content((0,0), [Red])
}))
})

#test-case({
import draw: *
on-layer(1, none)
on-layer(1, ctx => none)
})

0 comments on commit 5bc5fc3

Please sign in to comment.