This issue came out of work on the Hephaestus writer
Summary
resolve_common_steps expands a continuous scale's input range with expand_numeric_range_selective — plain linear arithmetic on the untransformed values — and only afterwards calls clip_to_transform_domain (src/plot/scale/scale_type/mod.rs:2017-2018, same order in src/plot/scale/types.rs:233). When the padded minimum crosses the transform's valid boundary, the clip replaces it with the boundary's smallest representable value rather than a sensible padded bound. For log that is f64::MIN_POSITIVE, which turns a 3-decade axis into a 311-decade one.
Reproduction
SELECT * FROM (VALUES (1), (10), (100), (1000)) AS t(v)
VISUALISE v AS x, v AS y
DRAW point
SCALE y VIA log
Resolved y scale in the Vega-Lite output:
"scale": { "type": "log", "base": 10, "domain": [2.2250738585072014e-308, 1049.95], "zero": false }
"axis": { "values": [4.999999999999997e-308, 1.999999999999999e-256, 9.999999999999996e-205,
9.999999999999996e-153, 4.999999999999999e-101, 2e-49, 1000.0] }
The data occupies the top ~1% of the axis, and the label expression VL emits for those breaks is 2498 characters of denormal decimal literals.
Expansion is not broken for log in general — it works whenever the padded bound stays positive (body_mass VIA log on ggsql:penguins resolves [2520, 6480], a real 5% pad). The trigger is min − mult·span − add <= 0, i.e. data spanning decades.
Impact
Both writers, differently:
- Vega-Lite — every point crushed against the top of the panel, plus the 2498-char
axis.labelExpr above.
- hephaestus — an essentially empty figure: chrome consumes the layout and only the
y title survives.
Suggested fix
Expand in transform space: transform the range, apply the multiplicative and additive expansion there, then invert. The padded bound then stays inside the transform's domain by construction, and clip_to_transform_domain becomes a safety net instead of the thing producing the domain.
This issue came out of work on the Hephaestus writer
Summary
resolve_common_stepsexpands a continuous scale's input range withexpand_numeric_range_selective— plain linear arithmetic on the untransformed values — and only afterwards callsclip_to_transform_domain(src/plot/scale/scale_type/mod.rs:2017-2018, same order insrc/plot/scale/types.rs:233). When the padded minimum crosses the transform's valid boundary, the clip replaces it with the boundary's smallest representable value rather than a sensible padded bound. Forlogthat isf64::MIN_POSITIVE, which turns a 3-decade axis into a 311-decade one.Reproduction
Resolved
yscale in the Vega-Lite output:The data occupies the top ~1% of the axis, and the label expression VL emits for those breaks is 2498 characters of denormal decimal literals.
Expansion is not broken for log in general — it works whenever the padded bound stays positive (
body_mass VIA logonggsql:penguinsresolves[2520, 6480], a real 5% pad). The trigger ismin − mult·span − add <= 0, i.e. data spanning decades.Impact
Both writers, differently:
axis.labelExprabove.ytitle survives.Suggested fix
Expand in transform space: transform the range, apply the multiplicative and additive expansion there, then invert. The padded bound then stays inside the transform's domain by construction, and
clip_to_transform_domainbecomes a safety net instead of the thing producing the domain.