Closed waterlens closed 4 months ago
You could try making the composite node one node with a table as content. This solution still requires you to manually position edges, but that's easier than manually joining nodes.
#import "@preview/fletcher:0.4.4" as fletcher: diagram, node, edge, draw
#let label = table(
columns: (12mm, 4mm, 4mm, 4mm),
`Node`, none, `4`, none,
stroke: (x, y) => if 0 < x and x < 3 { (x: 1pt) },
)
#diagram(
node-stroke: 1pt,
edge-stroke: 1pt,
mark-scale: 50%,
node((0,0), label, inset: 0pt, corner-radius: 3pt),
edge((0.09,0), (0,1), "*-straight", snap-to: ((99,99), auto)),
edge((0.41,0), (1,1), "*-straight", snap-to: ((99,99), auto)),
node((0,1), `Subnode`),
)
I've used snap-to
to prevent the edges from snapping to the composite node. (Surprisingly, passing none
to snap-to
doesn'y work — I'll fix that — so I gave it coordinates of a nonexistent node instead.)
It would be really cool to support something like pinit for doing something like this more automatically, though.
@waterlens I'm going to assume that the table-in-a-node trick worked fine, so that we don't need to add a new kind of 'fat' node or 'subnode' :)
Sorry, last month I'm busy with other things and didn't reply to this. Yeah it should be a better solution than mine. I think it could be more elegant to use pinit-like packages, but currently I'm not sure how to do it.
I also try to position the edge automatically. I think relative coordinates like (rel: (x, y), to: (u, v))
are helpful here. We can pre-calculate the position of start point inside a table, and then tell fletcher to start from that. @Jollywatt How do you think about this? I guess this method could be extended to support richer features for this awesome package.
#import "@preview/fletcher:0.5.0" as fletcher: diagram, node, edge, draw
#let cell(..texts) = {
let texts = texts.pos()
let columns = texts.map(((_, len)) => len)
let texts = texts.map(((text, _)) => text)
let anchor_pos = {
let anchor_pos = ()
let width = 0pt
for i in range(0, texts.len()) {
let text = texts.at(i)
if text == none {
anchor_pos.push(width + columns.at(i) / 2)
}
width += columns.at(i)
}
anchor_pos = anchor_pos.map((x) => x - width / 2)
anchor_pos
}
let n = columns.len()
let tbl = table(
columns: columns,
stroke: (x, y) => if x != 0 and x != n - 1 { (x: 1pt) },
..texts,
)
(node: tbl, anchors: anchor_pos)
}
#let mynode(n) = cell(
(`Node`, 12mm),
(none, 4mm),
(raw(str(n)), 4mm),
(none, 4mm)
)
#diagram(
node-stroke: 1pt,
edge-stroke: 1pt,
mark-scale: 50%,
node((0,0), mynode(4).node, inset: 0pt, corner-radius: 3pt),
edge((rel: (mynode(4).anchors.at(0), 0pt), to: (0, 0)), (0, 1), "*-straight", snap-to: (none, auto)),
node((0,1), `Subnode`),
)
That works really well:)
I'm not sure there's much more you can do with fletcher to automate this, but I did notice you could do this for the arrows:
#fletcher.MARKS.update(m => m + (
"*": (inherit: "circle", fill: auto, tip-origin: 0),
">": (inherit: "straight", rev: false),
"<": (inherit: "straight", rev: true),
))
Changing the tip-origin
of the dot makes it centred, which looks slightly nicer for your use case. And if you want the stright arrows to be the default, you can do that too.
Thanks!
I'm trying to draw a diagram similar to this one:
My plan is to regard it as separate 4 nodes which are close enough together. However, I found it hard to specify the distance between nodes in fletcher because I cannot directly get the size of a node. And I don't know if I can let all nodes share the same height automatically. Is it possible to add support to such a diagram, through a new concept of fat nodes and subnodes in fletcher?
Currently, I can get a scribbled diagram using fletcher,
with this code: