UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

201 lines 6.46 kB
import { Model } from "../../model"; import { assert } from "../../core/util/assert"; import { entries } from "../../core/util/object"; import { bisect_right, bisect_right_by, sort_by } from "../../core/util/arrayable"; const { min } = Math; export class Dimensional extends Model { static __name__ = "Dimensional"; constructor(attrs) { super(attrs); } static { this.define(({ Nullable, List, Str, Float }) => ({ ticks: [List(Float)], include: [Nullable(List(Str)), null], exclude: [List(Str), []], })); } compute(value, unit, exact) { const basis = (() => { const { include, exclude } = this; const basis = entries(this.get_basis()) .map(([name, [factor, tex_name, long_name]]) => ({ name, factor, tex_name, long_name })) .filter(({ name }) => (include == null || include.includes(name)) && !exclude.includes(name)); return sort_by(basis, ({ factor }) => factor); })(); const { ticks } = this; const found_unit = basis.find(({ name }) => name == unit); assert(found_unit != null); const value_in_unit = value * found_unit.factor; const [new_unit, new_value] = (() => { const index = bisect_right_by(basis, value_in_unit, ({ factor }) => factor); if (index > 0) { const { name: new_unit, factor } = basis[index - 1]; const new_value = value_in_unit / factor; return [new_unit, new_value]; } else { return [unit, value_in_unit]; } })(); exact = exact ?? ticks.length == 0; const preferred_value = (() => { if (exact) { return new_value; } else { const index = bisect_right(ticks, new_value); return ticks[min(index, ticks.length - 1)]; } })(); const preferred_value_raw = preferred_value * (value_in_unit / new_value); const scale_factor = (preferred_value_raw / value) / found_unit.factor; return { new_value: preferred_value, new_unit, scale_factor, exact, }; } } export class CustomDimensional extends Dimensional { static __name__ = "CustomDimensional"; constructor(attrs) { super(attrs); } static { this.define(({ Dict, Tuple, Float, Str, Or }) => ({ basis: [Dict(Or(Tuple(Float, Str), Tuple(Float, Str, Str)))], })); } get_basis() { return this.basis; } } export class Metric extends Dimensional { static __name__ = "Metric"; constructor(attrs) { super(attrs); } static { this.define(({ Str, Nullable }) => ({ base_unit: [Str], full_unit: [Nullable(Str), null], })); this.override({ ticks: [1, 2, 5, 10, 15, 20, 25, 50, 75, 100, 125, 150, 200, 250, 500, 750], }); } static _metric_basis = [ ["Q", 1e30, "Q", "quetta"], ["R", 1e27, "R", "ronna"], ["Y", 1e24, "Y", "yotta"], ["Z", 1e21, "Z", "zetta"], ["E", 1e18, "E", "exa"], ["P", 1e15, "P", "peta"], ["T", 1e12, "T", "tera"], ["G", 1e9, "G", "giga"], ["M", 1e6, "M", "mega"], ["k", 1e3, "k", "kilo"], ["h", 1e2, "h", "hecto"], ["", 1e0, "", ""], ["d", 1e-1, "d", "deci"], ["c", 1e-2, "c", "centi"], ["m", 1e-3, "m", "milli"], ["µ", 1e-6, "\\mu", "micro"], ["n", 1e-9, "n", "nano"], ["p", 1e-12, "p", "pico"], ["f", 1e-15, "f", "femto"], ["a", 1e-18, "a", "atto"], ["z", 1e-21, "z", "zepto"], ["y", 1e-24, "y", "yocto"], ["r", 1e-27, "r", "ronto"], ["q", 1e-30, "q", "quecto"], ]; get_basis() { const { base_unit, full_unit } = this; const basis = {}; for (const [prefix, factor, tex_prefix, long_prefix] of Metric._metric_basis) { const name = `${prefix}${base_unit}`; const tex_name = `${tex_prefix}${base_unit}`; const long_name = full_unit != null ? `${long_prefix}${full_unit}` : undefined; basis[name] = [factor, tex_name, long_name]; } return basis; } } export class ReciprocalMetric extends Metric { static __name__ = "ReciprocalMetric"; constructor(attrs) { super(attrs); } get_basis() { const basis = super.get_basis(); const reciprocal_basis = {}; for (const [name, [factor, tex_name]] of entries(basis)) { reciprocal_basis[`${name}⁻1`] = [factor ** -1, `${tex_name}^{-1}`]; } return reciprocal_basis; } } export class MetricLength extends Metric { static __name__ = "MetricLength"; constructor(attrs) { super(attrs); } static { this.override({ base_unit: "m", exclude: ["dm", "hm"], }); } } export class ReciprocalMetricLength extends ReciprocalMetric { static __name__ = "ReciprocalMetricLength"; constructor(attrs) { super(attrs); } static { this.override({ base_unit: "m", exclude: ["dm", "hm"], }); } } export class ImperialLength extends CustomDimensional { static __name__ = "ImperialLength"; constructor(attrs) { super(attrs); } static { this.override({ basis: { in: [1 / 12, "in", "inch"], ft: [1, "ft", "foot"], yd: [3, "yd", "yard"], ch: [66, "ch", "chain"], fur: [660, "fur", "furlong"], mi: [5280, "mi", "mile"], lea: [15840, "lea", "league"], }, ticks: [1, 3, 6, 12, 60], }); } } export class Angular extends CustomDimensional { static __name__ = "Angular"; constructor(attrs) { super(attrs); } static { this.override({ basis: { "°": [1, "^\\circ", "degree"], "'": [1 / 60, "^\\prime", "minute"], "''": [1 / 3600, "^{\\prime\\prime}", "second"], }, ticks: [1, 3, 6, 12, 60, 120, 240, 360], }); } } //# sourceMappingURL=dimensional.js.map