Jollywatt/typst-fletcher

Overlay or section node

Closed this issue · 3 comments

I tried to create a node which goes around multiple other nodes and edges, to show that those nodes are belonging together.
But it always extended the edges so it didn't reach the other node.

Bildschirmfoto vom 2024-02-01 10-02-06

It would be nice if there was a kind of node or section or overlay or whatever you want to call it to add this feature.
Or maybe i just missing something.

It's true that this feature isn't really supported yet. However, you can still use cetz to draw things with complete control (as very briefly mentioned in the manual under “CeTZ integration”)

But this is fairly advanced usage, so here's a demonstration that you might adapt to your use case. Below, all the nodes of a certain colour are selected and put in a bounding box, drawn directly with cetz. You can move the nodes or change their colours from in-group to out-group and the bounding box updates.

#import "@preview/fletcher:0.4.0" as fletcher: node, edge, vector, cetz

#let get-bounding-box(points) = {
  let (xs, ys) = array.zip(..points)
  let p1 = (calc.min(..xs), calc.min(..ys))
  let p2 = (calc.max(..xs), calc.max(..ys))
  (
    center: vector.scale(vector.add(p1, p2), 0.5),
    size: vector.sub(p2, p1)
  )
}

// draw a bounding rectangle around nodes
#let enclose-nodes(nodes) = {
  let points = nodes.map(node => node.real-pos)
  let (center, size) = get-bounding-box(points)

  let clearance = 8mm
  cetz.draw.content(
    center,
    rect(
      width: size.at(0) + 2*clearance,
      height: size.at(1) + 2*clearance,
      stroke: 0.5pt + red,
      radius: clearance,
    )
  )
}

#let in-group = orange.lighten(60%)
#let out-group = blue.lighten(60%)

#fletcher.diagram(
  node((-1,0), `R0`, fill: out-group, radius: 5mm),
  edge("o-o"),
  node((0, 0), `R1`, fill: in-group, radius: 5mm),
  edge("o-o"),
  node((1,.5), `R2`, fill: in-group, radius: 5mm),
  edge("o-o"),
  node((1,-1), `R3`, fill: in-group, radius: 5mm),

  render: (grid, nodes, edges, options) => {
    // lookup nodes by color
    let group = nodes.filter(node => node.fill == in-group)
    cetz.canvas({
      // draw the diagram as usual
      fletcher.draw-diagram(grid, nodes, edges, options)
      // draw a custom group rectangle
      enclose-nodes(group)
    })
  }
)

I suspect that this might be a common use case, so this could be added to fletcher itself…

Thanks for the quick solution.

  • Two things i miss is that it now depends on the fill color. Maybe a group parameter would be a good option.
  • It's still not possible if i just want part of the Node inside a group. Maybe adding an edge to a group would solve this.

As of 6916777, which added the enclose option to node(), you can now do this:

#diagram({
	let c = rgb(..orange.components().slice(0,3), 50%)
	edge("l", "o-o")
	node((0,0), `R1`, radius: 5mm, fill: c)
	edge("o-o")
	node((1,0), `R2`, radius: 5mm, fill: c)
	edge("u", "o-o")
	edge("o-o")
	node(`L7`, enclose: ((0,0), (1,0)), stroke: red + 0.5pt, extrude: (0,2))
})

…to get this:
Screen Shot 2024-04-08 at 9 25 33 PM