How Layout Works

How picjs decides where to put things

Layout rests on two things: a current position and a current direction. Everything else follows from how shapes and commands move them.

Shapes follow one another

A shape with no position of its own is placed against the previous one, edge to edge, in the current direction. The direction starts out east.

Box "one"
Box "two"
Box "three"
onetwothree

Face changes the direction

Face s
Box "one"
Box "two"
onetwo

Face takes a cardinal — n, se, east — or an angle: Face 45.

Gap leaves space

Without a gap, shapes touch. Gap opens up one unit; Gap 0.3 opens up that much. Gap same repeats whatever the last gap was.

Box "one"
Gap
Box "two"
Gap 0.2
Box "three"
onetwothree

Goto and Skip move without drawing

Goto takes a position, or a direction and a distance. Skip takes a position. Neither draws anything.

Box "here"
Goto (2.5, 1)
Box "there"
herethere

Placing a shape yourself

A shape given at is centred on that point, and the ones after it carry on from there.

Box "A" at (0, 0)
Box "B" at (2, 0.8)
Box "C"
ABC

Positions can be written relative to other shapes. Every shape has nine cardinal points — .n, .ne, .e, .se, .s, .sw, .w, .nw and .c — and you can do arithmetic on them.

a = Box "A"
Box "B" at a.e + (1, 0.5)
AB

Constrain relative positions with constraints

at places a shape once. with pins a point on it to a place and keeps it there: if the target moves, the shape moves too.

Box "B" with .w at a.e + (1, 0)

That matters when things move. Here b is placed with at and c with with. Watch a move: c keeps its distance, b stays where it was put.

abc

Lines between shapes

A line with no endpoints joins the shape before it to the shape after it, and takes up a unit of space as it goes.

Box "A"
-> "sends"
Box "B"
AsendsB

Give a line endpoints and it uses them instead, leaving the current position alone.

Either way the line stays attached. Move the shapes and it follows, shortening and lengthening as the gap between them changes.

AB

Groups

A group is anchored at the centre of its contents and behaves as one shape: it is placed by the same rules, and has its own cardinal points.

Inside a group the direction starts afresh, and whatever the group does to it is forgotten on the way out.

Box "before"
Group {
  Face s
  Box "in"
  Box "group"
}
Box "after"
beforeingroupafter

Aside draws without moving

Shapes inside an Aside are drawn where you ask, but the current position is left untouched, so what follows carries on as though the aside were not there.

Box "one"
Aside {
  Box "note" at (0.5, -1)
}
Box "two"
onenotetwo