The data-domain side of a chart mapping (linear, log, band, or time). Created by chart scale constructors; drives dependent marks and axes. Each plot area using the scale, as its own, an axis's or a mark's, assigns its own pixel range, so sharing one handle across areas links their domains while every area keeps its own geometry.
Resolved domain {min = N, max = N}, or nil while pending. Assign {min = N, max = N} or "auto"; dependent marks and axes re-render. Band scales read and assign the ordered category array instead (same list as categories).
Example
-- Assigning pins the domain; "auto" leaves it pending until the-- scale is fitted to data.
scale.domain = { min = -50, max = 50 }
print(scale.domain.min, scale.domain.max) --> -50.0 50.0
scale.domain = "auto"print(scale.domain) --> nil
categories
string[]?
read-only
Ordered category names of a band scale — the construction domain plus any categories marks discovered from their data by the time of the read. nil on linear / log / time scales, which map a numeric continuum and keep no category list.
Methods 9
Scale:map
method
Scale:map(value: number | string) -> number?
Data value → pixel position, resolved through the plot area using this scale. nil while pending (no domain, or no area has laid the scale out). Raises when areas with different pixel ranges share the scale — convert through area:data_to_world then.
Example
-- To canvas pixels across the area, which spans the 400-wide canvas,-- centered.print(scale:map(0), scale:map(50), scale:map(100)) --> -200.0 0.0 200.0
Scale:invert
method
Scale:invert(pixel: number) -> number | string | nil
Pixel position → data value, resolved through the plot area using this scale. Band scales return the name of the category whose slot contains the pixel. nil while pending or outside every slot. Raises when areas with different pixel ranges share the scale — convert through area:world_to_data then.
Example
-- From canvas pixels back to a data value.print(scale:invert(-100)) --> 25.0
Scan a column for min/max and set the domain. Returns self. Linear / log / time only. By default only writes if the domain is still pending, so a user pan / zoom during a long-running fit isn't overwritten when the scan finishes. Pass force = true to always apply.
Example
-- Fits a pending domain only, unless forced, so the fit never undoes-- a pan or zoom made while the data loaded.local loaves = datumhue.bytes("grams\n790\n812\n805\n"):csv()
scale:fit(loaves, "grams")
print(scale.domain.min, scale.domain.max) --> 0.0 100.0
scale:fit(loaves, "grams", true)
print(scale.domain.min, scale.domain.max) --> 790.0 812.0
Clear the domain and then refit from column. Equivalent to assigning domain = "auto" immediately followed by fit(data, column, true), but as one call. Returns self.
Width of one band, minus padding, resolved through the plot area using this scale. Band only; nil for numeric scales or while no area has laid the scale out. Raises when areas with different pixel ranges share the scale.
Scale:index_of
method
Scale:index_of(name: string) -> integer?
1-based category position for name, or nil if absent (band scales only). This is an ordinal for display/iteration; to position a mark, pass the category *name* to scale:map, not this number.
Example
-- 1-based, for listing; to place a mark, pass map the name itself.local days = datumhue.chart.scale.band({ domain = { "Mon", "Tue" } })
print(days:index_of("Tue"), days:index_of("Sun")) --> 2 nil
Ease the domain toward a target over a duration; dependent marks and axes re-render every frame. Pins the domain like a direct assignment. A scale whose domain is still pending snaps to the target immediately; a new call replaces an in-flight animation from the current domain. Errors on band scales.
Example
-- Eases to the new domain over the duration, in seconds.
scale:animate({
domain = { min = 0, max = 10 },
duration = 0.5,
easing = "quadratic_out",
})
datumhue.time.sleep(0.6)
print(scale.domain.max) --> 10.0
Subscribe to changes of the scale's resolved domain — assignment, pan / zoom, fit / reset, animation frames, and band-category updates all count. Fires at most once per frame while the domain is changing (a continuous pan delivers one call per frame). Subscribing does not fire for the domain the scale already has. Multiple subscriptions can be active on one scale.