Contour generation using the marching squares algorithm.
Computes contour polygons from gridded data. Useful for topographic maps, density visualizations, and isoline plots.
Examples
# Create contours from a 2D grid of values
grid = [
[0, 0, 0, 0],
[0, 5, 5, 0],
[0, 5, 10, 5],
[0, 0, 5, 0]
]
contours = Visualize.Contour.new()
|> Visualize.Contour.size(4, 4)
|> Visualize.Contour.thresholds([2.5, 7.5])
|> Visualize.Contour.compute(grid)
# Each contour has: value, type: "MultiPolygon", coordinates
# For density estimation from points, use Visualize.Contour.Density
Summary
Functions
Computes contours from a 2D grid of values.
The work one compute/2 does, as counts (spec/07 §6.3, #271): reads, the padded cells
the marching visits, once per threshold; segments, the segments it builds; hops, the
points the ring-following steps through. D-72's claim — the tracing is linear in the
segments and the grid is read once per threshold — is these numbers: hops never
exceeds segments, and reads is the padded cells times the thresholds, whatever the
grid's size; a test holds it by count rather than by a clock.
Creates a new contour generator
Renders contours as SVG path data.
Sets the grid dimensions
Enables or disables smoothing
Sets the threshold values for contour generation.
Types
@type t() :: %Visualize.Contour{ height: pos_integer(), smooth?: boolean(), thresholds: [number()] | pos_integer(), width: pos_integer() }
Functions
@spec compute(t(), [[number()]]) :: [contour_result()]
Computes contours from a 2D grid of values.
Returns a list of contour objects, each with:
- value: The threshold value
- type: "MultiPolygon"
- coordinates: GeoJSON-style coordinates
@spec cost(t(), [[number()]] | [number()]) :: %{ reads: non_neg_integer(), segments: non_neg_integer(), hops: non_neg_integer() }
The work one compute/2 does, as counts (spec/07 §6.3, #271): reads, the padded cells
the marching visits, once per threshold; segments, the segments it builds; hops, the
points the ring-following steps through. D-72's claim — the tracing is linear in the
segments and the grid is read once per threshold — is these numbers: hops never
exceeds segments, and reads is the padded cells times the thresholds, whatever the
grid's size; a test holds it by count rather than by a clock.
iex> grid = for y <- 0..3, x <- 0..3, do: x + y
iex> cost = Visualize.Contour.new() |> Visualize.Contour.size(4, 4) |> Visualize.Contour.thresholds([3]) |> Visualize.Contour.cost(grid)
iex> cost.reads
25
iex> cost.hops <= cost.segments
true
@spec new() :: t()
Creates a new contour generator
Renders contours as SVG path data.
Returns a list of path strings, one per threshold.
@spec size(t(), pos_integer(), pos_integer()) :: t()
Sets the grid dimensions
Enables or disables smoothing
@spec thresholds(t(), [number()] | pos_integer()) :: t()
Sets the threshold values for contour generation.
Can be a list of specific values or a count (generates that many levels).