On this page

Build and draw with Linnest

Linnest is the Typst and WebAssembly interface to Linnet’s graph model, DOT parser, layout algorithms, graph queries, and physics-aware drawing styles. This guide follows one graph from construction through layout and drawing; the separate Typst API reference carries the supported symbol inventory.

Choose the interface deliberately
Use Linnet’s Rust or Python interfaces for graph construction and algorithms in an application. Use Linnest when the graph, layout, or final drawing is owned by a Typst document.

Linnet

The linnet crate wrapped by this package is built around a half-edge graph data structure. This means that instead of a graph being represented as a set of nodes and edges, it is represented as a set of half-edges 𝐻, and a set of vertices 𝑉. The graph structure is then encoded through two maps. The first map, 𝜕:𝐻𝑉 maps each half-edge to its corresponding vertex. The preimage of any vertex 𝑣 is the set of half-edges that map to it, called the crown of 𝑣. The second map, 𝜄:𝐻𝐻, is an involution that glues half-edges together to form edges. If a half-edge is glued to itself, we call that an external half-edge. This means that linnet graphs are strictly more capable than normal edge and vertex graphs.

Native half-edges also make subgraphs more granular because they can be encoded as sets of half-edges. This directly supports vertex-induced subgraphs: the union of the crowns of a set of vertices.

Linnest

Linnest is the Typst and WebAssembly interface to Linnet. It provides layout algorithms, DOT parsing, and selected graph algorithms without a separate runtime process.

Graphs can be constructed in two ways: parse a DOT string with parse:

#let g = parse("digraph { a -> b }")

or build from edges and nodes with build, using a Fletcher-inspired syntax:

#let g = build({
  node(<a>, label: [$v$])
  node(<b>)
  edge(source(<a>), <a-b>, sink(<b>), label: [e])
  edge(source(<a>), <in-a1>, label: [e])
  edge(source(<a>), <in-a2>, label: [e])
})

In either case, type(g) is dictionary: graph values wrap an archived Linnet graph together with native Typst data. Rust owns topology, layout state, statement metadata, and internal opaque payload bytes. User data captured from Typst stays in Typst and is merged back into query records.

The main use case is to place nodes and edges on a canvas with the layout function and render the result with draw.

  • graph for construction, parsing, inspection, joins, and graph algorithms.
  • subgraph for subgraph object construction and inspection.
  • layout for the separate layout pass.
  • draw for rendering a laid-out graph object with CeTZ.
  • physics for reusable particle-line edge styles.

Choose an import path

Linnest and Kurvst are currently bundled source packages, not Typst Universe packages. A Clinnet run writes both package trees below build/templates/. From a custom template in that directory, import Linnest with:

#import "crates/linnest/typst/src/lib.typ": draw, graph, layout, subgraph

From this repository’s crates/linnest/typst/examples/ directory, the equivalent checkout-relative import is ../src/lib.typ. Keep the package directory and its linnest.wasm file together when copying it elsewhere. The examples below use the checkout-relative form because they are also compiled as repository tests.

Minimal Build Example

#import "../src/lib.typ": draw, graph, layout, subgraph
#import graph: build, dot, edge, edges, node, nodes, parse, sink, source

#let g = build({
  node(<a>)
  node(<c>)
  edge(
    source(<a>, compass: "e"),
    <a-c>,
    sink(<c>, compass: "w"),
    label: [a-c],
    statements: (
      color: "0055ff",
      source-color: "d72638",
      sink-color: "1b7f4c",
    ),
  )
},
  name: "demo",
)
#let g = layout(g)
#let east = subgraph.compass(g, "e")
#let edge-records = edges(g, subgraph: east)
#let dot-text = dot(g)
#let edge-label(edge) = text(fill: rgb("#" + edge.color))[#edge.label]
#let source-style(edge) = (stroke: rgb("#" + edge.source-color) + 0.5pt)
#let sink-style(edge) = (stroke: rgb("#" + edge.sink-color) + 0.5pt)
#context if target() == "paged" {
  draw(
    g,
    subgraph: east,
    edge-label: edge-label,
    edge-label-style: (anchor: "south"),
    source-style: source-style,
    sink-style: sink-style,
  )
} else {
  [The downloadable PDF renders this CeTZ result. The HTML manual keeps the
  copyable source because Typst's experimental HTML target does not yet emit
  the drawing content.]
}

Graph Objects

Graph values combine archived Linnet topology with native Typst data. The focused graph reference documents their constructors, transforms, queries, and update operations.

Continue into the API

Use the graph module for construction and queries, the layout functions for placement, and the drawing functions for CeTZ output. Physics styles and subgraph objects have their own focused reference pages.