tikzcd syntax
jaroeichler opened this issue · comments
Instead of writing
let (src, img, quo) = ((0, 1), (1, 1), (0, 0))
node(src, $G$)
node(img, $im f$)
node(quo, $G slash ker(f)$)
edge(src, img, $f$, "->")
edge(quo, img, $tilde(f)$, "hook-->", label-side: right)
edge(src, quo, $pi$, "->>")
I'd like
G arrow(r, ->, f) arrow(d, ->>, pi) & im(f) \
G slash ker(f) arrow(ur, ->, tilde(f))
In tikzcd the nodes are put into a matrix environment. The arrows are paths from the current node with a given direction to the target node. Is it possible to do something similar here?
It could very well be possible. So far, I haven't quite figured out a syntax that I think is expressive enough. But here are my current thoughts about it:
-
It would be nice to use a
&
-aligned math environment for convenient syntax. There is one hacky way I think this could be done:#let edge(..options) = metadata((kind: "edge", options: options)) #let eq = $ G edge("r", ->, f) edge("d", ->>, pi) & im(f) \ G slash ker(f) edge("ur", ->, tilde(f)) $
If you look at
#eq.fields()
, you’ll see that the “dictionary-structs” that represent edges are accessible through the metadata elements, so it’s conceivable that this could be translated into an array of nodes and elements, like a normal diagram. -
As yet, I’ve not allowed the use of the nodes’ content where a coordinate is expected, but since this is all in math-mode, it might make sense do to that. That would allow things like:
#fletcher.diagram($ A edge(A, C, bend: #50deg) & B & C $) // would be equivalent to: #fletcher.diagram( node((0,0), $A$), node((0,1), $B$), node((0,2), $B$), edge((0,0), (0,2), bend: 50deg), )
At least then you could form edges between non-adjacent nodes.
Overall, I’m a bit hesitant to make the notation too magical, but I think there’s still something there…
Maybe it's better to use matrix syntax then. At least, it's intended to be used for 2d arrays and should be easier to work with. Also, you should still be able to form edges between non-adjacent nodes
#fletcher.diagram(
A edge(rr, bend: #50deg), B, C;
)
Another option would be to at least write out rr
to (2, 0)
. This would be closer to the CeTZ sytax of line((), rel: (2, 0))
.
This matrix/align notation gets confusing for bigger diagrams though. So maybe it's better to stick to coordinates and shorten the notation there. But I don't think calling nodes by their content is a good idea. You often have maps
These are good comments. I’ve had a go at making something like this on the tikz-style
branch I just added.
On that branch, you can do this:
#fletcher.diagram(axes: (ltr, ttb), $
G edge(f, ->) edge(#(0,1), pi, ->>) & im(f) \
G slash ker(f) edge(#(1,0), tilde(f), "hook'-->")
$)
instead of this:
#fletcher.diagram(axes: (ltr, ttb),
node((0,0), $G$),
edge((0,0), (1,0), $f$, "->"),
edge((0,0), (0,1), $pi$, "->>"),
node((1,0), $im(f)$),
node((0,1), $G slash ker(f)$),
edge((0,1), (1,0), $tilde(f)$, "hook'-->")
)
It’s still rough around the edges. (I might have to make axes: (ltr, ttb)
the default, instead of (ltr, btt)
.)
The changes I made were:
- Interpreting
edge((x2, y2), ..opts)
asedge(from: auto, to: (x2, y2), ..opts)
, that is, if you specify one coordinate for an edge, that is interpreted as its tip, and its base is whatevernode
coordinate was last specified. - Even more magically, interpreting
edge(..opts)
asedge(from: auto, to: auto, ..opts)
, whereto
is the coordinate of the nextnode
specified. I’m not sure if this is best, because you could also say thatto
is the coordinate of the previousnode
, andfrom
the second-previous. - Interpreting
sym.arrow
and friends as mark shorthands, to allow them to be written directly in math-mode likeedge(->)
instead of as stringsedge("->")
. For more complex arrows, you can always use a string. - And of course, allowing
&
-separated equations to specify nodes on a grid.
Things that are tricky:
- Both of you suggested
r
as the most natural notation for(Δx, Δy) = (+1, 0)
. I don’t know exactly how that would work, since in math-modeedge(r, ..)
means#edge($r$, ..)
. It seems like a hack to whitelist some single-letter math variables and interpret them as coordinates. Especially if coordinates are optional, that could lead to ambiguity. - I could export a whole bunch of coordinates, e.g.,
r = (rel: (1, 0)), ru = (rel: (1, 1)), u = (rel: (0, 1)), ………
but that's a lot of namespace pollution. Might be better to just haveedge("r", ..)
. But thenedge("r", "o->")
would be hard to parse, with both arguments as strings. Etcetera. In the meantime, you have to use#(x, y)
.
Features I’m aiming for:
- All the notational “shortcuts” like omitting coordinates should make sense and work in code-mode as well as math-mode.
- You should be able to mix and match code-mode and math-mode in the same diagram, since both have advantages. For example, you can declare variables, have loops
diagram(for x in range(4) { edge((0,0), (x,1), "->") })
etc in code-mode, whereas math-mode just gives you a grid and less$
s.
I welcome any ideas!
This is done in v0.4.0 😃
You can now do:
G edge("r", ->, f) edge("d", ->>, pi) & im(f) \
G slash ker(f) edge("ur", ->, tilde(f))