AnyChart
Mojo struct 🡭
AnyChart
@memory_only
struct AnyChartA chart whose mark type has been erased, so charts of different marks can share a List for layers, facets and figures. It keeps everything a chart shares by value, copies of the shared channel structs the composition code reads (empty for a mark without them), the mark id, and the mark itself behind a pointer with the functions that draw, copy and free it, specialized when the chart is erased. Only the marks a program erases get instantiated.
Erasing has to name the mark’s render for every draw target it
may meet, since the mark type is gone by the time a target is
chosen, and each of those is a full render pipeline to compile:
the three a render_layers() program never uses cost it about
17 CPU-s (#838). AnyChart(chart) and erased() bind all four,
which a List[AnyChart] needs because any entry may consume it.
bound[...]() and erased_with[...]() bind only the targets named
and leave the other slots on a stub that raises, for the variadic
entries that erase inside a call that knows which targets it draws
marks into and never hand the chart on. Layers draw every mark
through their own layer renderers and bind none; facets draw each
cell through the slot for their output target. (render_grid()’s
align_axes probe draws through the Canvas slot whatever the
output, which is fine: it takes a List[AnyChart].)
record() draws into a DisplayList, a fifth slot that no entry
taking a List[AnyChart] uses, so erased() leaves it unbound and
a chart that will be recorded after erasing asks for it with
erased_with[display_list=True]() (#862). A target type with no
slot of its own is a compile error in render_mark, never a draw
through another target’s slot.
mark[M]() gives the mark back to code that has checked id.
Fields
- mark_id (
Mark) - caps (
_Capabilities) - settings (
_ChartSettings) - style (
_MarkStyle) - annotations (
_AnnotationData) - width (
Int) - height (
Int) - continuous (
_ContinuousData) - categorical (
_CategoricalData) - channels (
_ChannelData) - y_err (
_ErrorBarData) - distribution (
_DistributionData)
Implemented traits
AnyType, ChartLike, Copyable, Deinitable, Movable
Methods
__init__
fn def __init__[M: MarkType](out self, chart: Chart[M])Parameters:
- M (
MarkType)
Args:
- chart (
Chart[M]) - self (
Self)
Returns:
Self
fn def __init__[M: MarkType](out self, chart: Chart[M], *, canvas: def(Int, mut Canvas, _MarkStyle, _AnnotationData, _ChartSettings, Int, Int, Int, Int, Bool, Float64, Float64, Bool, mut FontCache, Bool) raises thin -> _RenderResult, svg: def(Int, mut SvgCanvas, _MarkStyle, _AnnotationData, _ChartSettings, Int, Int, Int, Int, Bool, Float64, Float64, Bool, mut FontCache, Bool) raises thin -> _RenderResult, pdf: def(Int, mut PdfCanvas, _MarkStyle, _AnnotationData, _ChartSettings, Int, Int, Int, Int, Bool, Float64, Float64, Bool, mut FontCache, Bool) raises thin -> _RenderResult, bounds: def(Int, mut BoundsTarget, _MarkStyle, _AnnotationData, _ChartSettings, Int, Int, Int, Int, Bool, Float64, Float64, Bool, mut FontCache, Bool) raises thin -> _RenderResult, display_list: def(Int, mut DisplayList, _MarkStyle, _AnnotationData, _ChartSettings, Int, Int, Int, Int, Bool, Float64, Float64, Bool, mut FontCache, Bool) raises thin -> _RenderResult)Parameters:
- M (
MarkType)
Args:
- chart (
Chart[M]) - canvas (
def(Int, mut Canvas, _MarkStyle, _AnnotationData, _ChartSettings, Int, Int, Int, Int, Bool, Float64, Float64, Bool, mut FontCache, Bool) raises thin -> _RenderResult) - svg (
def(Int, mut SvgCanvas, _MarkStyle, _AnnotationData, _ChartSettings, Int, Int, Int, Int, Bool, Float64, Float64, Bool, mut FontCache, Bool) raises thin -> _RenderResult) - pdf (
def(Int, mut PdfCanvas, _MarkStyle, _AnnotationData, _ChartSettings, Int, Int, Int, Int, Bool, Float64, Float64, Bool, mut FontCache, Bool) raises thin -> _RenderResult) - bounds (
def(Int, mut BoundsTarget, _MarkStyle, _AnnotationData, _ChartSettings, Int, Int, Int, Int, Bool, Float64, Float64, Bool, mut FontCache, Bool) raises thin -> _RenderResult) - display_list (
def(Int, mut DisplayList, _MarkStyle, _AnnotationData, _ChartSettings, Int, Int, Int, Int, Bool, Float64, Float64, Bool, mut FontCache, Bool) raises thin -> _RenderResult) - self (
Self)
Returns:
Self
fn def __init__(out self, *, copy: Self)Args:
- copy (
Self) - self (
Self)
Returns:
Self
__deinit__
fn def __deinit__(deinit self)Args:
- self (
Self)
bound
@staticmethod
fn def bound[canvas: Bool, svg: Bool, pdf: Bool, bounds: Bool, display_list: Bool, M: MarkType](chart: Chart[M]) -> Selfchart erased with only the named targets’ renders bound; see the struct docstring. The other slots raise if drawn into.
Parameters:
- canvas (
Bool) - svg (
Bool) - pdf (
Bool) - bounds (
Bool) - display_list (
Bool) - M (
MarkType)
Args:
- chart (
Chart[M])
Returns:
Self
mark
fn def mark[M: MarkType](self) -> ref[MutUnsafeAnyOrigin] MThe mark, for code that has checked id; raises otherwise rather than reinterpreting one mark’s memory as another’s.
Parameters:
- M (
MarkType)
Args:
- self (
Self)
Returns:
ref[MutUnsafeAnyOrigin] M
Raises:
id
fn def id(self) -> MarkArgs:
- self (
Self)
Returns:
Mark
canvas_width
fn def canvas_width(self) -> IntArgs:
- self (
Self)
Returns:
Int
canvas_height
fn def canvas_height(self) -> IntArgs:
- self (
Self)
Returns:
Int
chart_settings
fn def chart_settings(self) -> _ChartSettingsArgs:
- self (
Self)
Returns:
_ChartSettings
mark_style
fn def mark_style(self) -> _MarkStyleArgs:
- self (
Self)
Returns:
_MarkStyle
chart_annotations
fn def chart_annotations(self) -> _AnnotationDataArgs:
- self (
Self)
Returns:
_AnnotationData
scaled_by
fn def scaled_by(self, factor: Float64) -> SelfArgs:
- self (
Self) - factor (
Float64)
Returns:
Self
size
fn def size(var self, width: Int, height: Int) -> SelfSet the dimensions render()/render_svg()/save() construct their target at. Defaults to 640x420.
The unit is a point, 1/72 inch, which is what makes a figure
size mean something physical (#372). On the raster backends one
point is one pixel at the default resolution, so nothing about
the old reading changes; save(..., dpi=300) keeps the physical
size and multiplies the pixels. In a PDF it is a point on the
page. size_inches()/size_mm() say the same thing in the
units a page is usually specified in.
Args:
- self (
Self) - width (
Int): Figure width in points. - height (
Int): Figure height in points.
Returns:
Self: Self, for further chaining.
size_inches
fn def size_inches(var self, width: Float64, height: Float64) -> Selfsize() in inches: 72 points to the inch, rounded to whole points (#372).
A figure for a journal column or a slide is specified
physically, and size(468, 312) does not read as “6.5 by 4.3
inches” to anyone.
Args:
- self (
Self) - width (
Float64): Figure width in inches. - height (
Float64): Figure height in inches.
Returns:
Self: Self, for further chaining.
size_mm
fn def size_mm(var self, width: Float64, height: Float64) -> Selfsize() in millimeters: 25.4 mm to the inch and 72 points to the inch, rounded to whole points (#372).
Args:
- self (
Self) - width (
Float64): Figure width in millimeters. - height (
Float64): Figure height in millimeters.
Returns:
Self: Self, for further chaining.
theme
fn def theme(var self, t: Theme) -> SelfAttach a full Theme to this plot, replacing the default one. Every styling knob lives on Theme.
Args:
- self (
Self) - t (
Theme): TheThemeto attach, replacing whatever was set before (the defaultTheme()if this is the first call).
Returns:
Self: Self, for further chaining.
tooltips
fn def tooltips(var self, policy: Tooltips) -> SelfSet whether this chart’s data get SVG hover tooltips, overriding Theme.tooltips for this plot only. Tooltips.ON titles every datum, Tooltips.OFF none, and Tooltips.AUTO titles them when there are at most Theme.auto_tooltip_limit (tooltips.mojo). Precedence is this call, then the theme.
Tooltips.ON on a mark without tooltips raises when the chart
renders; which marks have them is
MarkType.supports_tooltips.
Args:
- self (
Self) - policy (
Tooltips):Tooltips.ON,Tooltips.OFForTooltips.AUTO.
Returns:
Self: Self, for further chaining.
labels
fn def labels(var self, title: String = "", subtitle: String = "", x_title: String = "", y_title: String = "", description: String = "") -> SelfSet the chart title/subtitle and/or axis titles. Named x_title/ y_title rather than x/y so a call next to .encode(x=..., y=...) never reads as setting data.
Each is independent and defaults to "" (not set); layout space is
reserved only for the non-empty visible ones (title/subtitle/
x_title/y_title – description draws nothing). subtitle
draws directly beneath title, smaller and in Theme.subtitle_color;
with no title it draws at the top position title would have used.
x_title/y_title caption whatever is drawn along the bottom/left
edge, whichever axis that is for the mark’s orientation. Mark.ARC
has no axes, so x_title/y_title raise at render() time there;
only title/subtitle/description apply.
description is an SVG <desc> – longer, screen-reader-only
context subtitle alone can’t carry since it’s drawn on the chart
itself. save()/save_layers()/save_facets() write it (falling
back to subtitle when empty) automatically whenever title is set;
see accessible_svg_string()’s own docstring for the full markup.
Any of the four drawn labels may hold mathematics between $
signs: "$\sigma^2$", "Rate $\frac{\Delta y}{\Delta x}$".
Inside the dollars, Latin letters and lowercase Greek are italic
variables and everything else upright; ^ and _ attach
scripts, \frac{}{} stacks a fraction, \sqrt{} draws a
radical, \mathrm{} forces upright text, and
\alpha…\Omega and operators such as \times, \leq
and \sum name symbols – the full list is in
dataviz/core/mathtext.mojo. A lone $ is a price and stays
plain; write \$ for a literal one beside math. The space an
expression needs above and below the line is measured and
reserved. A label that does not parse raises at render time,
naming the label and the character, rather than drawing a
guess.
Args:
- self (
Self) - title (
String): The chart’s title. Left empty (the default), reserves no layout space for it. - subtitle (
String): A secondary line shown under the title, independent of whethertitleis also set. - x_title (
String): Caption for whatever’s drawn along the bottom edge; raises at render() time onMark.ARC. - y_title (
String): Caption for whatever’s drawn along the left edge; raises at render() time onMark.ARC. - description (
String): Optional longer SVG<desc>text, drawn nowhere on the chart itself; falls back tosubtitlewhen left empty.
Returns:
Self: Self, for further chaining.
series_name
fn def series_name(var self, name: String) -> SelfName this layer for render_layers()’s/render_layers_svg()’s per-layer legend: a swatch (this layer’s own Theme.mark_color) plus name, one row per named layer, drawn before any per-point color/color_categories legend a Mark. POINT layer has of its own. A Plot.secondary_axis() layer’s row gets " (right axis)" appended, so the reader knows which axis it reads against.
render_layers()/render_layers_svg() and
_render_bar_combo_layers (the Mark.BAR-combo path) only;
render()/render_svg() ignore it, since a standalone plot has
only one series, nothing for a legend entry to distinguish.
Layers with no name draw no row.
See the Cookbook’s own “Layer Legend” recipe (docs/src/ cookbook_recipes/layer_legend.mojo) for a full worked example.
Args:
- self (
Self) - name (
String): This layer’s label in the per-layer legend. Left empty (the default, via not calling this), the layer draws no legend row.
Returns:
Self: Self, for further chaining.
annotate_line
fn def annotate_line(var self, value: Float64, label: String = "") -> SelfAdd a horizontal reference line at value on the y-axis (ECharts’ markLine with a fixed value; no auto-computed average/max/min modes). Each call adds a line. label, when non-empty, draws to the right of the line in Theme.annotation_color; the line spans the full plot width, solid (canvas has no dashed-stroke primitive). A value outside the mark’s padded y-domain draws nothing.
Only meaningful on a mark whose y-axis is a continuous
LinearScale, checked at render() time via
_RenderResult.has_y_scale: Mark.POINT/LINE/AREA/
EFFECT_SCATTER and every mark sharing _CategoricalFrame (BAR/
LOLLIPOP/WATERFALL/BOX/CANDLESTICK/BULLET/GROUPED_BAR/
STACKED_BAR/STREAMGRAPH). Other marks raise. Also wired into
render_facets() (per cell) and render_layers() (per layer,
against that layer’s own primary or secondary y-scale).
See the Cookbook’s own “Reference Line” recipe (docs/src/ cookbook_recipes/annotate_line.mojo) for a full worked example.
Args:
- self (
Self) - value (
Float64): The y-value to draw the line at. Outside the mark’s (padded) y-domain, draws nothing. - label (
String): Drawn to the right of the line when non-empty; left empty (the default), the line draws with no label.
Returns:
Self: Self, for further chaining – render()/render_svg()
raise later if the mark has no genuine continuous y-axis.
annotate_area
fn def annotate_area(var self, y0: Float64, y1: Float64, label: String = "") -> SelfAdd a shaded horizontal band from y0 to y1 on the y-axis (ECharts’ markArea with a fixed pair). Each call adds a band. label, when non-empty, draws inside the band near its top edge in Theme.annotation_color. y0/y1 may be given in either order.
A band partially overlapping the mark’s padded y-domain clips to the
visible portion; one with no overlap draws nothing. Drawn on top of
the mark at Theme.annotation_area_color’s partial opacity, so the
mark’s ink shows through.
Same mark support and facets/layers wiring as annotate_line().
See the Cookbook’s own “Reference Band” recipe (docs/src/ cookbook_recipes/annotate_area.mojo) for a full worked example.
Args:
- self (
Self) - y0 (
Float64): One edge of the band; need not be the lower one. - y1 (
Float64): The other edge of the band; whichever ofy0/y1is smaller becomes the band’s bottom edge. - label (
String): Drawn inside the band near its top edge when non-empty; left empty (the default), the band draws with no label.
Returns:
Self: Self, for further chaining – render()/render_svg()
raise later if the mark has no genuine continuous y-axis.
annotate_vline
fn def annotate_vline(var self, value: Float64, label: String = "") -> SelfAdd a vertical reference line at value on the x-axis: annotate_line()’s mirror image, with the same fixed-value scope, additive behavior, styling, and out-of-domain skip.
Narrower mark support: only Mark.POINT/LINE/AREA/
EFFECT_SCATTER, the marks with a continuous x-axis; a categorical
x-axis has no numeric value to place a line against (see
_RenderResult). Raises on an unsupported mark. Also wired into
render_facets() (per cell) and render_layers() (per layer,
against the one shared continuous x-scale every layer uses –
raises on a Mark.BAR combo chart’s categorical x-axis instead,
same as a standalone unsupported mark).
See the Cookbook’s own “Vertical Reference Line” recipe (docs/ docs/cookbook_recipes/annotate_vline.mojo) for a full worked example.
Args:
- self (
Self) - value (
Float64): The x-value to draw the line at. Outside the mark’s (padded) x-domain, draws nothing. - label (
String): Drawn near the line when non-empty; left empty (the default), the line draws with no label.
Returns:
Self: Self, for further chaining – render()/render_svg()
raise later if the mark has no genuine continuous x-axis.
annotate_point
fn def annotate_point(var self, x: Float64, y: Float64, label: String = "") -> SelfAdd a single labeled point at (x, y) (ECharts’ markPoint with a fixed coordinate): a small filled marker in Theme.annotation_color, with label just above it when non-empty. Each call adds a point.
Needs a continuous coordinate on both axes, so only Mark.POINT/
LINE/AREA/EFFECT_SCATTER support it; raises otherwise. A point
outside the padded domain on either axis draws nothing. Also wired
into render_facets() (per cell) and render_layers() (per layer,
against that layer’s own primary or secondary y-scale and the one
shared x-scale).
See the Cookbook’s own “Point Marker” recipe (docs/src/ cookbook_recipes/annotate_point.mojo) for a full worked example.
Args:
- self (
Self) - x (
Float64): The point’s x-coordinate. Outside the mark’s (padded) x-domain, draws nothing. - y (
Float64): The point’s y-coordinate. Outside the mark’s (padded) y-domain, draws nothing. - label (
String): Drawn just above the point when non-empty; left empty (the default), the point draws with no label.
Returns:
Self: Self, for further chaining – render()/render_svg()
raise later if the mark has no genuine continuous x/y-axis.
annotate_arrow
fn def annotate_arrow(var self, x: Float64, y: Float64, text: String, text_x: Float64, text_y: Float64) -> SelfPoint at (x, y) with an arrow, labeled text placed at (text_x, text_y). Each call adds an arrow.
This is the only annotation that can be placed in empty space,
which is what makes it usable on a crowded chart where every
other overlay lands on data – annotate_point()’s label sits a
fixed gap above its marker and cannot be moved. An arrow is
often the whole point of a chart going into a document: it is
what turns a plot into an argument.
Both ends are in data coordinates. Everything else in this API is in data space, and a second convention would need explaining every time it appeared. The cost is that a label position has to be chosen against the data’s own range; the benefit is that it stays put when the chart is resized.
Straight arrows only. Curved connectors, head styles and shrink factors are refinements on top of a feature that did not exist; the straight case carries most of the value.
Needs a continuous coordinate on both axes, so only Mark.POINT/
LINE/AREA/EFFECT_SCATTER support it; raises otherwise. An
arrow with either end outside the padded domain is skipped
whole rather than clipped – half an arrow points at nothing.
Args:
- self (
Self) - x (
Float64): The target’s x-coordinate, where the head lands. - y (
Float64): The target’s y-coordinate. - text (
String): The label, drawn centered at(text_x, text_y). Empty draws the arrow alone. - text_x (
Float64): The label’s x-coordinate, in data space. - text_y (
Float64): The label’s y-coordinate, in data space.
Returns:
Self: Self, for further chaining – render()/render_svg()
raise later if the mark has no genuine continuous x/y-axis.
annotate_band
fn def annotate_band(var self, x: List[Float64], y_lower: List[Float64], y_upper: List[Float64], label: String = "") -> SelfShade the region between two curves that vary with x: a confidence band around a trend line, or a min/max envelope. annotate_area()’s band is a constant (y0, y1) pair; this takes two parallel lists keyed by x. Each call adds a band.
x/y_lower/y_upper must be the same length and every
y_upper[i] >= y_lower[i], both checked at render() time. x need
not be sorted; the top edge traces (x[i], y_upper[i]) in order and
the bottom edge walks back in reverse.
Filled in Theme.annotation_area_color with straight edges (no
Theme.line_smoothing). label, when non-empty, centers above the
band’s middle x-index on its upper edge.
Only Mark.POINT/LINE/AREA/EFFECT_SCATTER, as for
annotate_point(). A band is clipped to the overlapping range on
both axes; one with no overlap draws nothing. Same facets/layers
wiring as annotate_point().
See the Cookbook’s own “Confidence Band” recipe (docs/src/ cookbook_recipes/annotate_band.mojo) for a full worked example.
Args:
- self (
Self) - x (
List[Float64]): The band’s x column, one entry per (y_lower,y_upper) pair, in the order the edges should trace. - y_lower (
List[Float64]): The band’s bottom edge, one entry perx. - y_upper (
List[Float64]): The band’s top edge, one entry perx; every value must be>= y_lower’s value at that same index. - label (
String): Drawn centered above the band’s middle point when non-empty; left empty (the default), the band draws with no label.
Returns:
Self: Self, for further chaining – render()/render_svg()
raise later if the lengths mismatch, an upper/lower value
is inverted, or the mark has no genuine continuous x/y-axis.
annotate_best_fit
fn def annotate_best_fit(var self, show_equation: Bool = False, show_r_squared: Bool = False, label: String = "", ci: Float64 = 0.94999999999999996) -> SelfOverlay an ordinary-least-squares best-fit line computed from this plot’s own _continuous.x/_continuous.y at render() time, so it works whether called before or after .encode(). Not additive: the last call wins, since the fit is determined by the data.
Drawn as a solid line in Theme.annotation_color across the mark’s
full padded x-domain. show_equation/show_r_squared each add one
line of text right-aligned near the plot’s top-right corner; label,
when non-empty, draws as a heading above them. R-squared is
1 - SS_res/SS_tot, defined as 1.0 when SS_tot is 0.0.
Only Mark.POINT/LINE/AREA/EFFECT_SCATTER. Raises at render()
time with fewer than 2 points or when every x value is identical.
Also wired into render_facets() (per cell, fit to that cell’s own
data) and render_layers() (per layer, fit to that layer’s own
data against its primary or secondary y-scale and the shared
x-scale).
See the Cookbook’s own “Best-Fit Trend Line” recipe (docs/src/ cookbook_recipes/best_fit_line.mojo) for a full worked example.
Args:
- self (
Self) - show_equation (
Bool): Draw the fitted line’s owny = mx + btext whenTrue;False(the default) draws only the line itself. - show_r_squared (
Bool): Draw the fit’s R-squared text whenTrue;False(the default) omits it. - label (
String): An optional heading drawn above the equation/ R-squared text; left empty (the default), no heading draws (independent ofshow_equation/show_r_ squared– alabelwith both leftFalsestill draws only the line, no text at all). - ci (
Float64): Two-sided confidence level for a band around the fitted line.0.95(the default) shades the 95% confidence interval of the fitted mean at each x; pass0.0for the bare line. The band is narrowest at the mean ofxand flares toward the ends, which is the point of drawing it: a line without one invites the reader to trust the slope more than the data supports (#352). Levels carried: 0.90, 0.95, 0.99. A band needs three points – with two, the line passes through both and there is no residual error to size it from – so with fewer the line draws alone. Drawn inTheme.annotation_area_color, under the line.
Returns:
Self
annotate_smooth
fn def annotate_smooth(var self, method: SmoothMethod = SmoothMethod.LOESS, span: Float64 = 0.75, degree: Int = Int(2)) -> SelfOverlay a curved trend line fitted to this plot’s own x/y data (#147) – annotate_best_fit()’s straight line, for data a straight line does not describe.
SmoothMethod.LOESS(the default): locally weighted regression. At each x, a polynomial ofdegree(1 or 2) is fitted to the nearestspanshare of the points, weighted by the tricube of their distance, and evaluated there.spansets how smooth: smaller follows the data more closely. The defaults,span=0.75anddegree=2, are the conventional ones for local regression.SmoothMethod.POLYNOMIAL: one least-squares polynomial ofdegreeover every point;spanis unused.
Drawn across the data’s own x range, in annotate_best_fit()’s
color and line style, clipped to the plot area. Unlike
annotate_best_fit() there is no confidence band, equation or
R-squared: those are defined for the straight-line fit.
Args:
- self (
Self) - method (
SmoothMethod):SmoothMethod.LOESSorSmoothMethod.POLYNOMIAL. - span (
Float64): LOESS’s window as a share of the points, in(0, 1]. - degree (
Int): LOESS’s local degree (1 or 2), or the polynomial’s.
Returns:
Self: Self, for further chaining – render()/render_svg() raise
later if the mark has no continuous x/y axes, or the data
cannot support the fit (too few points or distinct x values
for the degree, or a span outside (0, 1]).
scale_y_log
fn def scale_y_log(var self) -> SelfScale the y-axis logarithmically (base 10). Every y value, and every y-axis annotation value, must be strictly positive; render()/ render_svg() raise otherwise (see _log_data_extent()).
Mark.POINT/LINE/EFFECT_SCATTER only, and standalone render()/
render_svg() only: a categorical mark has no continuous y-domain,
Mark.AREA’s y-domain is forced through zero (which has no
logarithm), and render_layers() combines layers into one linear
scale. Every tick/gridline/point/annotation still goes through
LinearScale.to_pixel() with real-unit values; see that method.
See the Cookbook’s own “Log Scale (Y-Axis)” recipe (docs/src/ cookbook_recipes/log_scale_y.mojo) for a full worked example.
Args:
- self (
Self)
Returns:
Self: Self, for further chaining.
scale_x_log
fn def scale_x_log(var self) -> Selfscale_y_log()’s x-axis mirror. Mark.POINT/LINE/AREA/ EFFECT_SCATTER (x is never forced through zero, so AREA is allowed here), standalone render()/render_svg() only.
See the Cookbook’s own “Log Scale (X-Axis)” recipe (docs/src/ cookbook_recipes/log_scale_x.mojo) for a full worked example.
Args:
- self (
Self)
Returns:
Self: Self, for further chaining.
scale_y_symlog
fn def scale_y_symlog(var self, linthresh: Float64 = 1) -> SelfScale the y-axis symmetrically logarithmically: linear within [-linthresh, linthresh], logarithmic beyond it, continuous where they meet (#368).
What scale_y_log() cannot do. A log axis needs strictly
positive values, so a series that crosses zero – a temperature
anomaly, a profit and loss, a residual – cannot go on one at
all; and a linear axis collapses every small value against the
largest. Symlog keeps zero, keeps the sign, and still resolves
several orders of magnitude on each side.
linthresh is the half-width of the linear region in data
units, and must be positive. It sets what counts as “near
zero”: values inside it are laid out linearly, so the noise
floor of the measurement is usually the right choice. The
linear region takes exactly as much axis as one decade of the
logarithmic region, which is the only ratio that makes the two
halves comparable without a second knob.
Mark.POINT/LINE/AREA/EFFECT_SCATTER, standalone
render()/render_svg() only, the same scope
scale_y_log() has. Mutually exclusive with scale_y_log();
setting both raises at render time rather than silently
preferring one.
Args:
- self (
Self) - linthresh (
Float64): Half-width of the linear region around zero, in data units. Must be positive.
Returns:
Self: Self, for further chaining.
scale_x_symlog
fn def scale_x_symlog(var self, linthresh: Float64 = 1) -> Selfscale_y_symlog()’s x-axis mirror, with the same scope and the same linthresh meaning (#368).
Args:
- self (
Self) - linthresh (
Float64): Half-width of the linear region around zero, in data units. Must be positive.
Returns:
Self: Self, for further chaining.
scale_x_domain
fn def scale_x_domain(var self, min: Float64, max: Float64) -> SelfPin the x-axis domain to the given minimum and maximum, replacing _data_extent()’s 5%-padded domain (or _log_data_extent()’s when scale_x_log() is also set). For comparable charts across runs, a fixed reference range, or a zoomed-in view, without padding the data with fake points.
Mark.POINT/LINE/AREA/EFFECT_SCATTER only (the same marks
_render_generic’s continuous path draws), standalone render()/
render_svg() and render_facets() only – render_layers()
raises if any layer sets this, since a shared axis across several
layers needs one shared answer, not one per layer; the categorical
marks (Mark.BAR, LOLLIPOP, …) raise too, left for a
supported. render_facets() applies this per cell, so every cell
sharing the same override reads as one shared domain – the
facets counterpart to shared_y_scale=True for the x-axis (which
has no shared_y_scale equivalent otherwise).
A point outside the domain still computes a real (off-plot)
pixel position via LinearScale.to_pixel(), same as it would if
it merely fell outside a padded auto-computed domain; the SVG
viewBox’s own default overflow: hidden clips it at the canvas
edge, and the raster Canvas’s own pixel buffer bounds-checks
every draw call, so nothing paints outside the plot in either
backend.
Args:
- self (
Self) - min (
Float64): The domain’s lower bound. For a log x-axis (scale_x_log()), must be> 0. - max (
Float64): The domain’s upper bound; must be> min.
Returns:
Self: Self, for further chaining – render()/render_svg()
raise later if min >= max, the mark doesn’t support this,
or (with scale_x_log()) min <= 0.
scale_y_domain
fn def scale_y_domain(var self, min: Float64, max: Float64) -> Selfscale_x_domain()’s y-axis mirror – see that method’s own docstring for the shared rules. Overrides _zero_baseline_y_ extent()’s forced-zero domain on Mark.AREA too: an explicit The explicit domain is used exactly, zero baseline or not.
Args:
- self (
Self) - min (
Float64): The domain’s lower bound. For a log y-axis (scale_y_log()), must be> 0. - max (
Float64): The domain’s upper bound; must be> min.
Returns:
Self: Self, for further chaining – render()/render_svg()
raise later if min >= max, the mark doesn’t support this,
or (with scale_y_log()) min <= 0.
scale_x_ticks
fn def scale_x_ticks(var self, values: List[Float64], labels: List[String] = List()) -> SelfPut the x-axis major ticks exactly at values, instead of at the 1-2-5 positions the axis would choose (#368).
For an axis whose meaningful positions are not round numbers: a threshold, a target, the two dates a study ran between. The gridline, the tick mark and the label all move together, because they are one tick.
labels replaces the formatted numbers when given, one per
position, which is how an axis reads Q1 Q2 Q3 Q4 over values
that are really 1 2 3 4. Left empty, the positions are
formatted the way computed ticks are, at one decimal count for
the whole set.
A position outside the axis domain is dropped rather than drawn:
its pixel would fall outside the plot rect and its label would
print in the margin beside nothing. scale_x_domain() is what
moves the domain; this only says where the ticks go inside it.
Minor ticks are not derived from an explicit set, since the caller said where the ticks belong and a subdivision of an irregular set has no meaning.
Args:
- self (
Self) - values (
List[Float64]): Tick positions, in the axis’s own units. - labels (
List[String]): One label per position, or empty to format the positions.
Returns:
Self: Self, for further chaining – render()/render_svg()
raise later if the list is empty, a label count does not
match, a position is not finite, a position is not positive
on a log axis, or the mark does not support this.
scale_y_ticks
fn def scale_y_ticks(var self, values: List[Float64], labels: List[String] = List()) -> Selfscale_x_ticks()’s y-axis mirror – see that method’s docstring for the shared rules.
The y labels are what the left margin is measured from, so an explicit set widens or narrows the plot rect to fit itself, exactly as computed labels do.
Args:
- self (
Self) - values (
List[Float64]): Tick positions, in the axis’s own units. - labels (
List[String]): One label per position, or empty to format the positions.
Returns:
Self: Self, for further chaining – see scale_x_ticks().
scale_x_reverse
fn def scale_x_reverse(var self) -> SelfRun the x-axis right to left: the domain’s low end lands on the plot rect’s right edge (#368).
For a quantity that reads better descending – a rank where 1 is best, a countdown, a depth below a surface. Only the two pixel positions swap. The domain stays ascending, so the ticks are the same ticks in the same order and every mark keeps handing the scale the same values; what changes is where they land.
Args:
- self (
Self)
Returns:
Self: Self, for further chaining – render()/render_svg()
raise later if the mark does not support this.
scale_y_reverse
fn def scale_y_reverse(var self) -> SelfRun the y-axis top to bottom: the domain’s low end lands on the plot rect’s top edge (#368).
scale_x_reverse()’s mirror. On Mark.IMSHOW, whose y-axis
already counts downward so that row 0 is at the top, this
composes rather than competes: it puts row 0 back at the bottom.
Args:
- self (
Self)
Returns:
Self: Self, for further chaining – see scale_x_reverse().
equal_aspect
fn def equal_aspect(var self) -> SelfGive one data unit the same pixel length on both axes (#368).
For anything whose two axes are the same kind of quantity, where the shape of what is drawn is part of what it says: a map, a circle that has to look round, a residual plot read against the 45-degree line.
The plot rect shrinks; the domains do not grow. With equal aspect something has to give, and the two choices are showing a wider range than the caller asked for or leaving part of the figure empty. This leaves space: the rect keeps the aspect the data implies and centers itself in the room it had. Nothing is drawn outside the data’s own range, and the axis labels, ticks and margins are the ones measured for the domains as given.
Not available on a log or time axis, where a “data unit” is not
a constant length: raises at render() time rather than
claiming a guarantee it cannot keep.
Args:
- self (
Self)
Returns:
Self: Self, for further chaining – render()/render_svg()
raise later on a log or time axis, or if the mark does not
support this.
scale_color_domain
fn def scale_color_domain(var self, min: Float64, max: Float64) -> SelfPin the continuous color domain to the given minimum and maximum, replacing the [min, max] this mark would otherwise take from its own colored values. scale_x_domain()’s color counterpart, and the thing that makes two color-encoded charts comparable.
Without it every continuous-color mark derives its own limits, so
two panels of the same quantity are drawn against two different
scales and look identical while meaning different things – the
reader has no way to see the difference, because the only place
it shows is in two legends whose numbers nobody cross-checks.
shared_color_domain() computes one domain across several
charts’ data to pass here; it is the counterpart to
shared_bin_edges().
Applies to every mark that colors by a continuous value:
Mark.HEATMAP, CALENDAR_HEATMAP, CORRPLOT, IMSHOW,
PCOLORMESH, HIST2D, HEXBIN, CONTOUR, CONTOURF,
TRICONTOUR, TRICONTOURF, TRIPCOLOR, QUIVER,
STREAMPLOT, and Plot.encode(color=...)’s continuous channel
on Mark.POINT/SINGLE_AXIS/EFFECT_SCATTER. Anything else
raises rather than accepting a setting it would ignore.
Mark.BOXENPLOT is not on the list even though it builds a
ColorScale: its ramp runs over the depth of the letter-value
nest, not over a data value, so an explicit data domain has
nothing to say about it.
Values outside the domain are not dropped; they clamp to the
ramp’s end color, because GradientStops.color_at() clamps t
outside [0, 1]. A separate under/over color would say more,
and is left for its own change (see the module note on
_color_scale_for).
The color legend follows automatically: every mark hands the
legend the same ColorScale it colored with, so the labels read
the overridden domain, not the data’s.
Args:
- self (
Self) - min (
Float64): The value the ramp’s low end means. - max (
Float64): The value the ramp’s high end means; must be> min.
Returns:
Self: Self, for further chaining – the render raises later if
min >= max or the mark has no continuous color channel.
scale_color_thresholds
fn def scale_color_thresholds(var self, boundaries: List[Float64]) -> SelfColor in discrete bands instead of a continuous ramp (#370).
n boundaries make n - 1 bands, and every value in a band
gets one flat color. That is what a reader needs when the
question is “which category is this” rather than “how much”:
soil pH bands, risk tiers, a legend with named ranges.
Intervals are lower-inclusive, [b[i], b[i+1]), with the
last closed at the top so the domain maximum has somewhere to
go. A value exactly on an interior boundary belongs to the band
above it. That is the one place this is easy to get wrong, so it
is stated here and
tested.
Values below the first boundary take the lowest band and values above the last take the highest, rather than raising. Clipping rather than rejecting keeps a shared threshold list usable across facets whose data ranges differ.
Boundaries must be strictly increasing, checked at render time along with the rest of the color domain.
Not combinable with scale_color_log() or
scale_color_center(): thresholds already say where every band
starts, so there is nothing left for either to place.
Args:
- self (
Self) - boundaries (
List[Float64]): The band edges, at least two, strictly increasing.
Returns:
Self: Self, for further chaining.
Raises:
scale_color_under
fn def scale_color_under(var self, color: Color) -> SelfColor values below the color domain with color instead of the ramp’s low end (#370).
Without it an out-of-range value clamps: a reading of -40 on a domain starting at 0 is painted the same as a reading of 0, and the chart says the two are alike. A distinct color says “this is off the scale”, which is a different statement and usually the one that matters – a sensor out of range, a region with no data of its own, a value the domain was deliberately narrowed to exclude.
Applies to every mark scale_color_domain() applies to, and to
every form of the ramp: continuous, logarithmic and banded. With
scale_color_thresholds() the band edges are the range, so
“below” means below the first boundary.
The color legend shows it, as a block at the low end of the bar inside the bar’s own footprint, so the legend costs exactly the room it did before and its end labels stay attached to the ends of the ramp, where those numbers are true.
Args:
- self (
Self) - color (
Color): The color for a value below the domain.
Returns:
Self: Self, for further chaining.
scale_color_over
fn def scale_color_over(var self, color: Color) -> SelfColor values above the color domain with color instead of the ramp’s high end – scale_color_under()’s mirror, and see that method’s docstring for the shared rules (#370).
Values exactly at the domain maximum belong to the ramp, not to the over color: the top end is part of the range, which is the rule the last threshold band already follows.
Args:
- self (
Self) - color (
Color): The color for a value above the domain.
Returns:
Self: Self, for further chaining.
scale_color_log
fn def scale_color_log(var self) -> SelfNormalize color by log10 instead of linearly (#370).
A linear ramp cannot resolve values spread over several orders of magnitude: on a 1-to-10,000 domain everything below 1,000 lands in the first tenth of the ramp and reads as one color. With this, equal ratios get equal color distance, so 1 to 10 spans as much of the ramp as 1,000 to 10,000.
The domain must be strictly positive, whether it came from the
data or from scale_color_domain(). That is checked at render
time, because until then the mark’s own limits are not known.
Not combinable with scale_color_center(): centering places the
neutral color by linear distance from each end, which a log
domain does not preserve.
Args:
- self (
Self)
Returns:
Self: Self, for further chaining.
Raises:
scale_color_center
fn def scale_color_center(var self, center: Float64) -> SelfPin the middle of the color ramp to center, so a diverging ramp’s neutral color lands on a value that means something – zero, a baseline, a target – instead of on the numeric midpoint of whatever the data happened to span.
A diverging ramp’s whole claim is that its middle is neutral and its two ends are opposite. Placed at the data’s midpoint that claim is simply false: over values from -2 to +10 the neutral color sits at +4, so half the positive range is painted in the color that is supposed to mean “negative”. Centering fixes the mapping rather than the data, and the two arms are then free to be different sizes – which is the honest picture when the data is not symmetric.
Separate from scale_color_domain() on purpose. Centering is
about where the ramp’s middle goes, not about what its ends
mean, and the two are wanted independently: a center on its own
re-places the middle inside the data’s own limits, which is the
common case, while a domain on its own leaves the ramp
symmetric. Folding both into one call would have forced every
caller who wants a centered ramp to also state limits they had
no opinion about.
center must lie strictly inside the resolved color domain. It
is not quietly widened to fit: widening would move the ramp’s
ends, so a chart asking only “center this at zero” would get
different end colors than the one beside it, which is the silent
disagreement the whole feature exists to remove. The render
raises and names the domain instead, so the fix is an explicit
scale_color_domain().
This is reached by moving the ramp’s stops instead of bending
the value projection; see
ColorScale.from_theme_centered() for why that route is the one
that keeps the legend honest.
Defined for any ramp, not only a three-stop diverging one – it
means “the color at offset 0.5 lands on center” whatever the
stops are. On a sequential ramp like colormaps.viridis() that
is legal but rarely useful.
Args:
- self (
Self) - center (
Float64): The value the ramp’s middle color sits on. Must be strictly between the color domain’s min and max.
Returns:
Self: Self, for further chaining – the render raises later if
center is not strictly inside the domain, or the mark has
no continuous color channel.
secondary_axis
fn def secondary_axis(var self) -> SelfDraw this layer’s y values against a second, independent y-domain on the plot’s right edge instead of render_layers()’s shared left-axis domain (ECharts’ yAxisIndex: 1, as a boolean since only two y-axes are ever drawn). For a combo chart whose series have different units; see _render_layers_generic.
render_layers()/render_layers_svg() only; render()/
render_svg() raise if a plot with this set reaches them. At least
one layer must stay on the primary axis. The secondary axis gets an
axis line, ticks, and tick labels on the right edge but no
gridlines. This layer’s Plot.labels() y_title captions the
secondary axis (see _secondary_axis_y_title).
See the Cookbook’s own “Dual Y-Axis” recipe (docs/src/ cookbook_recipes/dual_axis.mojo) for a full worked example.
Args:
- self (
Self)
Returns:
Self: Self, for further chaining.
erased
fn def erased(self) -> SelfArgs:
- self (
Self)
Returns:
Self
erased_with
fn def erased_with[canvas: Bool = False, svg: Bool = False, pdf: Bool = False, bounds: Bool = False, display_list: Bool = False](self) -> SelfA copy with the slots this chart was erased with: the mark type is gone, so no slot can be bound here.
Parameters:
- canvas (
Bool) - svg (
Bool) - pdf (
Bool) - bounds (
Bool) - display_list (
Bool)
Args:
- self (
Self)
Returns:
Self
capabilities
fn def capabilities(self) -> _CapabilitiesArgs:
- self (
Self)
Returns:
_Capabilities
x_data
fn def x_data(self) -> List[Float64]Args:
- self (
Self)
Returns:
List[Float64]
y_data
fn def y_data(self) -> List[Float64]Args:
- self (
Self)
Returns:
List[Float64]
render_mark
fn def render_mark[T: DrawTarget](self, mut target: T, ox0: Int, oy0: Int, ox1: Int, oy1: Int, has_shared_y_domain: Bool, shared_y_min: Float64, shared_y_max: Float64, shared_y_is_log: Bool, *, mut cache: FontCache, vector_target: Bool) -> _RenderResultParameters:
- T (
DrawTarget)
Args:
- self (
Self) - target (
T) - ox0 (
Int) - oy0 (
Int) - ox1 (
Int) - oy1 (
Int) - has_shared_y_domain (
Bool) - shared_y_min (
Float64) - shared_y_max (
Float64) - shared_y_is_log (
Bool) - cache (
FontCache) - vector_target (
Bool)
Returns:
_RenderResult
Raises: