Skip to content

Layout

Vex came from a layout engine. A layout has boxes, and each rule of a layout relates a box to other boxes: “this box starts where that box ends”, “these boxes do not overlap”.

The program below gives each box a push. It reads the centre of the box at the origin. For each other box that it overlaps, it reads the direction from that centre to its centre. The reduction adds the directions.

Drag a box onto another box: red arrows show the pushes. Select “Relax” to move each box along its push until no boxes overlap.

Push the boxes apart
Apush (-0.7592566023652966, -0.6507913734559685)
Bpush (0.7592566023652966, 0.6507913734559685)
Cno overlap, no push
Dno overlap, no push
const centre = root.from("size")._.halve()._.add("position");
const push = centre
  .others(
    (e) => e._.subtract(e.from("size")._.halve()._.add("position"))._.normalize(),
    { where: overlaps },
  )
  .reduce("add");

The test of the where option reads both boxes: origin() reads at the origin, and a bare field name reads at the target. The old Vex could not express this, because its focus was one mutable value.

Does the box overlap another box?origin
root.from("position")._.add("size")
  .others((e) =>
    e._.subtract("position")._.allPositive()._.and(
      e.from("position")._.add("size")._.subtract(e.origin().from("position"))._.allPositive()))
  .any()
step 27 of 27

Result at A: true

originABCD
resulttruetruefalsefalse