Mannot:0.3.0 - Now supports math annotations using CeTZ

Hey everyone!:wave:
I’m excited to announce the release of mannot v0.3.0, the Typst package for marking and annotating elements within your math blocks!

This new version brings some exciting changes and improvements:
:arrow_right: Flexible Leader Lines: The annot function now supports direct, straight leader lines (in addition to elbow-shaped ones). Also, you can customize your arrowheads using tiptoe package.
:art: CeTZ Annotations: Unleash the power of graphical annotations with CeTZ canvas support using the new annot-cetz function.

Your feedback is always appreciated!

Links

Examples

#import "@preview/mannot:0.3.0": *

#set page(width: auto, height: auto, margin: (left: 4cm, top: 2cm, rest: 1cm), fill: white)
#set text(24pt)

$
  markul(p_i, tag: #<p>) = markrect(
    exp(- mark(beta, tag: #<beta>, color: #red) mark(E_i, tag: #<E>, color: #green)),
    tag: #<Boltzmann>, color: #blue,
  ) / markhl(sum_j exp(- beta E_j), tag: #<Z>)

  #annot(<p>, pos: bottom + left)[Probability of \ state $i$]
  #annot(<beta>, pos: top + left, dy: -1.5em, leader-connect: "elbow")[Inverse temperature]
  #annot(<E>, pos: top + right, dy: -1em)[Energy]
  #annot(<Boltzmann>, pos: top + left)[Boltzmann factor]
  #annot(<Z>)[Partition function]
$

#import "@preview/mannot:0.3.0": *
#import "@preview/cetz:0.3.4"

#set page(width: auto, height: auto, margin: (y: 2cm, bottom: 1cm), fill: white)
#set text(size: 24pt, fill: black)

#let rmark = mark.with(color: red)
#let bmark = mark.with(color: blue)
#let pmark = mark.with(color: purple)

$
  ( rmark(a x, tag: #<ax>) + bmark(b, tag: #<b>) )
  ( rmark(c x, tag: #<cx>) + bmark(d, tag: #<d>) )
  = rmark(a c x^2) + pmark((a d + b c) x) + bmark(b d)
$

#annot-cetz(
  (<ax>, <b>, <cx>, <d>),
  cetz,
  {
    import cetz.draw: *
    set-style(mark: (end: "straight"))
    bezier-through("ax.south", (rel: (x: 1, y: -.5)), "cx.south", stroke: red)
    bezier-through("ax.south", (rel: (x: 1, y: -1)), "d.south", stroke: purple)
    bezier-through("b.north", (rel: (x: .6, y: .5)), "cx.north", stroke: purple)
    bezier-through("b.north", (rel: (x: .6, y: 1)), "d.north", stroke: blue)
  },
)

#import "@preview/mannot:0.3.0": *
#import "@preview/cetz:0.3.4"

#set page(width: auto, height: auto, margin: (x: 4cm, top: 2cm, bottom: 1cm), fill: white)
#set text(size: 24pt, fill: black)

#let markhl = markhl.with(stroke: 1pt)

$
  markhl(1 mark(., tag: #<sep>) 23, tag: #<mantissa>, color: #red)
  mark(
    mark(times, tag: #<prd>)
    mark(10, tag: #<base>)^mark(4, tag: #<exp>),
    tag: #<pow>,
  )
$

#{
  let annot = annot.with(leader-tip: tiptoe.triangle, leader-toe: none)
  annot(<mantissa>, pos: left, dx: -.5em, dy: -1em, annot-text-props: (size: .9em))[mantissa]

  let annot = annot.with(leader-stroke: .03em, leader-tip: none, leader-toe: none)
  annot(<sep>, pos: bottom + left, dx: -.5em)[decimal \ separator]
  annot(<prd>, pos: top, dx: -1em, dy: -1.2em)[product]
  annot(<base>, pos: top, dy: -1em)[base]
  annot(<exp>, pos: top + right, dx: 1em)[exponent]

  annot-cetz(
    <pow>,
    cetz,
    {
      import cetz.draw: *
      cetz.decorations.flat-brace(
        "pow.south-west",
        "pow.south-east",
        flip: true,
        name: "brace",
        stroke: blue,
      )
      content("brace.south", anchor: "north", text(blue, .9em)[power])
    },
  )
}

3

12 Likes

This is sick! I think I know a professor that will love this. To be fair, I think any complex math expressions can benefit from this, especially in learning materials.

I think the syntax is minimalistic enough, though still is bulky compared to not annotated expressions, but then they are not annotated. Pretty cool stuff.

1 Like

I like the decoupling of marking and annotating a lot, usually with stuff like this the core expression becomes way too bloated and hard to read. Even though it’s still a bit bulky, I think it works fine like this! is there a case where one wouldn’t use the tag? because I think changing the named parameter to a positional one could make it a bit shorter. I shorter syntax is okay and desirable in this case.

1 Like

Thank you for your feedback!

Regarding your question about when one wouldn’t use the tag: for example, if you simply want to highlight a part of the equation, you can just call the markhl function without providing a tag:

$  markhl(x + y) $

5

And you’re right, making the tag argument positional could indeed make the syntax a bit shorter in many cases. I agree that a shorter syntax is often desirable here. With careful parsing of the arguments, it might be possible to create a tag argument that is both optional and positional, which could be quite beneficial.

wouldn’t users simply use the highlight method then? Highlight Function – Typst Documentation

highlight unfortunately doesn’t work (it doesn’t draw any highlight) on or inside equations

1 Like

Also, to decorate math equations without affecting the layouts, markrect and markul might be better than rect and underline:

- `rect` & `underline`
$
  y = #rect($ y $) "or" #rect($y$)
  = f(underline(y))
$

- `markrect` & `markul`
$
  y = markrect(y)
  = f(markul(y))
$

5

2 Likes