How can I ensure that a cetz diagram does not extend off of a page?

I’m a new user so I’m not able to upload files. Sorry about that.

So, I’m trying to create a worksheet with exercises. Each exercise involves some text and then a cetz diagram.

One issue that has come up is that a cetz diagram near the end of a page will sometimes extend off of the page instead of jumping to the next page. Ideally, if there is no room for the cetz drawing I would like the entire exercise (the text and associated diagram) to jump to the next page.

I am very new to typst and cetz so even just a pointer towards a place where I can learn more about the technical details that result in this would be great.

Code:

#import "@preview/cetz:0.4.0"

#let exercise_counter = counter("exercise_counter")
#let exercise_block(body) = {
  context {
    exercise_counter.update(1)
  
    block(
      strong(align(center)[Exercises]) + body, 
      fill: luma(230),
      inset: 8pt,
      radius: 4pt,
      width: 100%
    )
  }
}
#let exercise(body, title: none) = {
  context {
    strong({
      exercise_counter.display()
      exercise_counter.step()
      [.]
    
      if title == none {
        [] 
      } else {
        [ #title.] 
      }
    })
  
    [ #body\ ] 
  } 
}

#lorem(300)

#exercise_block[
#lorem(25)

#exercise()[Simple Double Pendulum.]

#align(center)[
#cetz.canvas({
  import cetz.draw: *

  set-style(content: (frame: "rect", stroke: none, padding: .1))

  let theta1 = 65deg
  let theta2 = 45deg

  line(
    (0,0),
    (4,0),
    name: "base"
  )

  line(
    "base.50%",
    (rel: (angle: -theta1, radius: 3)),
    name: "arm1"
  )
  content(
    "arm1.50%",
    [$l_1$],
    anchor: "south-west"
  )
  line(
    "base.50%",
    (rel: (angle: -90deg, radius: 3)),
    stroke: (dash: "dotted")
  )
  arc(
    "base.50%",
    start: -theta1,
    stop: -90deg,
    radius: 1,
    anchor: "origin",
    stroke: (dash: "dotted"),
    name: "arc1"
  )
  content(
    "arc1.end",
    [$phi_1$],
    anchor: "north-west"
  )
  circle(
    "arm1.100%",
    radius: 2pt,
    fill: black
  )
  content(
    "arm1.100%",
    [$m_1$],
    anchor: "south-west"
  )

  line(
    "arm1.100%",
    (rel: (angle: -theta2, radius: 3)),
    name: "arm2"
  )
  content(
    "arm2.50%",
    [$l_2$],
    anchor: "south-west"
  )
  line(
    "arm1.100%",
    (rel: (angle: -90deg, radius: 3)),
    stroke: (dash: "dotted")
  )
  arc(
    "arm1.100%",
    start: -theta2,
    stop: -90deg,
    radius: 1,
    anchor: "origin",
    stroke: (dash: "dotted"),
    name: "arc2"
  )
  content(
    "arc2.center",
    [$phi_1$],
    anchor: "north-west"
  )
  circle(
    "arm2.100%",
    radius: 2pt,
    fill: black
  )
  content(
    "arm2.100%",
    [$m_2$],
    anchor: "north-west"
  )
})
]

#exercise(title: "TITLE")[#lorem(10)]

#exercise(title: "TITLE")[#lorem(5)]

#lorem(20)

#align(center)[
#cetz.canvas({
  import cetz.draw: *

  let theta = -55deg

  line(
    (0,0),
    (4,0),
    name: "base"
  )
  line(
    (2,0),
    (2,-4),
    stroke: (dash: "dotted")
  )

  line(
    "base.50%",
    (rel: (angle: theta, radius: 2)),
    name: "right-arm-1"
  )
  arc(
    "base.50%",
    start: -90deg,
    stop: theta,
    radius: 0.5,
    stroke: (dash: "dotted"),
    anchor: "origin"
  )
  line(
    "base.50%",
    (rel: (angle: 180deg - theta, radius: 2)),
    name: "left-arm-1"
  )

  line(
    "left-arm-1.end",
    (rel: (angle: theta, radius: 2))
  )
  line(
    "right-arm-1.end",
    (rel: (angle: 180deg - theta, radius: 2)),
    name: "right-arm-2"
  )

  circle(
    "left-arm-1.end",
    radius: 2pt,
    fill: black
  )
  circle(
    "right-arm-1.end",
    radius: 2pt,
    fill: black
  )
  circle(
    "right-arm-2.end",
    radius: 2pt,
    fill: black
  )
})
]

#exercise(title: "TITLE")[#lorem(3)]
]

I figured out a solution that works on my own for the moment. I just put the cetz diagram in a block and then set that block to have “breakable: false” so that it doesn’t let itself split.

I’ll mark this as a solution to my own question. If it is more appropriate to just delete my question please let me know.

1 Like

Did you look at posting guidelines?

You supposed to include related code snippets in the post.

The diagram fits exactly onto the page, but since it uses place, it is put onto the page margins. Actually, the previous version works better. Might be a regression.

Cc @jwolf

This is actually a bug. I’ve pushed a fix for the next version! The block element the canvas uses was missing breakable: false.

(Canvas Element Should be Non-Breakable by johannes-wolf · Pull Request #890 · cetz-package/cetz · GitHub)