How to add overlay nodes and edges to Fletcher diagram without affecting layout of lower content

Hello!

I’m writing some slides for a presentation, using touying, and want to present a diagram. To aid the cadence of my explanation, I want to reveal it piece by piece, adding some captions as I explain each component.

I read fletcher’s manual and found out about the layer parameters for both node and edge which, to my understanding, allow you to draw content “above” or “below” (or rather “in front” / “behind”). Content on the same layer will be drawn in the order it is passed to diagram.
Trying this out in a simple case, it seems that is not the real behaviour; the “overlaid” content shifts the layout on the layers below. Here’s a minimal example:

#import "@preview/fletcher:0.5.8" as fletcher: diagram, node, edge

#diagram(
  node(name: <foo>)[Foo],
  node((rel: (0, 1)), name: <bar>)[Bar],
  node((rel: (0, 1)), name: <baz>)[Baz],
  node((rel: (2, 0), to: <bar>), name: <bork>)[Bork],
  node((rel: (2, 0)), name: <yay>)[Yay],
  edge(<foo>, <bork>, "->"),
  edge(<bar>, <bork>, "->"),
  edge(<baz>, <bork>, "->"),
  edge(<bork>, <yay>, "->"),
)

This produces the following document:

However, if we add this node and edge on layer 2 (the default is 0, except for encloses nodes, where it is -1):

node((rel: (.5, .7), to: <bork>), name: <over>, layer: 2, rect[Some overlay]),
edge(<bork>, <over>, layer: 2, "..")

The nodes get shifted like this:

Have I misunderstood this feature or is there a bug? In any case, how could I achieve my goal?

Thank you for your time in advance!

Just in case you didn’t already see it, touying has a Fletcher integration that allows to add “animations”.
I don’t think you can use complex animations with fletcher, but you could maybe use place to display your captions elements at the right position and reveal/hide them individually during the animation.

I am aware of the integration, however I believe the question isn’t really about Touying, but rather on the behaviour of overlays and how to add nodes that do not affect the layout, if it is at all possible. Touying animations are the underlying motivation, though.

As far as I understand, you can use complex animations by using the callback-style approach:

#slide(repeat: ..., self => {
  let (uncover, only, alternatives) = utils.methods(self)
  // ...
})

But again, the question is more about layering and drawing nodes/edges without affecting other layout.
I suppose I could use place and manually do it; would be nice if there was a better way to do it.

1 Like

In one of my slidesets, I have solved this using fletcher’s render parameter. In relevant parts, the code looks like this:

render: (grid, nodes, edges, options) => fletcher.cetz.canvas({
  fletcher.draw-diagram(
    grid,
    nodes.filter(n => n.name in included-nodes),
    edges.filter(e => e.vertices.all(pos => pos in included-nodes)),
    debug: options.debug,
  )
})

So basically I don’t modify the fletcher code at all—that is always the same diagram—and instead filter the nodes and edges while rendering. That ensures that Fletcher’s layout is always identical.

The shifting of the “Yay” node is because the overlay node’s width contributes to the total width in the underlying “flexible grid”. (You can see this more clearly with diagram(debug: true, ..).)

In this case, this is not what you want. Unfortunately, there still isn’t a proper option to make nodes not affect the grid (yet), but you can achieve this in a hacky way my placing the overlay node with physical coordinates, i.e.,

node((rel: (5mm, 7mm), to: <bork>), name: <over>, layer: 2, rect[Some overlay]),

or similar instead of (.5, .7).

The layer parameters should not affect positioning.

2 Likes

I see! I read the manual again and indeed it talks about that difference between “elastic” and absolute coordinates. Right at the beginning, no less! :upside_down_face:
This approach seems to work quite well for my use-case, thank you.

1 Like

Interesting. I think I have settled for the absolute coordinates for now, but I will look into this for more complex scenarios. Thanks!