Jollywatt / typst-fletcher

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

Control spacing between individual grid cells #47

Open YaLTeR opened 3 weeks ago

YaLTeR commented 3 weeks ago

Hi, I'm just trying out cetz and fletcher, and it feels really good! But I've hit this difficult case.

I'm making a flowchart where I need to merge two arrows:

test

#context(diagram(
  // debug: true,
  spacing: (6mm, 0mm),
  node-stroke: 1pt,
  {
    let data = parallelogram.with(angle: 30deg)

    let c = [Ground-Truth Image, $I_G$]
    let s = measure(c)
    let w = s.width + 6pt * 2

    node((0, 0), [Input Image, $I$], shape: data, width: w)
    node((0, 2), c, shape: data)

    node((1, 0), [Base network, $F_L$])
    node((1, 2), [Base network, $F_L$])

    node((3, 1), [MSE])

    node((4, 1), [Result, $S$], shape: data)

    edge((0, 0), "r", "-|>")
    edge((0, 2), "r", "-|>")

    edge((1, 0), "r,d,r", "-|>")
    edge((1, 2), "r,u,r", "-|>")

    edge((3, 1), "r", "-|>")
  }
))

The connection where the two arrows merge visually looks much wider than necessary. Since there's no node there, all that width comes from 2 × spacing:

test

I'd like to reduce spacing specifically around that column at x=2.

Jollywatt commented 3 weeks ago

Because spacing is set to 6mm in the horizontal direction, there's 6mm on either side of column 2 even though that column has width zero.

You could remove the empty column and have edges with fractional coordinates, like this:

#context diagram(
  debug: true,
  spacing: (6mm, 0mm),
  node-stroke: 1pt,
  {
    let data = fletcher.shapes.parallelogram.with(angle: 30deg)

    let c = [Ground-Truth Image, $I_G$]
    let s = measure(c)
    let w = s.width + 6pt * 2

    node((0, 0), [Input Image, $I$], shape: data, width: w)
    node((0, 2), c, shape: data)

    node((1, 0), [Base network, $F_L$])
    node((1, 2), [Base network, $F_L$])

    node((2, 1), [MSE])

    node((3, 1), [Result, $S$], shape: data)

    edge((0, 0), "r", "-|>")
    edge((0, 2), "r", "-|>")

    edge((1, 0), (rel: (.66,0)), "d", (2,1), "-|>")
    edge((1, 2), (rel: (.66,0)), "u", (2,1), "-|>")

    edge((2, 1), "r", "-|>")
  }
Screenshot 2024-08-17 at 09 36 18

(or maybe this is better style for this case?)

Screenshot 2024-08-17 at 09 36 57
YaLTeR commented 3 weeks ago

You could remove the empty column and have edges with fractional coordinates, like this:

Right, I guess that works. I wanted to keep the vertical edge exactly in the middle between the nodes, is that still possible here without eyeballing the fraction?

(or maybe this is better style for this case?)

Indeed, maybe this style is better here. However, it has a bit of an inverse problem: I feel like now I want to ever so slightly reduce the spacing between "Base network" and "MSE" on this example, since the top and bottom arrows make it look a bit wider.

Jollywatt commented 3 weeks ago

is that still possible here without eyeballing the fraction?

Hmm. I can't come up with an automatic way with the current features. It would be great if it were possible to use cetz anchors like "east" to refer to the edges of the nodes, because then you could use something like ("node1.east", 50%, "node2.west") to get the exact middle. Unfortunately cetz anchors aren't supported yet in fletcher.

Even without that, though, it might be good to allow grid spacings to be individually controlled. I'll make this a feature request. Any thoughts on how you'd want the syntax to be?

YaLTeR commented 3 weeks ago

Any thoughts on how you'd want the syntax to be?

I was thinking, maybe similar to built-in Typst grid and table row-gutter/column-gutter? Where you can pass one number to set all, or pass an array of numbers to set each one individually. Or even like align where you can also pass a function that takes the X, Y position and returns the value.