On this page
Diagrams and measured connections¶
Use composition() when positions should depend on measured component sizes.
Use document() or subfigure() for grids. The direct combinators remain
useful for simple rows, stacks and fixed diagrams.
Run the Python blocks in order with core Inklet installed. SVG/PDF exports need
no preview tools; generating PNG previews additionally needs the render extra.
The diagram engine review includes an architecture example, connector-label comparisons and routing measurements.
A measured architecture¶
import inklet as i
art = i.composition(150, 45)
art.add('input', i.module('Observations'), x=5, y=12)
x, _ = art.point('input', 'out')
art.add('model', i.module('Encoder'), x=x + 10, y=12)
x, _ = art.point('model', 'out')
art.add('output', i.module('Prediction'), x=x + 10, y=12)
art.link('input:out', 'model:in')
art.link('model:out', 'output:in')
art.constrain(art.page_width - art.point('output', 'e')[0], minimum=5,
message='Increase the page width for the output module')
doc = i.document(width=160, height=55)
doc.add('architecture', art)
first = doc.compile()
art['model'].configure(label='Shared encoder')
second = doc.compile()
assert first.to_svg() != second.to_svg()
second.save('architecture.svg', 'architecture.pdf')

Rendered from the code above.
Modules grow from shaped text, padding and minimum sizes. Since downstream x positions refer to ports, changing the encoder label moves its output and the following module. Links resolve after placement. Unsatisfied constraints and cyclic measurements raise a named layout error.
Ports and placement¶
module() has default in and out ports. Supply a mapping such as
ports={'input': (0, .5), 'upper': (1, .25), 'lower': (1, .75)} for custom
fractional box positions. Compass anchors such as n, e and center are
also available. See diagram components for the ports
provided by databases, sequences and feature matrices.
Composition coordinates default to millimetres. With anchor=None, a child's
local coordinate frame is retained. For explicit alignment, use
anchor='nw', 'center', a registered port, or 'area-nw' for a plot area.
Use point(name, anchor), measure(name, 'width'), page_width and
page_height in arithmetic expressions. Forward references are allowed if
they do not form a cycle.
route='orthogonal' creates right-angle routes. Explicit waypoints can use
measured expressions too. The composition frame is not a clipping mask;
outlying artwork remains visible to diagnostics.
A direct flow diagram¶
acquire = i.box('Acquire', width=25)
analyse = i.box('Analyse', width=25)
fig = i.figure(width=100)
fig.add(i.hstack([acquire, analyse], gap=12))
fig.link(acquire, analyse, label='observations')
fig.save('flow.svg')
assert not any(d.severity == 'error' for d in fig.lint())
Keep handles to the original children when linking through direct combinators. The combinators wrap them, so anchors resolve after layout. If a direct drawing is placed twice, copy it first. Document cells handle independent copies for you.
Graph layout¶
graph = i.graph(
{name: i.box(name, width=22) for name in ('Input', 'Model', 'Result')},
[('Input', 'Model'), ('Model', 'Result')],
layout='layered', direction='right',
)
graph_figure = i.figure(width=120)
graph.add_to(graph_figure)
graph_figure.save('graph.svg')
assert not any(d.severity == 'error' for d in graph_figure.lint())
graph() returns both a laid-out diagram and links. add_to() adds both to a
Figure; .diagram alone does not include the routed connections. Available
layouts are layered, tree, force and circular. Edge crossings can remain
in dense graphs and are reported for review.
decision = i.graph(
{name: i.box(name) for name in ('weight', 'types', 'discard', 'verdict')},
[('weight', 'types', {'label': 'yes', 'same_rank': True}),
('weight', 'discard', 'no'), ('types', 'verdict')],
)
assert decision.ranks == (0, 0, 1, 1)
A layered graph puts every edge's target a rank below its source. Mark an edge
same_rank=True to keep both ends in one rank instead, side by side with the
source first: a decision flow can then send "yes" across and "no" down, and a
row label can sit level with its row. Each node takes at most one same-rank
edge in and one out; a hidden edge (stroke='none', head='none') aligns
nodes without drawing anything.
For branches, loops, explicit routing and diagram annotations, use the
tested cookbook. For a graph inside a responsive document
component, see network() in the stress example.