Module

DataViz.Layout.Pattern

Package
purescript-hylograph-layout
Repository
afcondon/purescript-hylograph-layout

Geometric Layout Patterns

Simple, deterministic layout functions for positioning N items within a viewport. Each function handles all the tedious calculations (aspect ratio, spacing, centering) and returns an array of points ready to zip with your data.

Usage

import DataViz.Layout.Pattern (grid, phyllotaxis)
import DataViz.Layout.Pattern.Types (viewportWithPadding)

-- Layout 100 items in a grid with 20px padding
let vp = viewportWithPadding 800.0 600.0 20.0
let positions = grid vp 100

-- Zip with your data
let positioned = Array.zipWith (\pos datum -> { x: pos.x, y: pos.y, datum })
                               positions myData

#grid Source

grid :: Viewport -> Int -> Array Point

Lay out N items in a grid, automatically choosing rows/columns to best fit the viewport's aspect ratio.

Items are centered within the viewport and evenly spaced.

#gridWithAspect Source

gridWithAspect :: Number -> Viewport -> Int -> Array Point

Grid with a specific target aspect ratio for cells (width/height)

#gridExact Source

gridExact :: Int -> Viewport -> Int -> Array Point

Grid with exact column count specified

#honeycomb Source

honeycomb :: Viewport -> Int -> Array Point

Honeycomb layout - offset alternating rows for hexagonal packing More compact than a regular grid for circular items.

#brick Source

brick :: Viewport -> Int -> Array Point

Brick layout - like honeycomb but with rectangular cells Good for text labels or rectangular items.

#phyllotaxis Source

phyllotaxis :: Viewport -> Int -> Array Point

Phyllotaxis layout - golden angle spiral like sunflower seeds Produces beautiful, naturally-looking distributions. Items are placed at increasing distances from center using golden angle.

#archimedean Source

archimedean :: Number -> Viewport -> Int -> Array Point

Archimedean spiral - constant spacing between arms

#fermat Source

fermat :: Number -> Viewport -> Int -> Array Point

Fermat spiral - like phyllotaxis but with configurable divergence angle

#radial Source

radial :: Viewport -> Int -> Array Point

Radial layout - items evenly distributed around a circle

#concentricRings Source

concentricRings :: Int -> Viewport -> Int -> Array Point

Concentric rings - distribute items across multiple rings Inner rings have fewer items proportional to their circumference.

#sunburst Source

sunburst :: Int -> Viewport -> Int -> Array Point

Sunburst layout - like concentric rings but with angular sectors

#horizontal Source

horizontal :: Viewport -> Int -> Array Point

Horizontal line across the viewport

#vertical Source

vertical :: Viewport -> Int -> Array Point

Vertical line down the viewport

#diagonal Source

diagonal :: Viewport -> Int -> Array Point

Diagonal from top-left to bottom-right

#along Source

along :: Point -> Point -> Int -> Array Point

Layout along an arbitrary line between two points

#layered Source

layered :: Viewport -> Array Int -> Array Point

Layered layout - multiple horizontal rows with varying item counts. Perfect for neural networks, hierarchies, or any row-based visualization.

Takes an array of item counts per layer. Returns flat array of all positions. Layers are evenly spaced vertically; items within each layer are evenly spaced horizontally across the full width.

-- Neural network: 4 inputs, 8 hidden, 8 hidden, 2 outputs
let positions = layered vp [4, 8, 8, 2]

#layeredCentered Source

layeredCentered :: Viewport -> Array Int -> Array Point

Layered layout with items centered in each row. Unlike layered, this centers items using cell-based positioning (items are centered within evenly-divided cells, not edge-to-edge).

Better for when you want consistent margins at row edges.

#masonry Source

masonry :: Int -> Number -> Viewport -> Array Number -> Array Rect

Masonry (Pinterest-style) layout with fixed column count. Places each item into the shortest column, producing a compact arrangement.

Takes column count, gap between items, viewport, and array of item heights. Returns positioned rectangles in input order.

let rects = masonry 3 10.0 (viewport 800.0 600.0) [100.0, 150.0, 80.0, 200.0, 120.0]

#masonryAuto Source

masonryAuto :: Number -> Number -> Viewport -> Array Number -> Array Rect

Masonry layout with automatic column count from max column width. Computes how many columns fit given the max width, then delegates to masonry.

let rects = masonryAuto 250.0 10.0 (viewport 800.0 600.0) heights

#shelf Source

shelf :: Number -> Viewport -> Array { height :: Number, width :: Number } -> Array Rect

Shelf layout — place items left-to-right, wrapping to the next row on overflow. Horizontal complement to masonry. Each item has its own width and height.

let rects = shelf 8.0 (viewport 800.0 600.0) [{width: 120.0, height: 80.0}, ...]

#shelfUniform Source

shelfUniform :: Number -> Number -> Viewport -> Array Number -> Array Rect

Shelf layout with uniform item height — only widths vary.

let rects = shelfUniform 40.0 8.0 (viewport 800.0 600.0) [100.0, 150.0, 80.0]

#waffle Source

waffle :: Int -> Int -> Viewport -> Array Int -> Array WaffleCell

Waffle chart — proportional unit grid (alternative to pie charts). A rows×cols grid where cells are colored by category proportional to each category's count. Cells filled left-to-right, top-to-bottom.

let cells = waffle 10 10 (viewport 400.0 400.0) [30, 25, 20, 15, 10]

#stacked Source

stacked :: Number -> Viewport -> Array (Array Number) -> Array (Array Rect)

Vertical stacked bar chart — groups of segments stacked bottom-to-top. Each inner array is one bar's segments. All bars scaled to the same maximum.

let rects = stacked 8.0 (viewport 800.0 400.0) [[30.0, 20.0, 10.0], [25.0, 35.0, 5.0]]

#stackedHorizontal Source

stackedHorizontal :: Number -> Viewport -> Array (Array Number) -> Array (Array Rect)

Horizontal stacked bar chart — segments stacked left-to-right.

#stackedDiverging Source

stackedDiverging :: Number -> Viewport -> Array (Array Number) -> Array (Array Rect)

Diverging stacked bar chart — segments extend in both directions from a center baseline. Positive values go right/up from center, negative go left/down.

#waterfall Source

waterfall :: Number -> Number -> Viewport -> Array Number -> Array Rect

Waterfall chart — cumulative bars where each starts where the previous ended. Positive deltas go up, negative deltas go down.

let rects = waterfall 40.0 8.0 (viewport 800.0 400.0) [100.0, 50.0, -30.0, 80.0, -20.0]

#justified Source

justified :: Number -> Number -> Viewport -> Array Number -> Array Rect

Justified layout — all rows fill exact viewport width, row heights vary. Like Google Images or Flickr galleries. Input is aspect ratios (width/height).

Algorithm: greedily fill rows. Each row's height = availableWidth / sum(aspectRatios). When height drops below threshold, break to next row. Last row left-aligned at target height.

let rects = justified 200.0 8.0 (viewport 800.0 600.0) [1.5, 0.75, 1.33, 2.0, 0.8]

#binPack Source

binPack :: Viewport -> Array { height :: Number, width :: Number } -> Array Rect

2D rectangle bin packing using Skyline Bottom-Left algorithm. Packs rectangles into the viewport like a texture atlas / sprite sheet. Items are sorted tallest-first for better packing.

let rects = binPack (viewport 800.0 600.0) [{width: 100.0, height: 80.0}, ...]

#calendarGrid Source

calendarGrid :: Int -> Int -> Number -> Viewport -> Array Rect

Calendar grid — 7-column weekly layout with weekday offset. Pure geometry: caller converts dates to day-of-week offset and day count.

-- March 2026 starts on Sunday (offset 0), has 31 days
let rects = calendarGrid 0 31 4.0 (viewport 700.0 500.0)

#swimlane Source

swimlane :: Int -> Number -> Viewport -> Array { end :: Number, lane :: Int, start :: Number } -> Array Rect

Swimlane layout — horizontal lanes with items positioned by start/end. For timelines, Gantt charts, kanban boards. Start and end values are normalized 0.0-1.0, mapped to viewport width.

let rects = swimlane 3 4.0 (viewport 800.0 300.0)
              [ {lane: 0, start: 0.0, end: 0.4}
              , {lane: 1, start: 0.2, end: 0.8}
              , {lane: 2, start: 0.5, end: 1.0}
              ]

Re-exports from DataViz.Layout.Pattern.Types

#WaffleCell Source

type WaffleCell = { category :: Int, rect :: Rect }

A cell in a waffle chart, tagged with its category index

#Viewport Source

type Viewport = { height :: Number, padding :: Padding, width :: Number }

Viewport specification - the bounding box for layout

#Rect Source

type Rect = { height :: Number, width :: Number, x :: Number, y :: Number }

A positioned rectangle

#Point Source

type Point = { x :: Number, y :: Number }

A 2D point

#Padding Source

type Padding = { bottom :: Number, left :: Number, right :: Number, top :: Number }

Padding specification (can be uniform or per-side)

#viewportWithPadding Source

viewportWithPadding :: Number -> Number -> Number -> Viewport

Create a viewport with uniform padding

#viewport Source

viewport :: Number -> Number -> Viewport

Create a viewport with no padding

#usableArea Source

usableArea :: Viewport -> { height :: Number, width :: Number, x :: Number, y :: Number }

Get the usable area after accounting for padding Returns { x, y, width, height } where x,y is the top-left of usable area

#uniformPadding Source

uniformPadding :: Number -> Padding

Uniform padding on all sides

#padding Source

padding :: { bottom :: Number, left :: Number, right :: Number, top :: Number } -> Padding

Per-side padding

#noPadding Source

noPadding :: Padding

No padding