How can I cleanly create dynamic annotations for equations?

I am trying to create some slides with touying and mannot to annotate equations. I would like the annotations to appear dynamically (i.e. to appear upon a click).

Here is a MWE that clumsily achieves what I want:

#import "@preview/mannot:0.3.0": *
#import "@preview/touying:0.6.1": *
#import themes.simple: *

#show: simple-theme.with(aspect-ratio: "16-9")

#slide(repeat: 3, self => [
  #let (uncover, only, alternatives) = utils.methods(self)

  #let delayedmark(content, tag: none, color: red, start: 2) = {
     let entries = (mark(content, tag: tag, color: black),)*start + (mark(content, tag: tag, color: color),)
     alternatives(repeat-last: true, ..entries)
  }

  #let delayedmarkhl(content, tag: none, color: orange, start: 2) = {
     let entries = (markhl(content, tag: tag, color: white),)*start + (markhl(content, tag: tag, color: color),)
     alternatives(repeat-last: true, ..entries)
  }

  $
    markul(p_i, tag: #<p>) = markrect(
      exp(- delayedmark(beta, tag: #<beta>, color: #red, start: #1) mark(E_i, tag: #<E>, color: #green)),
      tag: #<Boltzmann>, color: #blue,
    ) / delayedmarkhl(sum_j exp(- beta E_j), tag: #<Z>)
  
    #annot(<p>, pos: bottom + left)[Probability of \ state $i$]
    #annot(<E>, pos: top + right, dy: -1em)[Energy]
    #annot(<Boltzmann>, pos: top + left)[Boltzmann factor]
    #pause
    #annot(<beta>, pos: top + left, dy: -1.5em, leader-connect: "elbow")[Inverse temperature]
    #pause
    #annot(<Z>)[Partition function]
  $

])

mwe.pdf (16.2 KB)

This achieves the desired effect but is very clunky. For example, I must redefine delayedmark on each slide I want to use it for because it is local to that slide.

Is there a cleaner way of doing this?

I would think of this code-to-code transformation: (Without testing/knowing the details)

// 1
#let delayedmark(content, tag: none, color: red, start: 2, mark: mark, markcolor: black, alternatives: none) = {
   let entries = (mark(content, tag: tag, color: markcolor),)*start + (mark(content, tag: tag, color: color),)
   alternatives(repeat-last: true, ..entries)
}

// 2
#let mymethods(self) = {
  let (uncover, only, alternatives) = utils.methods(self)
  let dm = delayedmark.with(alternatives: alternatives)
  (uncover, only, alternatives, dm, dm.with(mark: markhl, markcolor: white))
}

// usage per slide
#let (uncover, only, alternatives, delayedmark, delayedmarkhl) = mymethods(self)
1 Like

I have a different suggestion that uses the contextual page number instead of the touying functions. Whether this is cleaner (in your opinion), really depends on the actual equations you are planning to annotate.
I am creating an internal page counter that is incremented each time the equation is shown on a page. The colors used for marking are then set based on the page number. The page counter is unique for each slide by using the internal slide counter, see Touying Counters | Touying.

#import "@preview/mannot:0.3.0": *
#import "@preview/touying:0.6.1": *
#import themes.simple: *

#show: simple-theme.with(aspect-ratio: "16-9")

#slide(
  repeat: 3,
  self => [
    #let rhs = context {
      let page-counter = counter("slide-" + str(utils.slide-counter.get().last()))
      let page-number = page-counter.get().last()
      let betacolor = if page-number == 0 { black } else { red }
      let sumcolor = if page-number <= 1 { white } else { orange }
      $markrect(exp(- mark(beta, tag: #<beta>, color: #betacolor) mark(E_i, tag: #<E>, color: #green)), tag: #<Boltzmann>, color: #blue)/markhl(sum_j exp(- beta E_j), tag: #<Z>, color: #sumcolor)$
      page-counter.step()
    }
    
    $
      markul(p_i, tag: #<p>) = rhs
      #annot(<p>, pos: bottom + left)[Probability of \ state $i$]
      #annot(<E>, pos: top + right, dy: -1em)[Energy]
      #annot(<Boltzmann>, pos: top + left)[Boltzmann factor]
      #pause
      #annot(<beta>, pos: top + left, dy: -1.5em, leader-connect: "elbow")[Inverse temperature]
      #pause
      #annot(<Z>)[Partition function]
    $
    
  ]
)
1 Like

Thanks – in lieu of a completely different solution to the one I’m converging on, this change certainly makes the code cleaner :slight_smile:

Interesting suggestion! Like you say whether or not this is cleaner is slightly a matter of taste but nice to see how to use Touying counters to tackle this