From 2b5f749a78e805b32dd2cedd4a35b541ca0b7da3 Mon Sep 17 00:00:00 2001 From: Johannes Wolf Date: Mon, 25 Nov 2024 02:53:02 +0100 Subject: [PATCH] repo: Remove axes.typ --- src/axes.typ | 890 --------------------------------------------------- 1 file changed, 890 deletions(-) delete mode 100644 src/axes.typ diff --git a/src/axes.typ b/src/axes.typ deleted file mode 100644 index dd01c8e..0000000 --- a/src/axes.typ +++ /dev/null @@ -1,890 +0,0 @@ -#import "/src/cetz.typ": util, draw, vector, matrix, styles, process, drawable, path-util, process -#import "/src/plot/formats.typ" - -#let default-style = ( - tick-limit: 100, - minor-tick-limit: 1000, - auto-tick-factors: (1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10), // Tick factor to try - auto-tick-count: 11, // Number of ticks the plot tries to place - fill: none, - stroke: auto, - label: ( - offset: .2cm, // Axis label offset - anchor: auto, // Axis label anchor - angle: auto, // Axis label angle - ), - axis-layer: 0, - grid-layer: 0, - background-layer: 0, - padding: 0, - tick: ( - fill: none, - stroke: black + 1pt, - minor-stroke: black + .5pt, - offset: 0, - minor-offset: 0, - length: .1cm, // Tick length: Number - minor-length: 70%, // Minor tick length: Number, Ratio - label: ( - offset: .15cm, // Tick label offset - angle: 0deg, // Tick label angle - anchor: auto, // Tick label anchor - "show": auto, // Show tick labels for axes in use - ) - ), - break-point: ( - width: .75cm, - length: .15cm, - ), - grid: ( - stroke: (paint: gray.lighten(50%), thickness: 1pt), - ), - minor-grid: ( - stroke: (paint: gray.lighten(50%), thickness: .5pt), - ), -) - -// Default Scientific Style -#let default-style-scientific = util.merge-dictionary(default-style, ( - left: (tick: (label: (anchor: "east"))), - bottom: (tick: (label: (anchor: "north"))), - right: (tick: (label: (anchor: "west"))), - top: (tick: (label: (anchor: "south"))), - stroke: (cap: "square"), - padding: 0, -)) - -// Default Schoolbook Style -#let default-style-schoolbook = util.merge-dictionary(default-style, ( - x: (stroke: auto, fill: none, mark: (start: none, end: "straight"), - tick: (label: (anchor: "north"))), - y: (stroke: auto, fill: none, mark: (start: none, end: "straight"), - tick: (label: (anchor: "east"))), - label: (offset: .1cm), - origin: (label: (offset: .05cm)), - padding: .1cm, // Axis padding on both sides outsides the plotting area - overshoot: .5cm, // Axis end "overshoot" out of the plotting area - tick: ( - offset: -50%, - minor-offset: -50%, - length: .2cm, - minor-length: 70%, - ), - shared-zero: $0$, // Show zero tick label at (0, 0) -)) - -#let _prepare-style(ctx, style) = { - if type(style) != dictionary { return style } - - let res = util.resolve-number.with(ctx) - let rel-to(v, to) = { - if type(v) == ratio { - return v * to / 100% - } else { - return res(v) - } - } - - style.tick.length = res(style.tick.length) - style.tick.offset = rel-to(style.tick.offset, style.tick.length) - style.tick.minor-length = rel-to(style.tick.minor-length, style.tick.length) - style.tick.minor-offset = rel-to(style.tick.minor-offset, style.tick.minor-length) - style.tick.label.offset = res(style.tick.label.offset) - - // Break points - style.break-point.width = res(style.break-point.width) - style.break-point.length = res(style.break-point.length) - - // Padding - style.padding = res(style.padding) - - if "overshoot" in style { - style.overshoot = res(style.overshoot) - } - - return style -} - -#let _get-axis-style(ctx, style, name) = { - if not name in style { - return style - } - - style = styles.resolve(style, merge: style.at(name)) - return _prepare-style(ctx, style) -} - -#let _get-grid-type(axis) = { - let grid = axis.ticks.at("grid", default: false) - if grid == "major" or grid == true { return 1 } - if grid == "minor" { return 2 } - if grid == "both" { return 3 } - return 0 -} - -#let _inset-axis-points(ctx, style, axis, start, end) = { - if axis == none { return (start, end) } - - let (low, high) = axis.inset.map(v => util.resolve-number(ctx, v)) - - let is-horizontal = start.at(1) == end.at(1) - if is-horizontal { - start = vector.add(start, (low, 0)) - end = vector.sub(end, (high, 0)) - } else { - start = vector.add(start, (0, low)) - end = vector.sub(end, (0, high)) - } - return (start, end) -} - -#let _draw-axis-line(start, end, axis, is-horizontal, style) = { - let enabled = if axis != none and axis.show-break { - axis.min > 0 or axis.max < 0 - } else { false } - - if enabled { - let size = if is-horizontal { - (style.break-point.width, 0) - } else { - (0, style.break-point.width, 0) - } - - let up = if is-horizontal { - (0, style.break-point.length) - } else { - (style.break-point.length, 0) - } - - let add-break(is-end) = { - let a = () - let b = (rel: vector.scale(size, .3), update: false) - let c = (rel: vector.add(vector.scale(size, .4), vector.scale(up, -1)), update: false) - let d = (rel: vector.add(vector.scale(size, .6), vector.scale(up, +1)), update: false) - let e = (rel: vector.scale(size, .7), update: false) - let f = (rel: size) - - let mark = if is-end { - style.at("mark", default: none) - } - draw.line(a, b, c, d, e, f, stroke: style.stroke, mark: mark) - } - - draw.merge-path({ - draw.move-to(start) - if axis.min > 0 { - add-break(false) - draw.line((rel: size, to: start), end, mark: style.at("mark", default: none)) - } else if axis.max < 0 { - draw.line(start, (rel: vector.scale(size, -1), to: end)) - add-break(true) - } - }, stroke: style.stroke) - } else { - draw.line(start, end, stroke: style.stroke, mark: style.at("mark", default: none)) - } -} - -// Construct Axis Object -// -// - min (number): Minimum value -// - max (number): Maximum value -// - ticks (dictionary): Tick settings: -// - step (number): Major tic step -// - minor-step (number): Minor tic step -// - decimals (int): Tick float decimal length -// - label (content): Axis label -// - mode (string): Axis scaling function. Takes `lin` or `log` -// - base (number): Base for tick labels when logarithmically scaled. -#let axis(min: -1, max: 1, label: none, - ticks: (step: auto, minor-step: none, - decimals: 2, grid: false, - format: "float" - ), - mode: auto, base: auto) = ( - min: min, max: max, ticks: ticks, label: label, inset: (0, 0), show-break: false, mode: mode, base: base, - kind: "cartesian", -) - -// Format a tick value -#let format-tick-value(value, tic-options) = { - // Without it we get negative zero in conversion - // to content! Typst has negative zero floats. - if value == 0 { value = 0 } - - if type(value) != std.content { - let format = tic-options.at("format", default: "float") - if format == none { - value = [] - } else if type(format) == std.content { - value = format - } else if type(format) == function { - value = (format)(value) - } else if format == "sci" { - value = formats.sci(value, digits: tic-options.at("decimals", default: 2)) - } else { - value = formats.decimal(value, digits: tic-options.at("decimals", default: 2)) - } - } else if type(value) != std.content { - value = str(value) - } - - return value -} - -// Get value on axis [0, 1] -// -// - axis (axis): Axis -// - v (number): Value -// -> float -#let value-on-axis(axis, v) = { - if v == none { return } - let (min, max) = (axis.min, axis.max) - let dt = max - min; if dt == 0 { dt = 1 } - - return (v - min) / dt -} - -// Compute list of linear ticks for axis -// -// - axis (axis): Axis -#let compute-linear-ticks(axis, style, add-zero: true) = { - let (min, max) = (axis.min, axis.max) - let dt = max - min; if (dt == 0) { dt = 1 } - let ticks = axis.ticks - let ferr = util.float-epsilon - let tick-limit = style.tick-limit - let minor-tick-limit = style.minor-tick-limit - - let l = () - if ticks != none { - let major-tick-values = () - if "step" in ticks and ticks.step != none { - assert(ticks.step >= 0, - message: "Axis tick step must be positive and non 0.") - if axis.min > axis.max { ticks.step *= -1 } - - let s = 1 / ticks.step - - let num-ticks = int(max * s + 1.5) - int(min * s) - assert(num-ticks <= tick-limit, - message: "Number of major ticks exceeds limit " + str(tick-limit)) - - let n = range(int(min * s), int(max * s + 1.5)) - for t in n { - let v = (t / s - min) / dt - if t / s == 0 and not add-zero { continue } - - if v >= 0 - ferr and v <= 1 + ferr { - l.push((v, format-tick-value(t / s, ticks), true)) - major-tick-values.push(v) - } - } - } - - if "minor-step" in ticks and ticks.minor-step != none { - assert(ticks.minor-step >= 0, - message: "Axis minor tick step must be positive") - if axis.min > axis.max { ticks.minor-step *= -1 } - - let s = 1 / ticks.minor-step - - let num-ticks = int(max * s + 1.5) - int(min * s) - assert(num-ticks <= minor-tick-limit, - message: "Number of minor ticks exceeds limit " + str(minor-tick-limit)) - - let n = range(int(min * s), int(max * s + 1.5)) - for t in n { - let v = (t / s - min) / dt - if v in major-tick-values { - // Prefer major ticks over minor ticks - continue - } - - if v != none and v >= 0 and v <= 1 + ferr { - l.push((v, none, false)) - } - } - } - - } - - return l -} - -// Compute list of linear ticks for axis -// -// - axis (axis): Axis -#let compute-logarithmic-ticks(axis, style, add-zero: true) = { - let ferr = util.float-epsilon - let (min, max) = ( - calc.log(calc.max(axis.min, ferr), base: axis.base), - calc.log(calc.max(axis.max, ferr), base: axis.base) - ) - let dt = max - min; if (dt == 0) { dt = 1 } - let ticks = axis.ticks - - let tick-limit = style.tick-limit - let minor-tick-limit = style.minor-tick-limit - let l = () - - if ticks != none { - let major-tick-values = () - if "step" in ticks and ticks.step != none { - assert(ticks.step >= 0, - message: "Axis tick step must be positive and non 0.") - if axis.min > axis.max { ticks.step *= -1 } - - let s = 1 / ticks.step - - let num-ticks = int(max * s + 1.5) - int(min * s) - assert(num-ticks <= tick-limit, - message: "Number of major ticks exceeds limit " + str(tick-limit)) - - let n = range( - int(min * s), - int(max * s + 1.5) - ) - - for t in n { - let v = (t / s - min) / dt - if t / s == 0 and not add-zero { continue } - - if v >= 0 - ferr and v <= 1 + ferr { - l.push((v, format-tick-value( calc.pow(axis.base, t / s), ticks), true)) - major-tick-values.push(v) - } - } - } - - if "minor-step" in ticks and ticks.minor-step != none { - assert(ticks.minor-step >= 0, - message: "Axis minor tick step must be positive") - if axis.min > axis.max { ticks.minor-step *= -1 } - - let s = 1 / ticks.step - let n = range(int(min * s)-1, int(max * s + 1.5)+1) - - for t in n { - for vv in range(1, int(axis.base / ticks.minor-step)) { - - let v = ( (calc.log(vv * ticks.minor-step, base: axis.base) + t)/ s - min) / dt - if v in major-tick-values {continue} - - if v != none and v >= 0 and v <= 1 + ferr { - l.push((v, none, false)) - } - - } - - } - } - } - - return l -} - -// Get list of fixed axis ticks -// -// - axis (axis): Axis object -#let fixed-ticks(axis) = { - let l = () - if "list" in axis.ticks { - for t in axis.ticks.list { - let (v, label) = (none, none) - if type(t) in (float, int) { - v = t - label = format-tick-value(t, axis.ticks) - } else { - (v, label) = t - } - - v = value-on-axis(axis, v) - if v != none and v >= 0 and v <= 1 { - l.push((v, label, true)) - } - } - } - return l -} - -// Compute list of axis ticks -// -// A tick triple has the format: -// (rel-value: float, label: content, major: bool) -// -// - axis (axis): Axis object -#let compute-ticks(axis, style, add-zero: true) = { - let find-max-n-ticks(axis, n: 11) = { - let dt = calc.abs(axis.max - axis.min) - let scale = calc.floor(calc.log(dt, base: 10) - 1) - if scale > 5 or scale < -5 {return none} - - let (step, best) = (none, 0) - for s in style.auto-tick-factors { - s = s * calc.pow(10, scale) - - let divs = calc.abs(dt / s) - if divs >= best and divs <= n { - step = s - best = divs - } - } - return step - } - - if axis == none or axis.ticks == none { return () } - if axis.ticks.step == auto { - axis.ticks.step = find-max-n-ticks(axis, n: style.auto-tick-count) - } - if axis.ticks.minor-step == auto { - axis.ticks.minor-step = if axis.ticks.step != none { - axis.ticks.step / 5 - } else { - none - } - } - - let ticks = if axis.mode == "log" { - compute-logarithmic-ticks(axis, style, add-zero: add-zero) - } else { - compute-linear-ticks(axis, style, add-zero: add-zero) - } - ticks += fixed-ticks(axis) - return ticks -} - -// Prepares the axis post creation. The given axis -// must be completely set-up, including its interval. -// Returns the prepared axis -#let prepare-axis(ctx, axis, name) = { - let style = styles.resolve(ctx.style, root: "axes", - base: default-style-scientific) - style = _prepare-style(ctx, style) - style = _get-axis-style(ctx, style, name) - - if type(axis.inset) != array { - axis.inset = (axis.inset, axis.inset) - } - - axis.inset = axis.inset.map(v => util.resolve-number(ctx, v)) - - if axis.show-break { - if axis.min > 0 { - axis.inset.at(0) += style.break-point.width - } else if axis.max < 0 { - axis.inset.at(1) += style.break-point.width - } - } - - return axis -} - -// Transform a single value along a cartesian axis -#let transform-cartesian(axis, v) = { - let a = axis.origin - let b = axis.target - - let length = vector.dist(a, b) - axis.inset.sum() - let offset = axis.inset.at(0) - - let transform-func(n) = if axis.mode == "log" { - calc.log(calc.max(n, util.float-epsilon), base: axis.base) - } else { - n - } - - let factor = length / (transform-func(axis.max) - transform-func(axis.min)) - return vector.scale( - vector.norm(vector.sub(b, a)), - (transform-func(v) - transform-func(axis.min)) * factor + offset) -} - -// Transform a single vector along a x, y and z axis -// -// - axes (list): List of axes -// - vec (vector): Input vector to transform -// -> vector -#let transform-vec(axes, vec) = { - let res = (0, 0, 0) - for (dim, axis) in axes.enumerate() { - let v = vec.at(dim, default: 0) - - if axis.kind == "cartesian" { - res = vector.add(res, transform-cartesian(axis, v)) - } else { - panic("Unknown axit type " + repr(axis.kind)) - } - } - return res -} - -// Draw inside viewport coordinates of two axes -// -// - axes (list): List of axes -// - name (string,none): Group name -#let axis-viewport(axes, body, name: none) = { - draw.group(name: name, (ctx => { - let transform = ctx.transform - - ctx.transform = matrix.ident() - let (ctx, drawables, bounds) = process.many(ctx, util.resolve-body(ctx, body)) - - ctx.transform = transform - - drawables = drawables.map(d => { - if "segments" in d { - d.segments = d.segments.map(((kind, ..pts)) => { - (kind, ..pts.map(pt => { - transform-vec(axes, pt) - })) - }) - } - if "pos" in d { - d.pos = transform-vec(axes, d.pos) - } - return d - }) - - return ( - ctx: ctx, - drawables: drawable.apply-transform(ctx.transform, drawables) - ) - },)) -} - -// Draw grid lines for the ticks of an axis -// -// - cxt (context): -// - axis (dictionary): The axis -// - ticks (array): The computed ticks -// - low (vector): Start position of a grid-line at tick 0 -// - high (vector): End position of a grid-line at tick 0 -// - dir (vector): Normalized grid direction vector along the grid axis -// - style (style): Axis style -#let draw-grid-lines(ctx, axis, ticks, low, high, dir, style) = { - let offset = (0,0) - if axis.inset != none { - let (inset-low, inset-high) = axis.inset.map(v => util.resolve-number(ctx, v)) - offset = vector.scale(vector.norm(dir), inset-low) - dir = vector.sub(dir, vector.scale(vector.norm(dir), inset-low + inset-high)) - } - - let kind = _get-grid-type(axis) - if kind > 0 { - for (distance, label, is-major) in ticks { - let offset = vector.add(vector.scale(dir, distance), offset) - let start = vector.add(low, offset) - let end = vector.add(high, offset) - - // Draw a major line - if is-major and (kind == 1 or kind == 3) { - draw.line(start, end, stroke: style.grid.stroke) - } - // Draw a minor line - if not is-major and kind >= 2 { - draw.line(start, end, stroke: style.minor-grid.stroke) - } - } - } -} - -// Place a list of tick marks and labels along a path -#let place-ticks-on-line(ticks, start, stop, style, flip: false, is-mirror: false) = { - let dir = vector.sub(stop, start) - let norm = vector.norm((-dir.at(1), dir.at(0), dir.at(2, default: 0))) - - let def(v, d) = { - return if v == none or v == auto {d} else {v} - } - - let show-label = style.tick.label.show - if show-label == auto { - show-label = not is-mirror - } - - for (distance, label, is-major) in ticks { - let offset = style.tick.offset - let length = if is-major { style.tick.length } else { style.tick.minor-length } - if flip { - offset *= -1 - length *= -1 - } - - let pt = vector.lerp(start, stop, distance) - let a = vector.add(pt, vector.scale(norm, offset)) - let b = vector.add(a, vector.scale(norm, length)) - - draw.line(a, b, stroke: style.tick.stroke) - - if show-label and label != none { - let offset = style.tick.label.offset - if flip { - offset *= -1 - length *= -1 - } - - let c = vector.sub(if length <= 0 { b } else { a }, - vector.scale(norm, offset)) - - let angle = def(style.tick.label.angle, 0deg) - let anchor = def(style.tick.label.anchor, "center") - - draw.content(c, [#label], angle: angle, anchor: anchor) - } - } -} - -// Draw up to four axes in an "scientific" style at origin (0, 0) -// -// - size (array): Size (width, height) -// - left (axis): Left (y) axis -// - bottom (axis): Bottom (x) axis -// - right (axis): Right axis -// - top (axis): Top axis -// - name (string): Object name -// - draw-unset (bool): Draw axes that are set to `none` -// - ..style (any): Style -#let scientific(size: (1, 1), - left: none, - right: auto, - bottom: none, - top: auto, - draw-unset: true, - name: none, - ..style) = { - import draw: * - - if right == auto { - if left != none { - right = left; right.is-mirror = true - } else { - right = none - } - } - if top == auto { - if bottom != none { - top = bottom; top.is-mirror = true - } else { - top = none - } - } - - group(name: name, ctx => { - let (w, h) = size - anchor("origin", (0, 0)) - - let style = style.named() - style = styles.resolve(ctx.style, merge: style, root: "axes", - base: default-style-scientific) - style = _prepare-style(ctx, style) - - // Compute ticks - let x-ticks = compute-ticks(bottom, style) - let y-ticks = compute-ticks(left, style) - let x2-ticks = compute-ticks(top, style) - let y2-ticks = compute-ticks(right, style) - - // Draw frame - if style.fill != none { - on-layer(style.background-layer, { - rect((0,0), (w,h), fill: style.fill, stroke: none) - }) - } - - // Draw grid - group(name: "grid", ctx => { - let axes = ( - ("bottom", (0,0), (0,h), (+w,0), x-ticks, bottom), - ("top", (0,h), (0,0), (+w,0), x2-ticks, top), - ("left", (0,0), (w,0), (0,+h), y-ticks, left), - ("right", (w,0), (0,0), (0,+h), y2-ticks, right), - ) - for (name, start, end, direction, ticks, axis) in axes { - if axis == none { continue } - - let style = _get-axis-style(ctx, style, name) - let is-mirror = axis.at("is-mirror", default: false) - - if not is-mirror { - on-layer(style.grid-layer, { - draw-grid-lines(ctx, axis, ticks, start, end, direction, style) - }) - } - } - }) - - // Draw axes - group(name: "axes", { - let axes = ( - ("bottom", (0, 0), (w, 0), (0, -1), false, x-ticks, bottom,), - ("top", (0, h), (w, h), (0, +1), true, x2-ticks, top,), - ("left", (0, 0), (0, h), (-1, 0), true, y-ticks, left,), - ("right", (w, 0), (w, h), (+1, 0), false, y2-ticks, right,) - ) - let label-placement = ( - bottom: ("south", "north", 0deg), - top: ("north", "south", 0deg), - left: ("west", "south", 90deg), - right: ("east", "north", 90deg), - ) - - for (name, start, end, outsides, flip, ticks, axis) in axes { - let style = _get-axis-style(ctx, style, name) - let is-mirror = axis == none or axis.at("is-mirror", default: false) - let is-horizontal = name in ("bottom", "top") - - if style.padding != 0 { - let padding = vector.scale(outsides, style.padding) - start = vector.add(start, padding) - end = vector.add(end, padding) - } - - let (data-start, data-end) = _inset-axis-points(ctx, style, axis, start, end) - - let path = _draw-axis-line(start, end, axis, is-horizontal, style) - on-layer(style.axis-layer, { - group(name: "axis", { - if draw-unset or axis != none { - path; - place-ticks-on-line(ticks, data-start, data-end, style, flip: flip, is-mirror: is-mirror) - } - }) - - if axis != none and axis.label != none and not is-mirror { - let offset = vector.scale(outsides, style.label.offset) - let (group-anchor, content-anchor, angle) = label-placement.at(name) - - if style.label.anchor != auto { - content-anchor = style.label.anchor - } - if style.label.angle != auto { - angle = style.label.angle - } - - content((rel: offset, to: "axis." + group-anchor), - [#axis.label], - angle: angle, - anchor: content-anchor) - } - }) - } - }) - }) -} - -// Draw two axes in a "school book" style -// -// - x-axis (axis): X axis -// - y-axis (axis): Y axis -// - size (array): Size (width, height) -// - x-position (number): X Axis position -// - y-position (number): Y Axis position -// - name (string): Object name -// - ..style (any): Style -#let school-book(x-axis, y-axis, - size: (1, 1), - x-position: 0, - y-position: 0, - name: none, - ..style) = { - import draw: * - - group(name: name, ctx => { - let (w, h) = size - anchor("origin", (0, 0)) - - let style = style.named() - style = styles.resolve( - ctx.style, - merge: style, - root: "axes", - base: default-style-schoolbook) - style = _prepare-style(ctx, style) - - let x-position = calc.min(calc.max(y-axis.min, x-position), y-axis.max) - let y-position = calc.min(calc.max(x-axis.min, y-position), x-axis.max) - let x-y = value-on-axis(y-axis, x-position) * h - let y-x = value-on-axis(x-axis, y-position) * w - - let shared-zero = style.shared-zero != false and x-position == 0 and y-position == 0 - - let x-ticks = compute-ticks(x-axis, style, add-zero: not shared-zero) - let y-ticks = compute-ticks(y-axis, style, add-zero: not shared-zero) - - // Draw grid - group(name: "grid", ctx => { - let axes = ( - ("x", (0,0), (0,h), (+w,0), x-ticks, x-axis), - ("y", (0,0), (w,0), (0,+h), y-ticks, y-axis), - ) - - for (name, start, end, direction, ticks, axis) in axes { - if axis == none { continue } - - let style = _get-axis-style(ctx, style, name) - on-layer(style.grid-layer, { - draw-grid-lines(ctx, axis, ticks, start, end, direction, style) - }) - } - }) - - // Draw axes - group(name: "axes", { - let axes = ( - ("x", (0, x-y), (w, x-y), (1, 0), false, x-ticks, x-axis), - ("y", (y-x, 0), (y-x, h), (0, 1), true, y-ticks, y-axis), - ) - let label-pos = ( - x: ("north", (0,-1)), - y: ("east", (-1,0)), - ) - - on-layer(style.axis-layer, { - for (name, start, end, dir, flip, ticks, axis) in axes { - let style = _get-axis-style(ctx, style, name) - - let pad = style.padding - let overshoot = style.overshoot - let vstart = vector.sub(start, vector.scale(dir, pad)) - let vend = vector.add(end, vector.scale(dir, pad + overshoot)) - let is-horizontal = name == "x" - - let (data-start, data-end) = _inset-axis-points(ctx, style, axis, start, end) - group(name: "axis", { - _draw-axis-line(vstart, vend, axis, is-horizontal, style) - place-ticks-on-line(ticks, data-start, data-end, style, flip: flip) - }) - - if axis.label != none { - let (content-anchor, offset-dir) = label-pos.at(name) - - let angle = if style.label.angle not in (none, auto) { - style.label.angle - } else { 0deg } - if style.label.anchor not in (none, auto) { - content-anchor = style.label.anchor - } - - let offset = vector.scale(offset-dir, style.label.offset) - content((rel: offset, to: vend), - [#axis.label], - angle: angle, - anchor: content-anchor) - } - } - - if shared-zero { - let pt = (rel: (-style.tick.label.offset, -style.tick.label.offset), - to: (y-x, x-y)) - let zero = if type(style.shared-zero) == std.content { - style.shared-zero - } else { - $0$ - } - content(pt, zero, anchor: "north-east") - } - }) - }) - }) -}