///|
/// Raw AutoMinor subdivisions and validated integer rc values are distinct.
pub(all) enum MinorTickDivisions {
Text(String)
Number(Double)
Integer(@bigint.BigInt)
} derive(Debug, Eq)
///|
pub extend MinorTickDivisions with @debug.Debug::{to_repr}
///|
pub extend MinorTickDivisions with Eq::{equal, not_equal}
///|
// Python 3.12 Unicode decimal digits; int() accepts these without normalizing text.
fn minor_tick_decimal_digit(character : Char) -> Int32? {
let code = character.to_int()
for
zero in [
0x30, 0x660, 0x6f0, 0x7c0, 0x966, 0x9e6, 0xa66, 0xae6, 0xb66, 0xbe6, 0xc66,
0xce6, 0xd66, 0xde6, 0xe50, 0xed0, 0xf20, 0x1040, 0x1090, 0x17e0, 0x1810, 0x1946,
0x19d0, 0x1a80, 0x1a90, 0x1b50, 0x1bb0, 0x1c40, 0x1c50, 0xa620, 0xa8d0, 0xa900,
0xa9d0, 0xa9f0, 0xaa50, 0xabf0, 0xff10, 0x104a0, 0x10d30, 0x11066, 0x110f0,
0x11136, 0x111d0, 0x112f0, 0x11450, 0x114d0, 0x11650, 0x116c0, 0x11730, 0x118e0,
0x11950, 0x11c50, 0x11d50, 0x11da0, 0x11f50, 0x16a60, 0x16ac0, 0x16b50, 0x1d7ce,
0x1d7d8, 0x1d7e2, 0x1d7ec, 0x1d7f6, 0x1e140, 0x1e2f0, 0x1e4f0, 0x1e950, 0x1fbf0,
] {
if code >= zero && code < zero + 10 {
return Some(code - zero)
}
}
None
}
///|
fn minor_tick_integer_space(character : Char) -> Bool {
let code = character.to_int()
(code >= 9 && code <= 13) ||
code == 32 ||
code == 0x85 ||
code == 0xA0 ||
code == 0x1680 ||
(code >= 0x2000 && code <= 0x200A) ||
code == 0x2028 ||
code == 0x2029 ||
code == 0x202F ||
code == 0x205F ||
code == 0x3000
}
///|
fn minor_tick_integer_error() -> @core.DataError {
@core.DataError::InvalidTicks(
message="'tick.minor.ndivs' must be 'auto' or non-negative int",
)
}
///|
fn minor_tick_parse_integer(
value : String,
) -> @bigint.BigInt raise @core.DataError {
let chars = value.to_array()
let mut first = 0
let mut last = chars.length()
while first < last && minor_tick_integer_space(chars[first]) {
first += 1
}
while last > first && minor_tick_integer_space(chars[last - 1]) {
last -= 1
}
if first == last {
raise minor_tick_integer_error()
}
let negative = chars[first] == '-'
if negative || chars[first] == '+' {
first += 1
}
let digits = StringBuilder()
let mut previous_digit = false
let mut count = 0
for index in first.. {
digits.write_string(digit.to_string())
count += 1
previous_digit = true
}
None => {
if chars[index] != '_' ||
!previous_digit ||
index + 1 == last ||
minor_tick_decimal_digit(chars[index + 1]) is None {
raise minor_tick_integer_error()
}
previous_digit = false
}
}
}
if !previous_digit || count > 4300 {
raise minor_tick_integer_error()
}
let integer = @bigint.BigInt::from_string(digits.to_string())
if negative && !integer.is_zero() {
raise minor_tick_integer_error()
}
integer
}
///|
// Truncate the complete finite binary64 domain without an Int64 intermediate.
fn minor_tick_truncate(value : Double) -> @bigint.BigInt raise @core.DataError {
if value.is_nan() || value.is_inf() {
raise minor_tick_integer_error()
}
let bits = value.reinterpret_as_uint64()
let exponent = ((bits >> 52) & 0x7FFUL).to_int() - 1023 - 52
let significand = (bits & 0xFFFFFFFFFFFFFUL) | 0x10000000000000UL
let integer = if exponent < -52 {
@bigint.BigInt::from_int(0)
} else if exponent < 0 {
@bigint.BigInt::from_uint64(significand) >> -exponent
} else {
@bigint.BigInt::from_uint64(significand) << exponent
}
if value < 0.0 && !integer.is_zero() {
raise minor_tick_integer_error()
}
integer
}
///|
fn validate_minor_tick_divisions(
value : MinorTickDivisions,
) -> MinorTickDivisions raise @core.DataError {
match value {
Text(text) =>
if text.to_lower() == "auto" {
Text(text)
} else {
Integer(minor_tick_parse_integer(text))
}
Number(number) => Integer(minor_tick_truncate(number))
Integer(integer) => {
if integer < @bigint.BigInt::from_int(0) {
raise minor_tick_integer_error()
}
Integer(integer)
}
}
}
///|
/// Return the current x or y minor subdivision rc value.
pub fn minor_tick_divisions(horizontal~ : Bool = true) -> MinorTickDivisions {
if horizontal {
current.val.x_minor_tick_divisions
} else {
current.val.y_minor_tick_divisions
}
}
///|
/// Validate the new value before replacing its axis-specific rc setting.
pub fn set_minor_tick_divisions(
value : MinorTickDivisions,
horizontal~ : Bool = true,
) -> Unit raise @core.DataError {
let value = validate_minor_tick_divisions(value)
if horizontal {
current.val.x_minor_tick_divisions = value
} else {
current.val.y_minor_tick_divisions = value
}
}
///|
pub fn RcParams::minor_tick_divisions(
self : RcParams,
horizontal~ : Bool = true,
) -> MinorTickDivisions {
if horizontal {
self.x_minor_tick_divisions
} else {
self.y_minor_tick_divisions
}
}