How can I overlay cetz diagrams on top of code?

In LaTeX, using \tikzmarknode, I can draw tikz pictures directly on top of code listings. e.g.

Is there a way to do something similar with cetz and triple-backquote code segments? I could not find any way to place markers like tikzmark inside a triple backquote block.

1 Like

Does this count?

#import "@preview/cetz:0.3.4"

#let node(body) = block(
  stroke: 1pt + rgb("#706898"),
  fill: rgb("#ebe8e8"),
  inset: 0.3em,
  radius: 0.5em,
  text(0.6em, body),
)

#let line = cetz.draw.line.with(
  mark: (start: "o", end: (symbol: "stealth", fill: black), scale: 0.7),
  stroke: (dash: "dashed", thickness: 0.5pt),
)

#let code = ```
example : ∀ (p q: Prop), p ∨ q → q ∨ p := by
  intro p q h
  cases h
  ⋅ apply Or.inr
    assumption
  ⋅ apply Or.inl
    assumption
```

#let node1 = node($
  p, q & : sans("Prop") \
  h & : p or q \
  & tack q or p
$)

#let node2 = node($
  p, q & : sans("Prop") \
  h & : p \
  & tack q or p \
  p, q & : sans("Prop") \
  h & : q \
  & tack q or p
$)

#cetz.canvas({
  import cetz.draw: content
  content((0, 0), anchor: "north-west", code)
  content((6, -1.5), name: "node1", node1)
  content((4.4, -2.8), name: "node2", node2)
  line((2.6, -0.55), "node1")
  line((1.9, -0.95), "node2")
})
output

1 Like

Is there a way to determine the coordinate of the two nodes automatically?

“Automatically” how?

It’s certainly possible to some extent. You can use codly for a code display with many features, for example line highlights and line labels (detailed by code line and code column).

The label gives you access to the absolute position of that word in the code through the query system. I wonder if there’s some way to use labels as coordinates in cetz.

Proof of concept / "bad example"

This example shows that it is possible. I think it would be better to do this with cetz instead of typst drawing primitives. As seen in the example, the text after the figure starts without taking the extent of the annotation boxes into account.

#import "@preview/codly:1.3.0": *
#show: codly-init.with()

// Testing out codly's feature that allows labels on individual lines of code

#let code = raw(block: true, 
"example : ∀ (p q: Prop), p ∨ q → q ∨ p := by
  intro p q h
  cases h
  ⋅ apply Or.inr
    assumption
  ⋅ apply Or.inl
    assumption
")
#codly(highlights:(
  (line: 2, start: 3, end: none, label: <mark1>),
  (line: 3, start: 3, end: none, label: <mark2>),
), annotations: (
))
#figure(
  code
)<myfigure>

Test my labels @mark1 and @mark2


/// Draw a line from the label's code line down to a box
/// xoffset: x-offset from label coordinate to the start of the line
/// xline, yline: length of the line to the box.
#let drawbox(at-label, content, yoffset: 1em, xoffset: 20em, xline: 5em, yline: 5em,
  line-style: (:), block-style: (:)) = context {
  let codeline = query(at-label).first()
  let pos = codeline.location().position()
  let xstart = pos.x - 30mm // page margin, needs to be updated
  let ystart = pos.y - 30mm

  let xbox = xstart + xoffset + xline 
  let ybox = ystart + yoffset + yline
  place(top + left, dx: xoffset + xstart, dy: ystart + yoffset,
    line(start: (0%, 0%), end: (xline, yline), ..line-style))
  place(top + left, dx: xbox, dy: ybox, block(..block-style, content))
}

#let drawbox = drawbox.with(
  block-style: (inset: 0.75em, stroke: 0.5pt + purple.darken(60%), fill: gray.lighten(90%).transparentize(25%), radius: 0.5em),
  line-style: (stroke: (thickness: 0.5pt, dash: "dashed")))

#let annot1 = $
  p, q & : sans("Prop") \
  h & : p or q \
  & tack q or p
$
#drawbox(<mark1>, annot1, xline: 4em, yline: 1em)

#let annot2 = $
  p, q & : sans("Prop") \
  h & : p \
  & tack q or p \
  p, q & : sans("Prop") \
  h & : q \
  & tack q or p
$

#drawbox(<mark2>, annot2, yline: 2em, xoffset: 11.5em)


#lorem(20)
2 Likes

Automatically in the sense that I can put a mark object after intro p q h and use a name to refer to that object’s coordinate.

Well, there is no easy way to do it other than what Codly provides, or recreating it from scratch, which I assume is pretty trivial. Does How can I overlay cetz diagrams on top of code? - #5 by bluss work for you?