Neural Sketch

Positioning Mechanisms

An in-depth guide to absolute and relative positioning in NeuralSketch, essential for creating precise and visually coherent diagrams.

Positioning is not a detail—it is the diagram. It governs rhythm, structure, and spatial logic, shaping not only what your readers see but how they read.

Positioning is fundamental to crafting clear, accurate, and visually appealing diagrams. Neural Sketch provides robust, flexible, and intuitive positioning mechanisms designed for both straightforward and complex diagramming tasks.

Overview

Neural Sketch supports two complementary models of placement:

  • Absolute positioning: Explicitly define exact coordinates.
  • Relative positioning: Place elements in relation to other diagram primitives, containers, groups, or blocks.

Each method supports nuanced control through additional positioning keys, allowing you to precisely adjust or align elements with minimal boilerplate.

Positioning mechanisms are supported across all major elements — including \nskBlock, \nskContainer, \nskGroup, \nskMark, and more. If it goes on the canvas, it can be placed.

Absolute Positioning: Declare, Then Adjust

Absolute positioning allows you to define the exact coordinates for placing a primitive on the canvas. This method is ideal for scenarios requiring explicit spatial precision.

Basic Usage

Define absolute positions using the x and y keys, representing horizontal and vertical coordinates respectively:

\nskBlock[
  x=3, y=1.5, 
  text-center=Absolute Block,
]

Precision Offsets: The Role of shift-*

After establishing an absolute position, you can apply additional fine-grained adjustments using shift-x and shift-y keys:

\nskBlock[
  x=3, y=1.5,
  shift-x=0.5, 
  shift-y=-0.25, 
  text-center=Shifted Block,
]

This moves the block horizontally to the right by 0.5 units and vertically downward by 0.25 units, relative to the specified coordinates.

You can set absolute positioning using unit-aware values — either relative units like 3 (default units) or explicit units like 3cm, 10pt, etc.

Mark shifting

shift-x and shift-y are particularly useful for micro-adjustments.

First, you can rougly position an element to a reference location, then apply a precise per-axis offset.

This pairs perfectly with \nskMark:

examples-doc/positioning-ms.tex
\nskBlock[id=x, fill=nskRed, pattern=hatch]
\nskBlock[id=A, type=circle, text-center=A, last-pos={right=3cm}, shift-y=10mm]
\nskBlock[id=B, text-center=B, last-pos={below}, fill=nskBlue]
 
\nskMark[id=xtop, at=x.east, shift-y=2mm] 
\nskMark[id=xbot, at=x.east, shift-y=-2mm] 
 
\nskConnect[from=xtop, to=A, bend-type=single, bend-direction=right, arrow-style={very thick, dashed}]
\nskConnect[from=xbot, to=B, bend-type=single, bend-direction=right]

Relative Positioning: Describe Relationships

Let layout emerge from intent.Think in terms of spatial relationships, not coordinates. Relative positioning lets you describe how one element relates to another—above, beside, offset by a margin.

It aligns primitives with reference to other primitives by ID, significantly simplifying complex diagram layouts.

Syntax

<key>={<direction>=<distance> [of <target>]}

where:

  • <key> can be pos, last-pos, or last-pos-s
  • <direction> is a TikZ-style compass direction such as above, below, left, right, or combined forms like above right
  • <distance> specifies the offset in any valid LaTeX\LaTeX dimension (1cm, 4pt, etc.)
  • <target> is the ID of a previously defined primitive (e.g., a block or container)

You can combine two directions with independent distances:

pos={above right=1cm and 0.5cm of baseNode}

This places the current element 1cm above and 0.5cm to the right of baseNode.

When combining directions, always list the vertical direction first (e.g., above right, not right above). This matches TikZ syntax and avoids parsing errors.

Using the pos Keyword

The pos key uses natural-language-inspired syntax, based on TikZ’s positioning library. This allows expressive, readable placements without numerical guesswork:

\nskBlock[id=ablock, text-center=Base Block]
\nskBlock[
  id=bblock,
  pos={above right=1cm and 0.5cm of ablock},
  text-center={Relative Block},
]

Here, the second block (bblock) is positioned exactly 1 cm above and 0.5 cm to the right of the first block (ablock).

Example

examples-doc/positioning-r.tex
\nskBlock[id=x, fill=nskRed, pattern=hatch, width=2cm, height=2cm, type=diamond]
\nskBlock[pos={right=of x}, text-center=$\bullet$, text-east=right]
\nskBlock[pos={left=of x}, text-center=$\bullet$, text-west=left]
\nskBlock[pos={above=of x}, text-center=$\bullet$, text-north=above]
\nskBlock[pos={below=of x}, text-center=$\bullet$, text-south=below]

Tip

When no <distance> is provided in pos, last-pos, or last-pos-s, Neural Sketch uses a default value defined by the internal variable g__nsk_style_figure_block_distance_tl. This value is globally configurable via the block-distance key in the package options. This allows you to standardize spacing across your diagrams and reduce boilerplate—so even pos={right=of blockA} or just last-pos={right=} just works

Retrieving Diagram History

Sometimes you don’t want to assign symbolic handles (id) to every element—or more intuitively, you want to place something relative to what you just drew. In these cases, NeuralSketch exposes a powerful diagram history system via \nskID{n} and \nskID!{n}.

  • \nskID{1} returns the most recently drawn element.
  • \nskID{2} is the one before that, and so on.
  • \nskID!{1} accesses the first element drawn in the current figure.

This enables dynamic, ID-free layout composition:

\nskBlock[...] % first block
 
\nskBlock[pos={right=of \nskID{1}}]
\nskBlock[pos={above=of \nskID{1}}]
\nskBlock[pos={below=of \nskID{2}}]

Leveraging the diagram history through the \nskID and \nskID! we can easily position and connect elements and you can even chain connections dynamically:

examples-doc/positioning-r2.tex
\nskBlock[type=diamond, fill=nskRed, pattern=hatch, width=2cm, height=2cm]
 
\nskBlock[pos={right=of \nskID{1}}, text-center=$\bullet$, fill=nskSecondaryAccent]
\nskBlock[pos={above right=2cm of \nskID{1}}, text-center=$\bullet$, type=circle]
\nskBlock[pos={below right=2cm of \nskID{2}}, text-center=$\bullet$, type=circle]
 
 
\foreach \i in {1,...,3} {
  \nskConnect[from=\nskID!{1}.east, to=\nskID{\i}, bend-type=single, bend-direction=right]
}

Want to understand how IDs are generated and how far back you can go? Check out the Auto-Incremental ID page for a full breakdown of how Neural Sketch manages and exposes the internal history.

last-pos: Chain without IDs

When building a diagram step by step, it’s often more natural to describe the layout incrementallywhat comes next, not just where it goes. The last-pos key enables this flow-driven composition by anchoring new elements to the most recently drawn primitive:

\nskBlock[text-center=Start]
\nskBlock[last-pos={right=1cm}, text-center=Next]
\nskBlock[last-pos={below=0.8cm}, text-center=Then]

This approach keeps your code linear, expressive, and free from the overhead of assigning and referencing explicit ids.

last-pos-s: Safe Chaining

In dynamic scenarios—loops, branches, or conditional macros—there may be no previously drawn element. That’s where last-pos-s (the safe variant) comes in.

It applies the offset only if a previous primitive exists, and silently skips otherwise:

\foreach \i in {1,...,5} {
  \nskBlock[
    last-pos-s={right=1cm},
    text-center={Block \i},
  ]
}

No errors. No undefined references. Just graceful fallback.

Use this when the presence of a preceding element can’t be guaranteed.

Custom Errors with last-pos

Unlike last-pos-s, the standard last-pos assumes a previously drawn primitive must exist. If not, Neural Sketch issues a targeted error to help you diagnose and correct the issue quickly.

ERROR-BadIndex

If the reference to the previous element is invalid—such as using last-pos with no prior primitive—Neural Sketch raises a custom ERROR-BadIndex. This message explicitly points out the missing or out-of-bounds index, making debugging fast and transparent.

This is especially important when converting code from one-off diagrams to templated or loop-driven ones. Switching to last-pos-s ensures your layout logic stays resilient.

Skip-first Positioning

Sometimes you find yourself needing to align two set of elements across two dimensions. For examples you have two containers, each with each containing block relatively placed next to each other. Then you would like to align each container below each other.

\nskContainer[...]{
  \foreach \i in {1,...,3} {
    \nskBlock[last-pos=right]
  }
}
 
\nskContainer[last-pos=below]{ % <-- this won't apply
  \foreach \i in {1,...,3} {
    \nskBlock[last-pos=right] % <-- stricter rules applies
  }
}

Because last-pos=right applies to the very first block in the second container, the second container ends up side by side with the first (stricter rule applied).

You have two clear options to fix this:

  1. Insert a dummy marker: Add an invisible \nskMark to reset the positioning context:
\nskContainer[...]{
  \foreach \i in {1,...,3} {
    \nskBlock[last-pos=right]
  }
}
 
\nskContainer[last-pos=below]{
  \nskMark[] % % <-- this is tracked
  \foreach \i in {1,...,3} {
    \nskBlock[last-pos=right] % <-- aligned next to dummy node
  }
}

\nskMark like every element, is tracked--so keep that in mind when Navigating the Diagram History

  1. Use the “skip-first” operator (^=): Apply positioning only from the second element onward:
\nskContainer[...]{
  \foreach \i in {1,...,3} {
    \nskBlock[last-pos=right]
  }
}
\nskContainer[...]{
  \foreach \i in {1,...,3} {
    \nskBlock[last-pos^=right]
  }
}
\nskReset[^] % <-- resets

This way, the first block inside the second container uses the container’s positioning, and subsequent blocks apply last-pos=right.

After your done, you can reset all ^ commands counters using

\nskReset[^]

Available: pos^=, last-pos^=, last-pos-s^=.

Currently only supported by nskBlock primitives.

Intelligent Alignment

Neural Sketch intelligently infers alignment intentions, simplifying diagrams that rely heavily on aligned elements.

For example, horizontally aligning adjacent blocks does not require explicit anchors—Neural Sketch automatically chooses appropriate edges:

\nskBlock[id=A, text-center={Block A}]
\nskBlock[
  pos = {right=1cm of A},
  text-center = {Block B},
]

In this scenario, NeuralSketch aligns the left edge of Block B with the right edge of Block A, automatically interpreting your intent without needing explicit anchors.

Neural Sketch allows to horizontally and vertically align

examples-doc/positioning-a.tex
\foreach \c/\s in {Yellow/1, Orange/2, Red/4} {
    \nskBlock[
      last-pos-s={below=},
      width=\s cm, fill=nsk\c, text-north=\nskBlockID
    ]
  }
 
\foreach \c/\s in {Yellow/1, Orange/2, Red/4} {
    \nskBlock[
      last-pos={right=},
      height=\s cm, fill=nsk\c, text-north=\nskBlockID
    ]
  }
 
\nskOnbg{
    \nskConnect[
      from=\nskID!{2}.north, to=\nskID!{4}.south,
      shorten-from=-10mm, shorten-to=-10mm, arrow-type=dashed,
      color=nskStrongCyan,
    ]
    \nskConnect[
      from=\nskID{1}.east, to=\nskID{3}.west,
      shorten-from=-10mm, shorten-to=-10mm, arrow-type=dashed,
      color=nskStrongCyan,
    ]
}

Combining Positioning with Containers and Groups

Positioning mechanisms extend naturally to containers and groups, allowing cohesive positioning and transformation of composite diagram components.

Containers

Containers wrap multiple primitives, allowing global transformations alongside internal relative positioning while providing handy stylings:

\nskContainer[last-pos={right=}]{
  \nskBlock[text-center=Inside Container]
  \nskBlock[last-pos=right=1cm, text-center={Relative Inside Container}]
}

This container moves as a unit, while blocks inside maintain local relative positioning.

Groups

Groups logically cluster primitives, making collective transformations (e.g., rotation or scaling) straightforward while maintaining local positional consistency:

\nskGroup[pos={below=of A}]{
  \nskBlock[text-center=A]
  \nskBlock[pos={right=2cm ofA}, text-center=B]
}

Both blocks move cohesively, preserving their relative positions within the transformed group.

\nskGroup is usually used internally, whenever possible you may want to use \nskContainer.

Aligning Multiple Elements together

Sometimes, you want to align one or more elements not relative to a single item, but to a group of elements treated as a whole. Instead of aligning to an individual block, you align to the combined bounding box of several blocks.

To do this, you can wrap the elements inside an \nskContainer. By default, \nskContainer adds padding and styling. If you simply want pure logical grouping—without visual decoration—you can use \nskContainer![...] (the exclamation mark disables styles).

Suppose you have a few blocks:

% ... some blocks ...
\nskBlock*[id=a, \p, \x]
\nskBlock*[last-pos={right=7mm}, \r, \x]
\nskBlock*[last-pos={right=7mm}, \c, \x]
\nskBlock*[last-pos={right=7mm}, \t]
% ... some blocks ...

Now, imagine you want to place a new element above all of them, centered on the group.

At first, it might seem natural to align to one specific block:

\nskBlock*[type=circle, pos={above=of <...>}]

However, none of the individual elements exactly aligns:

\nskBlock*[pos={above=of \nskID!{1}}]
\nskBlock*[pos={above=of \nskID!{2}}]
\nskBlock*[pos={above=of \nskID!{3}}]
\nskBlock*[pos={above=of \nskID!{4}}]
 
% ...
 
% we align above the 3rd element
\nskBlock*[type=circle, pos={above=of \nskID!{3}}]

Grouping for Correct Alignment

Instead, wrap the blocks inside a \nskContainer![...].

This groups them into a single logical unit:

% wrap elements --v
\nskContainer![id=group] {
  \nskBlock*[pos={above=of \nskID!{1}}] 
  \nskBlock*[pos={above=of \nskID!{2}}] 
  \nskBlock*[pos={above=of \nskID!{3}}] 
  \nskBlock*[pos={above=of \nskID!{4}}] 
}

Now, you can simply align your new element relative to the group:

\nskBlock[type=circle, pos={above=of group}] % or simply last-pos={above=}

Wrapping Elements Under a Reference

Sometimes, you want to “wrap” a sequence of diagram elements beneath another—shifting the reference anchor midstream. That’s what \nskWrap enables. It repositions the base anchor so that subsequent elements flow from a new starting point.

\nskWrap[under=<ID>]

This is especially useful for grouped content or bifurcated flows that stem from a shared parent.

This mechanism integrates cleanly with \nskContainer, allowing you to wrap and group new content under an existing structure.

examples-doc/positioning-w.tex
\nskBlock*[id=a, \p, \x]
[...]
\nskBlock*[last-pos={right=7mm}, \t]
 
\nskWrap[under=a] 
\nskBlock*[\t,]
[...]

However, sometimes you may want to wrap above an element, you can easily do so by using the over= key instead:

examples-doc/positioning-w.tex
\nskBlock*[id=a, \p, \x]
[...]
\nskBlock*[last-pos={right=7mm}, \t]
 
\nskWrap[over=a] 
\nskBlock*[\s,]
[...]

By default, \nskWrap[...] uses the global block-distance wrap elements; sometimes you may find the need to locally modify the spacing, for this occasions you can use shift-x and shift-y:

examples-doc/positioning-w.tex
\nskBlock*[id=a, \p, \x]
[...]
\nskBlock*[last-pos={right=7mm}, \t]
 
\nskWrap[under=a, shift-y=8mm] 
\nskBlock*[\s,]
[...]

Tip

You can easily chain logic, by using \nskID or \nskID!

Edge Alignment

Sometimes you need blocks to align flush—where their edges line up exactly with a reference point or neighboring element.

In these cases, you can use the align= key to precisely control edge alignment:

\nskBlock[..., align=<top|center|bottom>]

The align= key ensures that your block’s edge (top, center, or bottom) snaps to the correct position, making layouts feel tight and intentional.

You can combine align= with any *-pos-*= key for fine-grained control over placement.

Best Practices

  • Absolute positioning is ideal for single, fixed, or foundational elements.
  • Relative positioning reduces redundancy and complexity in interconnected or iterative diagrams.
  • Use last-pos-s within loops or uncertain contexts to avoid unnecessary errors gracefully.
  • Use Neural Sketch’s intelligent alignment for cleaner, simpler code.

Takeaways

Neural Sketch’s versatile positioning mechanisms—combining absolute coordinates, expressive relative references, intelligent alignment inference, and robust error handling—offer unmatched flexibility and ease-of-use, empowering you to create precise, sophisticated, and visually coherent diagrams with minimal effort.