///|
/// Symmetric logarithmic ticks retain raw parameters and their last axis view.
pub struct SymmetricalLogLocator {
priv base : Double
priv linthresh : Double
priv mut subs : SymLogSubs
priv mut numticks : LogNumTicks
priv mut view_interval_provider : (() -> (Double, Double) raise @core.DataError)?
}
///|
/// Capture a SymLog transform's base/threshold, or require both explicit values.
/// The transform's linscale and explicit overrides are ignored when it is present.
pub fn SymmetricalLogLocator::new(
transform? : @scale.Scale,
base? : Double,
linthresh? : Double,
subs~ : SymLogSubs? = None,
) -> SymmetricalLogLocator raise @core.DataError {
let (base, linthresh) = match transform {
Some(@scale.Scale::SymLog(base~, linthresh~, ..)) => (base, linthresh)
Some(_) =>
raise @core.DataError::InvalidTicks(
message="symlog locator transform must expose base and linthresh",
)
None =>
match (base, linthresh) {
(Some(base), Some(linthresh)) => (base, linthresh)
_ =>
raise @core.DataError::InvalidTicks(
message="Either transform, or both linthresh and base, must be provided.",
)
}
}
{
base,
linthresh,
subs: subs.unwrap_or_else(fn() { Values(TickValues::from_array([1.0])) }),
numticks: Integer(@bigint.BigInt::from_int(15)),
view_interval_provider: None,
}
}
///|
pub fn SymmetricalLogLocator::base(self : SymmetricalLogLocator) -> Double {
self.base
}
///|
pub fn SymmetricalLogLocator::linthresh(self : SymmetricalLogLocator) -> Double {
self.linthresh
}
///|
pub fn SymmetricalLogLocator::subs(self : SymmetricalLogLocator) -> SymLogSubs {
self.subs
}
///|
pub fn SymmetricalLogLocator::numticks(
self : SymmetricalLogLocator,
) -> LogNumTicks {
self.numticks
}
///|
pub fn SymmetricalLogLocator::set_numticks(
self : SymmetricalLogLocator,
value : LogNumTicks,
) -> Unit {
self.numticks = value
}
///|
/// Store numticks before subs; absent values preserve the corresponding slot.
pub fn SymmetricalLogLocator::set_params(
self : SymmetricalLogLocator,
subs? : SymLogSubs,
numticks? : LogNumTicks,
) -> Unit {
if numticks is Some(value) {
self.numticks = value
}
if subs is Some(value) {
self.subs = value
}
}
///|
pub fn SymmetricalLogLocator::copy(
self : SymmetricalLogLocator,
) -> SymmetricalLogLocator {
{
base: self.base,
linthresh: self.linthresh,
subs: self.subs,
numticks: self.numticks,
view_interval_provider: self.view_interval_provider,
}
}
///|
pub fn SymmetricalLogLocator::bind_view_interval(
self : SymmetricalLogLocator,
provider : () -> (Double, Double) raise @core.DataError,
) -> Unit {
self.view_interval_provider = Some(provider)
}
///|
pub fn SymmetricalLogLocator::view_interval_provider(
self : SymmetricalLogLocator,
) -> (() -> (Double, Double) raise @core.DataError)? {
self.view_interval_provider
}
///|
pub fn SymmetricalLogLocator::call(
self : SymmetricalLogLocator,
) -> Array[Double] raise @core.DataError {
let (lower, upper) = match self.view_interval_provider {
Some(provider) => provider()
None =>
raise @core.DataError::InvalidTicks(
message="symlog locator is not bound to a view interval",
)
}
self.tick_values(lower, upper)
}
///|
pub fn SymmetricalLogLocator::tick_values(
self : SymmetricalLogLocator,
lower : Double,
upper : Double,
) -> Array[Double] raise @core.DataError {
named_symlog_locator_ticks(self, lower, upper)
}
///|
pub fn SymmetricalLogLocator::nonsingular(
_self : SymmetricalLogLocator,
lower : Double,
upper : Double,
) -> (Double, Double) {
locator_nonsingular(lower, upper, 0.05)
}
///|
/// Round original endpoints first, expand equal decades, then apply .001 nonsingular.
pub fn SymmetricalLogLocator::view_limits(
self : SymmetricalLogLocator,
lower : Double,
upper : Double,
) -> (Double, Double) raise @core.DataError {
let mut first = lower
let mut last = upper
if last < first {
let previous = first
first = last
last = previous
}
if @config.axes_autolimit_mode() == "round_numbers" {
// A zero short-circuits the original helper and keeps its Python float.
// Other inputs produce NumPy scalars, including an underflowed zero.
let lower_is_python_float = first == 0.0
first = log_decade_less_equal(first, self.base)
last = log_decade_greater_equal(last, self.base)
if first == last {
// The strict helper divides this retained Python float by the base.
// NumPy scalar division instead retains its IEEE infinity/NaN result.
if lower_is_python_float && self.base == 0.0 {
raise @core.DataError::InvalidTicks(message="float division by zero")
}
first = log_decade_less(first, self.base)
last = log_decade_greater(last, self.base)
}
}
locator_nonsingular(first, last, 0.001)
}