Jollywatt / typst-fletcher

Typst package for drawing diagrams with arrows, built on top of CeTZ.
MIT License
270 stars 6 forks source link

Subscripts affect vertical alignment in an undesired way #44

Open hooyuser opened 4 days ago

hooyuser commented 4 days ago

The vertical alignment of the subscripts seems to be off, which disrupts the readability of the formula. Here is the link of the code snippet: https://typst.app/project/rcUVH6fe_sLns_uLrFA4tE

Expected Behavior

The formula is expected to align with the baseline of $\sum$ , ignoring the subscripts $i \in U$.

fletcher_alignment

$ sum_(i in  U) F(U)-->F(U) $

Current Behavior

The subscripts in the formula affect the vertical alignment, causing them to appear misaligned with the rest of the formula elements.

fletcher_alignment_2

#diagram({
  let (A, B) = ((0,0), (1,0))
  node(A, $ sum_(i in  U) F(U) $)
  node(B, $ F(U)$)
  edge(A, B, "->")
})

#diagram($
  limits(sum)_(i in  U) F(U)edge("->")& F(U)
$)

Is there a way to manually adjust or change the alignment for subscripts? Any guidance or documentation on this would be greatly appreciated. I'm also wondering if this misalignment should be adjusted to improve the default behavior.

Jollywatt commented 3 days ago

Hmm. This occurs because each node gets its own bounding box and is aligned separately. But I agree that it's probably more desirable to align by baseline for diagrams in math mode.

I'm not sure how this could be made automatic, since fundamentally each node exists in its own box, so baseline information is lost.

In the meantime, a non-ideal workaround is to use move(dy: ..) to manually adjust:

#diagram($
  #move(dy: .35em, $ limits(sum)_(i in  U) F(U) $) edge("->") & F(U)
$)
hooyuser commented 3 days ago

I may not have fully considered this yet, but here's a quick thought: could we allow an alignment pivot point that is different from the center of the bounding box? Consider a naive implementation for trimming attachments (see https://typst.app/project/rYLF85ECFVpZUGgR5w_G3p) :

#let trim_attachments(x) = {
  if repr(x.func()) == "equation" and x.block {
    let trimmed = for child in x.body.children {
      if repr(child.func()) == "attach" {
        child.base.text
      }
      else {
        child
      }
    }
    $ trimmed $
  }
  else {
    x
  }
}

Does it make sense to calculate the center of the bounding box for trim_attachments(node_text) and use it as the pivot point for alignment? And we could still compute the bounding box for node_text in the usual manner.

Jollywatt commented 3 days ago

Not sure. With block math, it’s not only the attachments, but everything which affects the bounding box - including the $\Sigma$ itself. For instance, look at the output of:

#let eq = $ sum_(i in  U) F(U)-->F(U) $
#let trimmed_eq = trim_attachments(eq)
#set rect(inset: 0pt, stroke: 0.5pt + red)
#rect(eq)
#rect(trimmed_eq)
#rect($ F(U)-->F(U) $)

However, fletcher diagrams in math mode use inline math by default, exactly for this purpose of reusing the same baseline. (Typst gives us two package deals: use inline math and get a baseline-aligned bounding box, or use block math and get an optical bounding box.)

However, it seems that when inline math is wrapped in a box, the baseline is not preserved:

foo [$sum_(n=0) 1/n! x^n$] bar
foo [$limits(sum)_(n=0) 1/n! x^n$] bar

#set box(stroke: 0.5pt + red)
foo [#box[$sum_(n=0) 1/n! x^n$]] bar
foo [#box[$limits(sum)_(n=0) 1/n! x^n$]] bar
Screenshot 2024-07-02 at 18 17 11

So to fix this, I need to figure out how to wrap inline math nodes in a box (which is passed to cetz for drawing) while preserving the baseline. I'll ask in Discord: link to question.