The area's x-axis Scale handle — the one passed at construction, or the default linear scale created for the area when none was passed (removed with the area). Read-only; swap scales via area:update({x_scale = ...}).
Add a line mark. Exactly one of points, data, or view is required. Rows with null values are skipped; a null in the x or y column breaks the line at that row.
Example
-- Exactly one of points, data or view; data names its two columns.local temps =
datumhue.bytes("hour,temp\n6,180\n7,215\n8,230\n9,224\n"):csv()
plot:line({
data = temps,
x_column = "hour",
y_column = "temp",
width = 2,
color = color("#e69875"),
})
Category column splitting rows into one series per distinct value (data sources only). Each series draws as its own line, takes a color from color_scale or the category palette in first-seen order, and contributes its own legend item.
Add a scatter (dots) mark. Exactly one of points, data, or view is required; data-bound points render every row in the visible range. Rows with null values are skipped.
Color scale for color_column. A ramp whose domain is pending fits the column's extent once the data loads, and again when Mark:replace_data swaps the data.
Add a filled-area mark — closed polygon between a data line and a numeric baseline. Exactly one of points, data, or view required. Rows with null values are skipped; a null in the x or y column breaks the area at that row.
Example
-- Fills between the line and the baseline, in data units; without a-- color, in the theme's chart-label color at half opacity.
plot:area({
points = { vec2(0, 2), vec2(1, 5), vec2(2, 3), vec2(3, 6) },
baseline = 1,
})
Category column splitting rows into one series per distinct value (data sources only). Each series draws as its own area, takes a color from color_scale or the category palette in first-seen order, and contributes its own legend item.
Add a horizontal or vertical reference line at a data-space value.
Example
-- value is in data units on the axis the line crosses.
plot.x_scale.domain = { min = 0, max = 10 }
plot.y_scale.domain = { min = 0, max = 10 }
plot:rule({
orientation = "horizontal",
value = 5,
width = 2,
color = color("#e67e80"),
value_label = "target",
})
Data-space position of the line. A category name resolves to the band scale's slot center when the mark is created; raises when the axis scale isn't a band scale or the category is unknown.
-- pos is in data units; offset moves the text in pixels, y up.
plot.x_scale.domain = { min = 0, max = 10 }
plot.y_scale.domain = { min = 0, max = 10 }
plot:text({
pos = vec2(5, 5),
text = "halfway",
align = "bottom_left",
offset = vec2(6, 6),
font_size = 18,
color = color("#e69875"),
})
Data-space anchor position. In the table form a category name resolves to the band scale's slot center when the mark is created; raises when that axis's scale isn't a band scale or the category is unknown.
Add a filled highlighted region spanning a [from, to] range on one axis and the full inner rect on the orthogonal axis.
Example
-- Shades x from 2 to 4 across the full height of the area.
plot.x_scale.domain = { min = 0, max = 10 }
plot:band({
axis = "x",
from = 2,
to = 4,
color = color("#dbbc7f"),
})
Add a bar mark — one rectangle per category, from inline entries/series or a data source grouped by category. Requires a band scale on the categorical axis; discovered categories append to it.
Example
-- Bars need a band scale on their category axis: the area's, or one-- given to the mark, which the area lays out.local days = datumhue.chart.scale.band({ domain = { "Mon", "Tue" } })
plot:bars({
entries = {
{ category = "Mon", value = 42 },
{ category = "Tue", value = 30 },
},
x_scale = days,
})
Add a histogram — contiguous bins over a numeric column or inline values. Pending scales fit to the bin extent and peak; a zoomed x scale re-bins over the visible domain. Rows with null values are skipped.
Example
-- bin_width is in data units; "fraction" makes the bars sum to 1.
plot:histogram({
values = { 1.2, 1.9, 2.1, 2.4, 2.8, 3.1, 3.3, 4.6 },
bin_width = 1,
normalize = "fraction",
})
Add a categorical heatmap — one colored cell per (x, y) category pair, from inline cells, a matrix, or a data source grouped by category pair. Both axes need band scales; discovered categories append to them.
Add a box-and-whisker mark — one five-number summary per category, from pre-computed entries or a data source grouped by category. Requires a band scale on the categorical axis; discovered categories append to it.
Example
-- Quartiles, whiskers and outliers are computed from the rows.local csv = "bread,grams\nRye,780\nRye,795\nRye,800\nRye,806\nRye,822"local loaves = datumhue.bytes(csv):csv()
local breads = datumhue.chart.scale.band({ domain = { "Rye" } })
plot:update({ x_scale = breads })
plot:box({
data = loaves,
category_column = "bread",
value_column = "grams",
})
Tabular source; requires category_column and value_column. Summaries take quartiles by linear interpolation, whiskers at the farthest values inside the 1.5 IQR fences, and the rest as outliers.
Explicit tick positions as data values; on a band scale, 0-based band indices (0 is the first category), as scale:map takes them.
label
string
no axis title
Axis title, centered along the axis beyond the tick labels; on a vertical axis it reads along the axis.
font_size
number | string
the theme's small text size
Size of the tick labels and the axis label — a number (px) or a unit string like "1.5rem"/"50vw". The gaps between the axis line, tick labels, and axis label grow with it.
format
string
the scale's own tick labels
Tick-label format string; the scale's kind selects how it is read. On a time scale it is a UTC date pattern of literal text plus %Y%m%d%H%M%S (%% for a percent sign), and an unknown specifier raises. On every other scale it is a numeric dialect with three forms: {:.N} for N fixed decimals, {} for plain stringification, or literal text with each {value} substituted.
Size of the item labels, or of a gradient legend's value labels — a number (px) or a unit string like "1.5rem"/"50vw". Swatches, row height, and panel padding scale with it.
Update the area geometry, scales, or background in place. Pass background = color(...) to pin a concrete fill, or background = false to opt back into the theme default.
Example
-- A margin table's sides are pixels; a side left out is 0.
plot:update({
margin = { left = 80, top = 80 },
background = color("#dbbc7f"),
})
Canvas coord → data coord using current scales. Exact for time scales (full-precision epoch-ms).
Example
-- Canvas pixels, y up, to data. The margins put the center of the-- inner rect at x = 12.
plot:line({ points = { vec2(0, 0), vec2(100, 10) } })
-- The scales fit the line when the frame ends.datumhue.time.wait_frames(1)
local mid = plot:world_to_data(vec2(12, 0))
print(mid.x, mid.y) --> 50.0 5.0
Data coord → canvas coord. An {x, y} table keeps a time-scale x exact (a vec2's single-precision x rounds epoch-ms first).
Example
-- Data to canvas pixels; an {x, y} table keeps a time x exact.
plot:line({ points = { vec2(0, 0), vec2(100, 10) } })
datumhue.time.wait_frames(1)
print(plot:data_to_world({ x = 100, y = 10 })) --> vec2(184.0, 110.0)
Clear both scale domains and refit against only currently-visible marks. Scales the user has pinned with pan / zoom / box-zoom are left alone. Pass {force = true} to always refit, matching the double-click-reset gesture.
Example
-- Refits both domains to the marks still shown.
plot:line({ points = { vec2(0, 1), vec2(4, 2) } })
local high = plot:line({ points = { vec2(0, 8), vec2(4, 9) } })
datumhue.time.wait_frames(1)
high.visible = false
plot:rescale({ force = true })
datumhue.time.wait_frames(1)
print(plot.y_scale.domain.min, plot.y_scale.domain.max) --> 1.0 2.0
Pin a title to the plot area. One title per area — re-calling replaces any prior title. Auto-repositions on area resize. Raises if the plot area was removed.
Example
-- The title sits in the top margin; a second call replaces it.
plot:title({ text = "Loaves sold", font_size = 18, align = "center" })
Draw a hover crosshair: guide lines at the cursor, a readout text with the cursor's data-space x/y, and dots + labels at every line / area mark's y(cursor.x). Overlays redraw each frame; nothing persists when the cursor leaves the plot area. One crosshair per area — raises while one is active; update or remove it instead.
Example
-- Guide lines and a readout follow the pointer over the area.
plot:line({ points = { vec2(0, 1), vec2(8, 3) } })
plot:crosshair({ readout = true, line_color = color("#e67e80") })
Enable built-in pan / zoom / box-zoom / double-click-reset interaction on the plot area. Works on linear, log, and time scales; band scales are skipped. Data-bound marks re-downsample at every zoom so detail stays pixel-faithful at any zoom level. One per area — raises while one is active; update or remove it instead.
Example
-- Pans and zooms x only, with the right-drag box zoom turned off.
plot:pan_zoom({ y = false, box_zoom_button = false })
Enable a brush selection gesture on the plot area. Dragging sweeps a tinted selection region; releasing delivers the selection's data-space bounds to on_brush, and a plain click clears the selection and delivers nil. The selection region persists until cleared, replaced, or the brush is removed. One brush per area — raises while one is active; update or remove it instead.
Example
-- on_brush gets the bounds in data units, or nil when a click-- clears the selection.
plot:brush({
axis = "x",
on_brush = function(selection)
if selection thenprint(selection.x_min, selection.x_max)
endend,
})
Register a callback fired every frame the cursor is inside the plot area's inner rect, with the cursor in data space (exact for time scales). Pass nil to clear.
Y-value at data-space x for each line / area mark on this area. Rows match the mark's readout setting. Empty if no mark has a value at x.
Example
-- The y of each line or area mark at x, in data units.
plot:line({ points = { vec2(0, 1), vec2(10, 3) }, name = "North" })
datumhue.time.wait_frames(1)
local values = plot:value_at(5)
print(#values, values[1].name, values[1].value) --> 1 North 2.0
The cursor in data space. Exact for time scales (full-precision epoch-ms). nil when the cursor is outside the plot area's inner rect, or while either scale's domain is pending.
The cursor in data and canvas space, with whether it is inside the area. Unlike mouse_data_position, returns a value even when the cursor is outside the area. nil when the cursor is not over the canvas, or while either scale's domain is pending.
Whether the cursor is inside the plot area's inner rect.
PlotArea:clear
method
PlotArea:clear()
Remove the area's content — every mark (line / points / bars / area / rule / text / band / histogram / heatmap / box), axis, legend, group, and its title. The area keeps its background, interaction overlays, and scales; the canvas survives.
Bundle a set of mark handles for synchronized visibility toggles and lifecycle. Not a hierarchy — marks render where they were created. Raises if the plot area was removed.
Example
-- Hiding the group hides both lines.local north = plot:line({ points = { vec2(0, 1), vec2(8, 3) } })
local south = plot:line({ points = { vec2(0, 2), vec2(8, 1) } })
local both = plot:group({ north, south })
both.visible = falseprint(north.visible, south.visible) --> false false
PlotArea:remove
method
PlotArea:remove()
Destroy the plot area and everything on it — marks, axes, legends, groups, its title and overlays, and the scales it created for itself. The canvas survives.