Public
Edited
Feb 3, 2023
Insert cell
# choose-ticks translation
Insert cell
function first_power10(min, max) {
let value = max < 0 ? - (10 ** Math.ceil(Math.log(-max)/ Math.log(10))) : 10 ** (Math.floor(Math.log(max) / Math.log(10)))
return value <= min ? false : value
}
Insert cell
first_power10(27, 1037) // should be 1000 (first power below max)
Insert cell
first_power10(0, Number.MAX_VALUE)
Insert cell
function clamp(x, lo, hi) {
return Math.min(hi, Math.max(x, lo))
}
Insert cell
clamp(1, 3, 4)
Insert cell
math = require('mathjs')
Insert cell
function to_signed_int (float64) {
const buffer = new ArrayBuffer(8)
const view = new DataView(buffer)
view.setFloat64(0, float64)
return view.getBigInt64(0)
}
Insert cell
function real_from_signed_int (signed) {
const buffer = new ArrayBuffer(8)
const view = new DataView(buffer)
view.setBigInt64(0, signed)
return view.getFloat64(0)
}
Insert cell
function mbn(x) {
return math.bignumber(to_signed_int(x).toString())
}
Insert cell
mbn_neg_0 = mbn(-0.0)
Insert cell
function real_to_ordinal(real) {
let signed = to_signed_int(real)
let mbn = math.bignumber(signed.toString())
return signed >= 0 ? mbn : math.subtract(mbn_neg_0, mbn)
}
Insert cell
function ordinal_to_real(ordinal) {
return ordinal >=0 ? real_from_signed_int(BigInt(ordinal)) : real_from_signed_int(BigInt(math.subtract(mbn_neg_0, math.bignumber(ordinal))))
}
Insert cell
function float_to_approximate_ordinal(float) {
return real_to_ordinal(float).toNumber()
}
Insert cell
float_to_approximate_ordinal(Number.MAX_VALUE)
Insert cell
Number.MAX_VALUE
Insert cell
Number.MIN_VALUE
Insert cell
ordinal_to_real(real_to_ordinal(Number.MIN_VALUE))
Insert cell
function choose_between(min, max, number) {
// ; Returns a given number of ticks, roughly evenly spaced, between min and max
// ; For any tick, n divisions below max, the tick is an ordinal corresponding to:
// ; (a) a power of 10 between n and (n + ε) divisions below max where ε is some tolerance, or
// ; (b) a value, n divisions below max
let sub_range = Math.round((max - min) / (1 + number))
let near = (x, n) => (x <= n) && (Math.abs((x - n) / sub_range) <= .2) // <= tolerance
return [...Array(number)].map((_, i) => i + 1).map(itr => {
let power10 = first_power10(
ordinal_to_real(clamp(max - ((itr + 1) * sub_range), min, max)),
ordinal_to_real(clamp(max - (itr * sub_range), min, max))
)
console.log(power10)
return (power10 && near(real_to_ordinal(power10), max - (itr * sub_range))) ? real_to_ordinal(power10)
: max - (itr * sub_range)
})
}
Insert cell
choose_between(real_to_ordinal(1), real_to_ordinal(1e3), 13)
Insert cell
first_power10(1, 10000)
Insert cell
choose_between(real_to_ordinal(0), real_to_ordinal(Number.MAX_VALUE), 13).map(v => ordinal_to_real(v))
Insert cell
ordinal_to_real(real_to_ordinal(1))
Insert cell
ordinal_to_real(real_to_ordinal(1e3))
Insert cell
choose_between(real_to_ordinal(1), real_to_ordinal(1e2), 13).map(v => ordinal_to_real(v))
Insert cell
choose_between(real_to_ordinal(0), real_to_ordinal(Number.MAX_VALUE), 0)
Insert cell
function pick_spaced_ordinals(necessary, min, max, number) {
// NOTE that subtle use of mathjs bignumbers is required in this function...
let sub_range = math.divide(math.bignumber(math.subtract(math.bignumber(max), math.bignumber(min))), math.bignumber(number)) // size of a division on the ordinal range
let necessary_star = (function loop(necessary) {
return necessary.length < 2 ? necessary
: math.smaller(math.subtract(necessary[1], necessary[0]), sub_range) ? loop(necessary.slice(1))
: [necessary[0], ...loop(necessary.slice(1))]
})(necessary) // filter out necessary points that are too close
let all = (function loop(necessary, min_star, start) {
if (start >= number) { return [] }
if (necessary.length == 0) { return choose_between(min_star, max, number - start) }
let idx = false
for (let i=0; i<number; i++) {
if (math.smallerEq(math.subtract(necessary[0], math.add(min, math.bignumber(math.multiply(i, sub_range)))), sub_range)) {
idx = i
break
}
}
return [...choose_between(min_star, necessary[0], idx - start), ...loop(necessary.slice(1), necessary[0], idx + 1)]
})(necessary_star, min, 0)
return [...all, ...necessary_star].sort((a, b) => math.subtract(math.bignumber(a), math.bignumber(b)))
}
Insert cell
pick_spaced_ordinals([ real_to_ordinal(0), real_to_ordinal(1)], real_to_ordinal(0), real_to_ordinal(Number.MAX_VALUE), 13).map(v => ordinal_to_real(v))
Insert cell
pick_spaced_ordinals([real_to_ordinal(-1), real_to_ordinal(-1), real_to_ordinal(0), real_to_ordinal(1), real_to_ordinal(Number.MAX_VALUE)], real_to_ordinal(-1), real_to_ordinal(Number.MAX_VALUE), 13).map(v => ordinal_to_real(v).toString())
Insert cell
function choose_ticks (min, max) {
let tick_count = 13
let necessary = [min, -1.0, 0, 1.0, max].filter(v => (min <= v) && (v <= max) && (min <= max)).map(v => real_to_ordinal(v))
let major_ticks = pick_spaced_ordinals(necessary, real_to_ordinal(min), real_to_ordinal(max), tick_count).map(v => ordinal_to_real(v))
return major_ticks
}
Insert cell
test = choose_ticks(-.731, Number.MAX_VALUE)
Insert cell
test.map(formatter)
Insert cell
formatter = v => {
const s = v.toString()
const [base, exponent] = s.split('e')
const [l, r] = base.split('.')
const formattedBase = !r ? base : `${l}.${r.slice(0, 2)}`
return !exponent ? formattedBase : `${formattedBase}e${exponent}`
}
Insert cell
formatter2 = v => {
const s = v.toPrecision(1)
const [base, exponent] = s.split('e')
if (!exponent) {
return v.toPrecision(1) == v ? v.toPrecision(1) : v.toPrecision(6)
}
if (exponent <= 1 && -1 <= exponent) {
const a = v.toString()
const b = v.toPrecision(6)
return a.length < b.length ? a : b
}
return v.toPrecision(1)
}
Insert cell
test2 = choose_ticks(1, 1000)
Insert cell
test2.map(formatter2)
Insert cell
function choose_ticks_clientside (min, max) {
function to_signed_int (float64) {
const buffer = new ArrayBuffer(8)
const view = new DataView(buffer)
view.setFloat64(0, float64)
return view.getBigInt64(0)
}
function real_from_signed_int (signed) {
const buffer = new ArrayBuffer(8)
const view = new DataView(buffer)
view.setBigInt64(0, signed)
return view.getFloat64(0)
}
function first_power10(min, max) {
let value = max < 0 ? - (10 ** Math.ceil(Math.log(-max)/ Math.log(10))) : 10 ** (Math.floor(Math.log(max) / Math.log(10)))
return value <= min ? false : value
}
function clamp(x, lo, hi) {
return Math.min(hi, Math.max(x, lo))
}
function mbn(x) {
return math.bignumber(to_signed_int(x).toString())
}
let mbn_neg_0 = mbn(-0.0)
function real_to_ordinal(real) {
let signed = to_signed_int(real)
let mbn = math.bignumber(signed.toString())
return signed >= 0 ? mbn : math.subtract(mbn_neg_0, mbn)
}
function ordinal_to_real(ordinal) {
return ordinal >=0 ? real_from_signed_int(BigInt(ordinal)) : real_from_signed_int(BigInt(math.subtract(mbn_neg_0, math.bignumber(ordinal))))
}
function choose_between(min, max, number) {
// ; Returns a given number of ticks, roughly evenly spaced, between min and max
// ; For any tick, n divisions below max, the tick is an ordinal corresponding to:
// ; (a) a power of 10 between n and (n + ε) divisions below max where ε is some tolerance, or
// ; (b) a value, n divisions below max
let sub_range = Math.round((max - min) / (1 + number))
let near = (x, n) => (x <= n) && (Math.abs((x - n) / sub_range) <= .2) // <= tolerance
return [...Array(number)].map((_, i) => i + 1).map(itr => {
let power10 = first_power10(
ordinal_to_real(clamp(max - ((itr + 1) * sub_range), min, max)),
ordinal_to_real(clamp(max - (itr * sub_range), min, max))
)
return power10 && near(real_to_ordinal(power10), max - (itr * sub_range)) ? real_to_ordinal(power10)
: max - (itr * sub_range)
})
}
function pick_spaced_ordinals(necessary, min, max, number) {
// NOTE use of mathjs bignumber arithmetic is required in this function!
let sub_range = math.divide(math.bignumber(math.subtract(math.bignumber(max), math.bignumber(min))), math.bignumber(number)) // size of a division on the ordinal range
let necessary_star = (function loop(necessary) {
return necessary.length < 2 ? necessary
: math.smaller(math.subtract(necessary[1], necessary[0]), sub_range) ? loop(necessary.slice(1))
: [necessary[0], ...loop(necessary.slice(1))]
})(necessary) // filter out necessary points that are too close
let all = (function loop(necessary, min_star, start) {
if (start >= number) { return [] }
if (necessary.length == 0) { return choose_between(min_star, max, number - start) }
let idx = false
for (let i=0; i<number; i++) {
if (math.smallerEq(math.subtract(necessary[0], math.add(min, math.bignumber(math.multiply(i, sub_range)))), sub_range)) {
idx = i
break
}
}
return [...choose_between(min_star, necessary[0], idx - start), ...loop(necessary.slice(1), necessary[0], idx + 1)]
})(necessary_star, min, 0)
return [...all, ...necessary_star].sort((a, b) => math.subtract(math.bignumber(a), math.bignumber(b)))
}
function choose_ticks (min, max) {
let tick_count = 13
let necessary = [min, -1.0, 0, 1.0, max].filter(v => (min <= v) && (v <= max) && (min <= max)).map(v => real_to_ordinal(v))
let major_ticks = pick_spaced_ordinals(necessary, real_to_ordinal(min), real_to_ordinal(max), tick_count).map(v => ordinal_to_real(v))
return major_ticks
}
return choose_ticks(min, max)
}
Insert cell
choose_ticks_clientside(-1.79e308, 1.79e308)
Insert cell
1 < 4 < 3
Insert cell
ordinal_to_real("-4607182418800017408")
Insert cell
real_to_ordinal(0).toString()
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more