///|
/// A backend-independent figure containing ordered axes panels.
pub struct Figure {
width : Int32
height : Int32
margin : Int32
axes : Array[@ref.Ref[AxesData]]
priv axes_selection : Array[@ref.Ref[AxesData]]
subfigures : Array[@ref.Ref[SubFigureData]]
colorbars : Array[@colorbar.Colorbar]
mut share_group_seed : Int32
priv mut share_x_next_order : Int64
priv mut share_y_next_order : Int64
mut title : String?
priv mut subplot_left : Double
priv mut subplot_bottom : Double
priv mut subplot_right : Double
priv mut subplot_top : Double
priv mut subplot_wspace : Double?
priv mut subplot_hspace : Double?
}
///|
/// Create an empty figure with one default axes panel.
pub fn figure(width~ : Int32 = 640, height~ : Int32 = 480) -> Figure {
let margin = 56
let figure_width = Double::from_int(width)
let figure_height = Double::from_int(height)
let left = if width > 0 {
Double::from_int(margin) / figure_width
} else {
0.0
}
let bottom = if height > 0 {
Double::from_int(margin) / figure_height
} else {
0.0
}
let default_axes = @ref.new({
artists: [],
line_originals: [],
x_axis: @axis.axis(),
y_axis: @axis.axis(horizontal=false),
title: None,
explicit_bounds: None,
legend: [],
auto_legend: [],
explicit_legend: false,
legend_position: UpperRight,
containers: [],
rect: None,
position_override: None,
active_position: None,
subplot_spec: None,
share_x_group: None,
share_y_group: None,
share_x_order: 0L,
share_y_order: 0L,
share_x_target: None,
share_y_target: None,
share_x_members: None,
share_y_members: None,
show_x_tick_labels: true,
show_y_tick_labels: true,
frame_visible: true,
spines: default_spines(),
x_margin: @config.x_margin(),
y_margin: @config.y_margin(),
raw_x_data: @core.limits(1.0 / 0.0, -1.0 / 0.0),
raw_y_data: @core.limits(1.0 / 0.0, -1.0 / 0.0),
x_data_minpos: 1.0 / 0.0,
y_data_minpos: 1.0 / 0.0,
ignore_existing_data_limits: true,
tight: None,
use_sticky_edges: true,
color_cycle: default_color_cycle(),
color_cycle_index: 0,
data_aspect: None,
anchor: Anchor::Named("C"),
adjustable: "box",
box_aspect: None,
secondary_x: [],
secondary_y: [],
})
let result : Figure = {
width,
height,
margin,
axes: [default_axes],
axes_selection: [default_axes],
subfigures: [],
colorbars: [],
share_group_seed: -1,
share_x_next_order: 0L,
share_y_next_order: 0L,
title: None,
subplot_left: left,
subplot_bottom: bottom,
subplot_right: 1.0 - left,
subplot_top: 1.0 - bottom,
subplot_wspace: None,
subplot_hspace: None,
}
let axes : Axes = { figure: result, state: default_axes, }
axes.bind_axis_data(true)
axes.bind_axis_data(false)
try! axes.clear_checked()
result
}
///|
/// Set the figure title.
pub fn Figure::set_title(self : Figure, title : String) -> Unit {
self.title = Some(title)
}
///|
/// Clear all axes, artists, legends, and colorbars while retaining the
/// figure's dimensions and subplot layout.
pub fn Figure::clear(self : Figure) -> Unit {
try! self.clear_checked()
}
///|
/// Execute the checked Axes clear lifecycle once per retained panel.
/// The panel-preserving Figure convenience contract remains unchanged.
pub fn Figure::clear_checked(self : Figure) -> Unit raise @core.DataError {
for state in self.axes.copy() {
({ figure: self, state, } : Axes).clear_checked()
}
for data in self.subfigures {
data.val.clear_titles()
}
self.title = None
self.subfigures.clear()
self.colorbars.clear()
}
///|
/// Set the x-axis label on the default axes.
pub fn Figure::set_x_label(self : Figure, label : String) -> Unit {
self.default_axes_data().val.x_axis.set_label(label)
}
///|
/// Set the y-axis label on the default axes.
pub fn Figure::set_y_label(self : Figure, label : String) -> Unit {
self.default_axes_data().val.y_axis.set_label(label)
}
///|
/// Set explicit data limits for the default axes.
pub fn Figure::set_bounds(
self : Figure,
limits : @core.Bounds,
) -> Unit raise @core.DataError {
if !limits.is_finite() {
raise @core.DataError::NonFiniteLimit(axis="both")
}
self.axes().set_bounds(limits)
}
///|
/// Set x-axis limits on the default axes, preserving their direction.
pub fn Figure::set_x_limits(
self : Figure,
lower : Double,
upper : Double,
) -> Unit raise @core.DataError {
if !@core.limits(lower, upper).is_finite() {
raise @core.DataError::NonFiniteLimit(axis="x")
}
self.axes().set_x_limits(lower, upper)
}
///|
/// Set y-axis limits on the default axes, preserving their direction.
pub fn Figure::set_y_limits(
self : Figure,
lower : Double,
upper : Double,
) -> Unit raise @core.DataError {
if !@core.limits(lower, upper).is_finite() {
raise @core.DataError::NonFiniteLimit(axis="y")
}
self.axes().set_y_limits(lower, upper)
}
///|
/// Set the automatic x-axis margin on the default axes.
pub fn Figure::set_x_margin(
self : Figure,
value : Double,
) -> Unit raise @core.DataError {
self.axes().set_x_margin(value)
}
///|
/// Set the automatic y-axis margin on the default axes.
pub fn Figure::set_y_margin(
self : Figure,
value : Double,
) -> Unit raise @core.DataError {
self.axes().set_y_margin(value)
}
///|
/// Set both automatic margins on the default axes.
pub fn Figure::set_margins(
self : Figure,
x : Double,
y : Double,
) -> Unit raise @core.DataError {
self.axes().set_margins(x, y)
}
///|
/// Return the default axes' automatic margins.
pub fn Figure::margins(self : Figure) -> (Double, Double) {
(self.axes().x_margin(), self.axes().y_margin())
}
///|
/// Return the default axes' automatic x-axis margin.
pub fn Figure::x_margin(self : Figure) -> Double {
self.axes().x_margin()
}
///|
/// Return the default axes' automatic y-axis margin.
pub fn Figure::y_margin(self : Figure) -> Double {
self.axes().y_margin()
}
///|
/// Restore automatic limits for one or both axes.
pub fn Figure::autoscale(
self : Figure,
enable~ : Bool? = Some(true),
axis~ : String = "both",
tight? : Bool,
) -> Unit raise @core.DataError {
self.axes().autoscale(enable~, axis~, tight?)
}
///|
/// Set fixed x-axis tick positions on the default axes.
pub fn Figure::set_x_ticks(
self : Figure,
values : Array[Double],
) -> Unit raise @core.DataError {
self.axes().set_x_ticks(values)
}
///|
/// Set fixed y-axis tick positions on the default axes.
pub fn Figure::set_y_ticks(
self : Figure,
values : Array[Double],
) -> Unit raise @core.DataError {
self.axes().set_y_ticks(values)
}
///|
/// Set the numeric major locator on the default x axis.
pub fn Figure::set_x_locator(
self : Figure,
locator : @ticker.Locator,
) -> Unit raise @core.DataError {
self.axes().set_x_locator(locator)
}
///|
/// Set the numeric major locator on the default y axis.
pub fn Figure::set_y_locator(
self : Figure,
locator : @ticker.Locator,
) -> Unit raise @core.DataError {
self.axes().set_y_locator(locator)
}
///|
/// Set the numeric formatter on the default x axis.
pub fn Figure::set_x_formatter(
self : Figure,
formatter : @ticker.Formatter,
) -> Unit {
self.axes().set_x_formatter(formatter)
}
///|
/// Set the numeric formatter on the default y axis.
pub fn Figure::set_y_formatter(
self : Figure,
formatter : @ticker.Formatter,
) -> Unit {
self.axes().set_y_formatter(formatter)
}
///|
/// Set the minor locator on the default x axis.
pub fn Figure::set_x_minor_locator(
self : Figure,
locator : @ticker.MinorLocator,
) -> Unit raise @core.DataError {
self.axes().set_x_minor_locator(locator)
}
///|
/// Set the minor locator on the default y axis.
pub fn Figure::set_y_minor_locator(
self : Figure,
locator : @ticker.MinorLocator,
) -> Unit raise @core.DataError {
self.axes().set_y_minor_locator(locator)
}
///|
/// Set fixed x-axis tick labels on the default axes.
pub fn Figure::set_x_tick_labels(
self : Figure,
labels : Array[String],
) -> Unit raise @core.DataError {
self.axes().set_x_tick_labels(labels)
}
///|
/// Set fixed y-axis tick labels on the default axes.
pub fn Figure::set_y_tick_labels(
self : Figure,
labels : Array[String],
) -> Unit raise @core.DataError {
self.axes().set_y_tick_labels(labels)
}
///|
/// Return the figure width in display units.
pub fn Figure::width(self : Figure) -> Int32 {
self.width
}
///|
/// Return the figure height in display units.
pub fn Figure::height(self : Figure) -> Int32 {
self.height
}
///|
/// Return the plot margin in display units.
pub fn Figure::margin(self : Figure) -> Int32 {
self.margin
}
///|
/// Return artists from the default axes in insertion order.
pub fn Figure::artists(self : Figure) -> Array[Artist] {
let artists = self.default_axes_data().val.artists
let result = Array(capacity=artists.length())
for artist in artists {
result.push(copy_artist(artist))
}
result
}
///|
/// Return the optional figure title.
pub fn Figure::title(self : Figure) -> String? {
self.title
}
///|
/// Return the optional x-axis label from the default axes.
pub fn Figure::x_label(self : Figure) -> String? {
self.default_axes_data().val.x_axis.label()
}
///|
/// Return the optional y-axis label from the default axes.
pub fn Figure::y_label(self : Figure) -> String? {
self.default_axes_data().val.y_axis.label()
}
///|
/// Return explicit limits from the default axes, if present.
pub fn Figure::explicit_bounds(self : Figure) -> @core.Bounds? {
self.default_axes_data().val.explicit_bounds
}
///|
/// Return explicit x limits, or the resolved datalim view, on the default axes.
pub fn Figure::x_limits(self : Figure) -> @core.Limits? {
self.axes().x_limits()
}
///|
/// Return explicit y limits, or the resolved datalim view, on the default axes.
pub fn Figure::y_limits(self : Figure) -> @core.Limits? {
self.axes().y_limits()
}
///|
/// Return the x-axis object from the default axes.
pub fn Figure::x_axis(self : Figure) -> @axis.Axis {
self.default_axes_data().val.x_axis
}
///|
/// Return the y-axis object from the default axes.
pub fn Figure::y_axis(self : Figure) -> @axis.Axis {
self.default_axes_data().val.y_axis
}
///|
/// Set equal data scaling on the default axes panel.
pub fn Figure::set_aspect_equal(self : Figure) -> Unit {
self.axes().set_aspect_equal()
}
///|
/// Restore automatic aspect handling on the default axes panel.
pub fn Figure::set_aspect_auto(self : Figure) -> Unit {
self.axes().set_aspect_auto()
}
///|
/// Hide axis decorations on the default axes panel while retaining artists.
pub fn Figure::set_axis_off(self : Figure) -> Unit {
self.axes().set_axis_off()
}
///|
/// Show axis decorations on the default axes panel.
pub fn Figure::set_axis_on(self : Figure) -> Unit {
self.axes().set_axis_on()
}
///|
/// Set the x-axis tick label side on the default axes panel.
pub fn Figure::set_x_tick_position(
self : Figure,
position : @axis.TickPosition,
) -> Unit {
self.axes().set_x_tick_position(position)
}
///|
/// Set the y-axis tick label side on the default axes panel.
pub fn Figure::set_y_tick_position(
self : Figure,
position : @axis.TickPosition,
) -> Unit {
self.axes().set_y_tick_position(position)
}
///|
/// Configure major or minor grid lines on the default axes panel.
pub fn Figure::grid(
self : Figure,
visible~ : Bool = true,
which~ : String = "both",
color~ : String = "#e5e7eb",
linewidth~ : Double = 1.0,
axis~ : String = "both",
) -> Unit raise @core.DataError {
self.axes().grid(visible~, which~, color~, linewidth~, axis~)
}
///|
/// Return the first/default axes panel for this figure.
/// If all panels have been removed, create a 1-by-1 subplot.
pub fn Figure::axes(self : Figure) -> Axes {
{ figure: self, state: self.default_axes_data(), }
}
///|
/// Return handles for all axes panels in creation order.
pub fn Figure::axes_list(self : Figure) -> Array[Axes] {
let result = Array(capacity=self.axes.length())
for state in self.axes {
result.push({ figure: self, state, })
}
result
}
///|
/// Add a panel at a GridSpec span. A fresh figure reuses its default panel.
pub fn Figure::add_subplot(self : Figure, spec : @gridspec.SubplotSpec) -> Axes {
self.add_subplot_with_targets(spec, None, None)
}
///|
/// Add a line plot from paired x/y samples.
pub fn Figure::plot(
self : Figure,
xs : Array[Double],
ys : Array[Double],
color~ : String = "",
label~ : String = "",
) -> Unit raise @core.DataError {
self.axes().plot(xs, ys, color~, label~)
}
///|
/// Add a line plot from explicit points to the default axes.
pub fn Figure::line(
self : Figure,
points : Array[@core.Point],
color~ : String = "",
label~ : String = "",
) -> Unit {
self.axes().line(points, color~, label~)
}
///|
/// Add a scatter plot to the default axes.
pub fn Figure::scatter(
self : Figure,
points : Array[@core.Point],
color~ : String = "",
label~ : String = "",
) -> Unit {
self.axes().scatter(points, color~, label~)
}
///|
/// Add bars to the default axes.
pub fn Figure::bar(
self : Figure,
points : Array[@core.Point],
color~ : String = "",
label~ : String = "",
) -> Unit {
self.axes().bar(points, color~, label~)
}