@bokeh/bokehjs
Version:
Interactive, novel data visualization
57 lines • 2.57 kB
JavaScript
import { ContinuousTicker } from "./continuous_ticker";
import { argmin, nth } from "../../core/util/array";
import { clamp, log } from "../../core/util/math";
export class AdaptiveTicker extends ContinuousTicker {
static __name__ = "AdaptiveTicker";
constructor(attrs) {
super(attrs);
}
static {
this.define(({ Float, List, Nullable }) => ({
base: [Float, 10.0],
mantissas: [List(Float), [1, 2, 5]],
min_interval: [Float, 0.0],
max_interval: [Nullable(Float), null],
}));
}
get_min_interval() {
return this.min_interval;
}
get_max_interval() {
return this.max_interval ?? Infinity;
}
// These arguments control the range of possible intervals. The interval I
// returned by get_interval() will be the one that most closely matches the
// desired number of ticks, subject to the following constraints:
// I = (M * B^N), where
// M is a member of mantissas,
// B is base,
// and N is an integer;
// and min_interval <= I <= max_interval.
get extended_mantissas() {
const prefix_mantissa = nth(this.mantissas, -1) / this.base;
const suffix_mantissa = nth(this.mantissas, 0) * this.base;
return [prefix_mantissa, ...this.mantissas, suffix_mantissa];
}
get base_factor() {
return this.get_min_interval() == 0.0 ? 1.0 : this.get_min_interval();
}
get_interval(data_low, data_high, desired_n_ticks) {
const data_range = data_high - data_low;
const ideal_interval = this.get_ideal_interval(data_low, data_high, desired_n_ticks);
const interval_exponent = Math.floor(log(ideal_interval / this.base_factor, this.base));
const ideal_magnitude = this.base ** interval_exponent * this.base_factor;
// An untested optimization.
// const ideal_mantissa = ideal_interval / ideal_magnitude
// index = sorted_index(this.extended_mantissas, ideal_mantissa)
// candidate_mantissas = this.extended_mantissas[index..index + 1]
const candidate_mantissas = this.extended_mantissas;
const errors = candidate_mantissas.map((mantissa) => {
return Math.abs(desired_n_ticks - (data_range / (mantissa * ideal_magnitude)));
});
const best_mantissa = candidate_mantissas[argmin(errors)];
const interval = best_mantissa * ideal_magnitude;
return clamp(interval, this.get_min_interval(), this.get_max_interval());
}
}
//# sourceMappingURL=adaptive_ticker.js.map