///|
// Each installed ticker slot has its own geometry/formatting role.
priv enum AxisTickerKind {
Numeric
Date
Category
} derive(Eq)
///|
// Numeric ticker state is shared independently from an Axis's geometry/context.
priv struct AxisNumericTickers {
mut major_locator_kind : AxisTickerKind
mut minor_locator_kind : AxisTickerKind
mut major_formatter_kind : AxisTickerKind
mut minor_formatter_kind : AxisTickerKind
mut major_date_view_provider : (() -> (Double, Double) raise @core.DataError)?
mut minor_date_view_provider : (() -> (Double, Double) raise @core.DataError)?
mut automatic_date_locator : @date.AutoDateLocator?
mut date_locator : @date.DateLocator?
mut date_formatter : @date.DateFormatter?
mut date_formatter_default : Bool
mut minor_date_locator : @date.DateLocator?
mut minor_date_formatter : @date.DateFormatter?
mut date_format : String?
mut category_locator_units : CategoryUnits?
mut category_formatter_units : CategoryUnits?
mut locator : @ticker.Locator?
mut minor_locator : @ticker.MinorLocator?
mut named_formatter : @ticker.BaseFormatter?
mut minor_named_formatter : @ticker.BaseFormatter?
mut formatter : @ticker.Formatter?
mut fixed_ticks : Array[Double]?
mut fixed_labels : Array[String]?
mut major_locator_default : Bool
mut minor_locator_default : Bool
mut major_formatter_default : Bool
mut minor_formatter_default : Bool
}
///|
fn AxisNumericTickers::new() -> AxisNumericTickers {
{
major_locator_kind: Numeric,
minor_locator_kind: Numeric,
major_formatter_kind: Numeric,
minor_formatter_kind: Numeric,
major_date_view_provider: None,
minor_date_view_provider: None,
automatic_date_locator: None,
date_locator: None,
date_formatter: None,
date_formatter_default: false,
minor_date_locator: None,
minor_date_formatter: None,
date_format: None,
category_locator_units: None,
category_formatter_units: None,
locator: None,
minor_locator: None,
named_formatter: None,
minor_named_formatter: None,
formatter: None,
fixed_ticks: None,
fixed_labels: None,
major_locator_default: true,
minor_locator_default: true,
major_formatter_default: true,
minor_formatter_default: true,
}
}
///|
// Converter metadata can outlive explicit ticker replacement.
fn Axis::numeric_tickers_active(self : Axis) -> Bool {
self.tickers.major_locator_kind == Numeric
}
///|
fn Axis::category_for_ticks(self : Axis) -> CategoryUnits? {
if self.tickers.major_locator_kind == Category {
self.tickers.category_locator_units
} else {
None
}
}
///|
fn Axis::category_for_labels(self : Axis) -> CategoryUnits? {
if self.tickers.major_formatter_kind == Category {
self.tickers.category_formatter_units
} else {
None
}
}
///|
fn Axis::date_format_for_ticks(self : Axis) -> String? {
if self.tickers.major_locator_kind == Date {
Some(self.date_format.unwrap_or(""))
} else {
None
}
}
///|
fn Axis::date_format_for_labels(self : Axis) -> String? {
if self.tickers.major_formatter_kind == Date {
Some(self.tickers.date_format.unwrap_or(""))
} else {
None
}
}
///|
/// Share numeric ticker slots without changing geometry or object ownership.
pub fn Axis::share_numeric_tickers(self : Axis, source : Axis) -> Unit {
self.tickers = source.tickers
}
///|
/// Inherit geometry without reinstalling tickers, as shared-axis linking does.
pub fn Axis::inherit_scale_from(self : Axis, source : Axis) -> Unit {
self.scale = source.scale
self.function_scale = source.function_scale
}
///|
pub fn Axis::is_default_locator(self : Axis, minor~ : Bool = false) -> Bool {
if minor {
self.tickers.minor_locator_default
} else {
self.tickers.major_locator_default
}
}
///|
pub fn Axis::is_default_formatter(self : Axis, minor~ : Bool = false) -> Bool {
if minor {
self.tickers.minor_formatter_default
} else {
self.tickers.major_formatter_default
}
}
///|
// Called only after the complete per-scale installation succeeds.
fn Axis::mark_scale_defaults(self : Axis) -> Unit {
self.tickers.major_locator_default = true
self.tickers.minor_locator_default = true
self.tickers.major_formatter_default = true
self.tickers.minor_formatter_default = true
}
///|
/// Query the current major locator's nonsingular range for empty data.
pub fn Axis::locator_default_limits(
self : Axis,
) -> (Double, Double) raise @core.DataError {
self.locator_nonsingular(-1.0 / 0.0, 1.0 / 0.0)
}
///|
fn Axis::locator_nonsingular(
self : Axis,
lower : Double,
upper : Double,
) -> (Double, Double) raise @core.DataError {
if self.tickers.major_locator_kind == Date {
return match self.tickers.date_locator {
Some(locator) => locator.nonsingular(lower, upper)
None =>
match self.tickers.automatic_date_locator {
Some(locator) => locator.nonsingular(lower, upper)
None => @ticker.NullLocator::new().nonsingular(lower, upper)
}
}
}
if self.tickers.major_locator_kind == Category {
return @ticker.NullLocator::new().nonsingular(lower, upper)
}
match self.tickers.locator {
Some(@ticker.Locator::NamedLogit(value)) => value.nonsingular(lower, upper)
Some(@ticker.Locator::NamedAsinh(value)) => value.nonsingular(lower, upper)
Some(@ticker.Locator::NamedSymLog(value)) => value.nonsingular(lower, upper)
Some(@ticker.Locator::NamedLog(value)) => value.nonsingular(lower, upper)
Some(@ticker.Locator::NamedMaxN(value)) => value.nonsingular(lower, upper)
Some(@ticker.Locator::NamedAuto(value)) => value.nonsingular(lower, upper)
Some(@ticker.Locator::NamedLinear(value)) => value.nonsingular(lower, upper)
Some(@ticker.Locator::Index(value)) => value.nonsingular(lower, upper)
Some(@ticker.Locator::NamedFixed(value)) => value.nonsingular(lower, upper)
Some(@ticker.Locator::Multiple(value)) => value.nonsingular(lower, upper)
Some(@ticker.Locator::Null(value)) => value.nonsingular(lower, upper)
_ => @ticker.NullLocator::new().nonsingular(lower, upper)
}
}
///|
/// Finalize a numeric view after a linear lifecycle reset.
/// The outer Scene operation controls sibling propagation and target inheritance.
pub fn Axis::set_cleared_view_limits(
self : Axis,
lower : Double,
upper : Double,
automatic~ : Bool,
) -> Unit raise @core.DataError {
if self.scale.is_nonlinear() {
raise @core.DataError::InvalidTicks(
message="clear view finalization requires linear geometry",
)
}
if !@core.limits(lower, upper).is_finite() {
raise @core.DataError::NonFiniteLimit(axis="axis")
}
let reversed = lower > upper
let (first, last) = self.locator_nonsingular(lower, upper)
// Source Axis.limit_range_for_scale reads own minpos even for LinearScale.
let (first, last) = self.scale.limit_range_for_scale(
first,
last,
self.get_minpos(),
)
let low = Double::min(first, last)
let high = Double::max(first, last)
let view = if reversed {
@core.limits(high, low)
} else {
@core.limits(low, high)
}
if automatic {
self.limits = None
self.auto_view = Some(view)
} else {
self.limits = Some(view)
self.auto_view = None
}
self.inverted = reversed
self.stored_view = view
self.autoscale_enabled = automatic
self.autoscale_pending = false
if self.tick_layout_view is Some(_) {
self.tick_layout_view = Some((view.lower(), view.upper()))
}
}
///|
// Converter axisinfo never overwrites a manually selected slot.
fn Axis::install_date_unit_defaults(
self : Axis,
info : @date.DateAxisInfo,
) -> Unit {
// Keep the convenience snapshot available even with an explicit numeric slot.
if self.tickers.automatic_date_locator is None {
self.tickers.automatic_date_locator = Some(
self.make_default_date_locator(info),
)
}
if self.tickers.major_locator_default {
self.tickers.major_locator_kind = Date
self.tickers.locator = None
self.tickers.fixed_ticks = None
self.tickers.date_locator = None
self.tickers.automatic_date_locator = Some(
self.make_default_date_locator(info),
)
self.bind_locator_data()
self.tickers.major_locator_default = true
}
if self.tickers.major_formatter_default {
self.tickers.major_formatter_kind = Date
self.tickers.named_formatter = None
self.tickers.formatter = None
self.tickers.fixed_labels = None
self.tickers.date_format = Some("")
self.tickers.date_formatter = Some(info.major_formatter())
self.tickers.date_formatter_default = true
self.tickers.major_formatter_default = true
}
if self.label_default {
self.label = Some(info.label())
}
}
///|
fn Axis::install_category_unit_defaults(
self : Axis,
units : CategoryUnits,
) -> Unit {
if self.tickers.major_locator_default {
self.tickers.major_locator_kind = Category
self.tickers.locator = None
self.tickers.fixed_ticks = None
self.tickers.category_locator_units = Some(units)
self.tickers.major_locator_default = true
}
if self.tickers.major_formatter_default {
self.tickers.major_formatter_kind = Category
self.tickers.named_formatter = None
self.tickers.formatter = None
self.tickers.fixed_labels = None
self.tickers.category_formatter_units = Some(units)
self.tickers.major_formatter_default = true
}
}