How to properly define coordinates of auxiliary points when drawing CeTZ arrow?

I tried to modify the @Andrew construction by using line instead of arc-through to make arrows “square” and going from first label to the second and focus only on two labels per function call:

#let logical_step(from, to, shift: 0.2, body) = {
  import "@preview/cetz:0.4.1"
  let elbow-arrow(beg, end, right, body) = {
    import cetz.draw: content, line, set-style

    let p1 = (rel: (0.2, 0), to: beg)
    let p2 = (rel: (right + 0.2 + shift, 0), to: beg)
    let p3 = (rel: (0.2 + shift, 0.15), to: end)
    let p4 = (rel: (0.2, 0.15), to: end)

    let mid = (rel: (0.1, 0), to: (p2, 50%, p3))
    set-style(mark: (end: ">", fill: black, scale: 0.4), stroke: 0.5pt)
    line(
      p1,
      p2,
      p3,
      p4,
    )
    content(mid, anchor: "west", pad(0.5em, body))
  }

  let mod-arrow(f, t, r, body) = elbow-arrow(f, t, r, text(
    size: 7pt,
  )[#set par(leading: 0.3em);#body])

  let arrow-coords(ctx, ..coords, shift: 0pt) = {
    let (_, ..coords) = cetz.coordinate.resolve(ctx, ..coords)
    let right-most = calc.max(..coords.map(v => v.first()))
    (coords, right-most)
  }

  return annot-cetz((from, to), cetz, cetz.draw.get-ctx(ctx => {
    let (beg, end) = arrow-coords(ctx, str(from), str(to)).at(0)
    let right = arrow-coords(ctx, str(from), str(to)).at(1)
    // let (beg, end) = arrow-coords(ctx, "f1", "f2")
    mod-arrow(beg, end, right)[#body]
  }))
}

which produce:


However, because I’m not so experience with cetz I can’t figure out how to properly define the coordinates of auxiliary points. For example, the code above works “fine” when first line is shorter then the second one and breaks otherwise.

FIY, showing a sample output but not giving the source to instantly reproduce it is not helpful. Also, unrelated to a topic question must be made as a separate topic. Only some clarifying questions/better solutions (i.e., the useful stuff) can be posted into an existing topic.

I went ahead and copied an example from How to add arrows between equation lines, like latex `witharrows`, in Typst? - #5 by Andrew, then figured out what the new API is, and thankfully it also shows the issue.

The right in elbow-arrow is used for 1 of 4 points, hence the problem. Either use it for p3 too, or make it relative to p2, or find perpendicular intersection point from p2 and p4.

#import "@preview/mannot:0.3.0": annot-cetz, mark
#import "@preview/cetz:0.4.2"

#let mark-line(tag) = mark(tag: tag)[]

#let logical-step(from, to, shift: 0.2, body) = {
  let elbow-arrow(beg, end, right, body) = {
    import cetz.draw: content, line, set-style

    let p1 = (rel: (0.2, 0), to: beg)
    let p2 = (rel: (right + 0.2 + shift, 0), to: beg)
    let p4 = (rel: (0.2, 0.15), to: end)
    let p3 = (p2, "|-", p4)

    let mid = (rel: (0.1, 0), to: (p2, 50%, p3))
    set-style(mark: (end: ">", fill: black, scale: 0.4), stroke: 0.5pt)
    line(p1, p2, p3, p4)
    content(mid, anchor: "west", pad(0.5em, body))
  }

  let arrow-coords(ctx, ..coords, shift: 0pt) = {
    let (_, ..coords) = cetz.coordinate.resolve(ctx, ..coords)
    let right-most = calc.max(..coords.map(v => v.first()))
    (coords, right-most)
  }

  let mod-arrow(f, t, r, body) = elbow-arrow(f, t, r, {
    set text(size: 7pt)
    set par(leading: 0.3em)
    body
  })

  return annot-cetz((from, to), cetz, cetz.draw.get-ctx(ctx => {
    let ((start, end), right) = arrow-coords(ctx, str(from), str(to))
    // let (beg, end) = arrow-coords(ctx, "f1", "f2")
    mod-arrow(start, end, right)[#body]
  }))
}

$
  x^2 + 5x & = 2x^2 #mark-line(<a>) \
  -x^2 + 5x & = 0 #mark-line(<b>) \
  x_(1,2) & = (-5 plus.minus sqrt(25)) / (-2) #mark-line(<c>) \
  x_(1,2) & = 0, 5 #mark-line(<d>) \
  #logical-step(<a>, <b>)[$-2x^2$]
  #logical-step(<b>, <c>)[Quadratic Formula]
  #logical-step(<c>, <d>)[Simplify]
$

1 Like

Thank you again for the quick and detailed solution.

Sorry for not creating the separate question topic.

1 Like