Nodes

node(position, body, ..)

Nodes are content centered at a coordinate. By default, nodes fit to their content (with an inset), but can also be given a specific size and shape. Nodes can be given various styles including stroke and fill.

Edges automatically snap to nodes (with an outset) and the positions and sizes of nodes affects diagram layout (unlike edges or plain CeTZ objects).

#diagram(
  spacing: (5pt, 2em), // small columns, large rows
  node((0,0), $A$),
  node((1,0), $f$, stroke: 1pt),
  node((2,0), $g$, stroke: 2pt + blue, shape: rect),
  node((3,0), $X$, stroke: blue, extrude: (0, 3)),
  {
    let b = blue.lighten(70%)
    node((0,1), `xyz`, fill: b, )
    let dash = (paint: blue, dash: "dashed")
    node((1,1), `xyz`, stroke: dash, inset: 1em)
    node((2,1), `xyz`, stroke: b, extrude: (0, -2))
    node((3,1), `xyz`, fill: b, height: 5em,
                       corner-radius: 5pt)
  }
)

Node styles

Available node styles:

Node styles can be set with named arguments, like node(stroke: 2pt, ..). Default styles can be set by passing options to the enclosing diagram by adding a prefix, like diagram(node-stroke: 2pt, ..), or by inserting

cetz.draw.set-style(node: (stroke: 2pt))

inside a diagram() and a CeTZ canvas. Like CeTZ styles, cetz.draw.set-style() is scoped to the current cetz.draw.group().

#diagram(
  node-stroke: 2pt, // default stroke style for nodes
  node((0,0), fill: yellow, [A]),
  edge("<->"),
  {
    import cetz.draw: *
    set-style(node: (extrude: (2,0)))
    // stroke becomes 2pt + blue
    node((0,1), stroke: blue, [B], outset: 4pt)
  }
)

Node shapes

By default, nodes are circular if the body is small and roughly as wide as it is tall, and rectangular otherwise. The shape option can be set to any of the following built-in shapes.

Most shapes have additional styles specific to the shape, such as:

Additional styles are described in each shape’s documentation.

A node’s shape can often be automatically inferred from the other styles given. For example, you can write node(.., radius: 3cm) instead of node(.., shape: "circle", radius: 3cm).

#diagram(
  node-fill: yellow,
  node-stroke: 0.7pt,
  node((0,0), corner-radius: 5pt)[Rounded],
  edge("->-"),
  node((0,1), radius: 2mm)
)

Making shapes fit better

All shapes have a fit parameter, which adjusts how tightly the shape fits in the body.

The default is usually in between, striking a balance. If a node looks too cramped inside a shape, you can usually adjust the fit instead of tweaking the node.inset.

You can see the bounding box of the node body with the "node.body" debug option.

#diagram(
  debug: "node.body",
  spacing: 5pt,
  node-stroke: blue,
  node-fill: blue.lighten(80%),
  node-shape: "diamond",
  node((0,0), fit: 0)[Zero fit],
  node((0,1), fit: 0.5)[Partial],
  node((0,2), fit: 1)[Whole fit],
)

In addition to controlling how the node’s body fits in the shape, the fit-cell parameter controls how the shape fits in the surrounding flexigrid cell.

This only matters for the layout of a surrounding flexigrid() or diagram(); the fit-cell style doesn’t affect node itself.

#diagram(
  debug: "grid.cells node.body",
  spacing: 5pt,
  node-stroke: blue,
  node-fill: blue.lighten(80%),
  node-shape: "triangle",
  node((0,0), fit-cell: 0)[Zero cell fit],
  node((1,1), fit-cell: 1)[Total cell fit],
)

Enclose nodes

Enclose nodes are a special type of node that are positioned around other nodes, which is useful for diagrams with nested layouts. Nodes with the node.enclose option automatically wrap around the specified nodes.

#diagram(
  spacing: (10mm, 5mm),
  node-stroke: 0.5pt,
  node-fill: white,
  node-corner-radius: 2pt,

  node((-2,0), radius: 2pt, fill: black),
  edge("r,u,r", "->", $f$),
  edge("r,d,r", "..>", $g$),

  node((0,-1), $F(s)$, <f>),
  edge("->", (1,0), corner: "-|"),

  node((0,+1), $G(s)$, <g>),
  edge("..>", (1,0), corner: "-|"),

  node((1,0), $ plus.o $, stroke: none, inset: 2pt),
  edge("->", "r"),

  // enclose nodes by name
  node(enclose: (<f>, <g>), text(teal)[Group],
    inset: 10pt,
    stroke: teal,
    fill: teal.transparentize(90%),
  ),
)

Nodes can also have row and column spans, which is a similar but distinct concept. Unlike nodes which span rows or columns, enclose nodes do not exist within a flexigrid layout, and do not affect the position of other nodes.

Column span and row span

Nodes also support spanning multiple columns or rows, but only when used inside a flexigrid layout. See the section on diagrams and layout for details.

Anatomy of a node

A summary of the node box model is shown below. The different components of a node can be displayed using node debug options.