UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

724 lines 22.3 kB
import { Signal0 } from "./signaling"; import { logger } from "./logging"; import * as enums from "./enums"; import { RGBAArray, ColorArray } from "./types"; import { includes, repeat } from "./util/array"; import { mul } from "./util/arrayable"; import { to_radians_coeff } from "./util/math"; import { color2rgba, encode_rgba } from "./util/color"; import { to_big_endian } from "./util/platform"; import { isNumber, isTypedArray, isPlainObject } from "./util/types"; import { isValue, isField, isExpr } from "./vectorization"; import { settings } from "./settings"; import { PrefixedStr } from "./kinds"; import { is_NDArray } from "./util/ndarray"; import { diagnostics } from "./diagnostics"; import { unreachable } from "./util/assert"; import { serialize } from "./serialization"; import { Uniform, UniformScalar, UniformVector, ColorUniformVector } from "./uniforms"; export { Uniform, UniformScalar, UniformVector }; function valueToString(value) { try { return JSON.stringify(value); } catch { return value.toString(); } } export function isSpec(obj) { return isPlainObject(obj) && ((obj.value === undefined ? 0 : 1) + (obj.field === undefined ? 0 : 1) + (obj.expr === undefined ? 0 : 1) == 1); // garbage JS XOR } let global_theme = null; export function use_theme(theme = null) { global_theme = theme; } export const unset = Symbol("unset"); export class UnsetValueError extends Error { static __name__ = "UnsetValueError"; } export class Property { obj; attr; kind; default_value; static __name__ = "Property"; __value__; get syncable() { return !this.internal; } _value = unset; get is_unset() { return this._value === unset; } _initialized = false; get initialized() { return this._initialized; } initialize(initial_value = unset) { if (this._initialized) { throw new Error("already initialized"); } let attr_value = unset; if (initial_value !== unset) { attr_value = initial_value; this._dirty = true; } else { const value = this._default_override(); if (value !== unset) { attr_value = value; } else { let themed = false; if (global_theme != null) { const value = global_theme.get(this.obj, this.attr); if (value !== undefined) { attr_value = value; themed = true; } } if (!themed) { attr_value = this.default_value(this.obj); } } } if (attr_value !== unset) { if (this.kind.coerce != null) { attr_value = this.kind.coerce(attr_value); } this._update(attr_value); } else { this._value = unset; } this._initialized = true; } get_value() { if (this._value !== unset) { return this._value; } else { throw new UnsetValueError(`${this.obj}.${this.attr} is unset`); } } set_value(val) { if (!this._initialized) { this.initialize(val); } else { this._update(val); this._dirty = true; } diagnostics.report(this); } // abstract _intrinsic_default(): T _default_override() { return unset; } _dirty = false; get dirty() { return this._dirty; } may_have_refs; change; /*readonly*/ internal; readonly; constructor(obj, attr, kind, default_value, options = {}) { this.obj = obj; this.attr = attr; this.kind = kind; this.default_value = default_value; this.change = new Signal0(this.obj, "change"); this.internal = options.internal ?? false; this.readonly = options.readonly ?? false; this.convert = options.convert; this.on_update = options.on_update; this.may_have_refs = kind.may_have_refs(); } //protected abstract _update(attr_value: T): void _update(attr_value) { this.validate(attr_value); if (this.convert != null) { const converted = this.convert(attr_value, this.obj); if (converted !== undefined) { attr_value = converted; } } this._value = attr_value; this.on_update?.(attr_value, this.obj); } toString() { /*${this.name}*/ return `Prop(${this.obj}.${this.attr}, value: ${valueToString(this._value)})`; } // ----- customizable policies normalize(values) { return values; } validate(value) { if (!this.valid(value)) { throw new Error(`${this.obj}.${this.attr} given invalid value: ${valueToString(value)}`); } } valid(value) { return this.kind.valid(value); } } export class PropertyAlias { attr; static __name__ = "PropertyAlias"; constructor(attr) { this.attr = attr; } } export function Alias(attr) { return new PropertyAlias(attr); } // // Primitive Properties // export class PrimitiveProperty extends Property { static __name__ = "PrimitiveProperty"; } export class Font extends PrimitiveProperty { static __name__ = "Font"; _default_override() { return settings.dev ? "Bokeh" : unset; } } // // DataSpec properties // export class ScalarSpec extends Property { static __name__ = "ScalarSpec"; __scalar__; _value = unset; get_value() { if (this._value !== unset) { return this._value; } else { throw new Error(`${this.obj}.${this.attr} is unset`); } } _update(attr_value) { if (isSpec(attr_value)) { this._value = attr_value; } else { this._value = { value: attr_value }; // Value<T> } if (isPlainObject(this._value)) { const { _value } = this; this._value[serialize] = (serializer) => { const { value, field, expr, transform, units } = _value; return serializer.encode_struct((() => { if (value !== undefined) { return { type: "value", value, transform, units }; } else if (field !== undefined) { return { type: "field", field, transform, units }; } else { return { type: "expr", expr, transform, units }; } })()); }; } if (isValue(this._value)) { this.validate(this._value.value); } } materialize(value) { return value; } scalar(value, n) { return new UniformScalar(value, n); } uniform(source) { const obj = this.get_value(); const n = source.get_length() ?? 1; if (isExpr(obj)) { const { expr, transform } = obj; let result = expr.compute(source); if (transform != null) { result = transform.compute(result); } result = this.materialize(result); return this.scalar(result, n); } else { const { value, transform } = obj; let result = value; if (transform != null) { result = transform.compute(result); } result = this.materialize(result); return this.scalar(result, n); } } } /** @deprecated */ export class AnyScalar extends ScalarSpec { static __name__ = "AnyScalar"; } export class DictScalar extends ScalarSpec { static __name__ = "DictScalar"; } export class ColorScalar extends ScalarSpec { static __name__ = "ColorScalar"; } export class NumberScalar extends ScalarSpec { static __name__ = "NumberScalar"; } export class StringScalar extends ScalarSpec { static __name__ = "StringScalar"; } export class NullStringScalar extends ScalarSpec { static __name__ = "NullStringScalar"; } export class ArrayScalar extends ScalarSpec { static __name__ = "ArrayScalar"; } export class LineJoinScalar extends ScalarSpec { static __name__ = "LineJoinScalar"; } export class LineCapScalar extends ScalarSpec { static __name__ = "LineCapScalar"; } export class LineDashScalar extends ScalarSpec { static __name__ = "LineDashScalar"; } export class FontScalar extends ScalarSpec { static __name__ = "FontScalar"; _default_override() { return settings.dev ? "Bokeh" : unset; } } export class FontSizeScalar extends ScalarSpec { static __name__ = "FontSizeScalar"; } export class FontStyleScalar extends ScalarSpec { static __name__ = "FontStyleScalar"; } export class TextAlignScalar extends ScalarSpec { static __name__ = "TextAlignScalar"; } export class TextBaselineScalar extends ScalarSpec { static __name__ = "TextBaselineScalar"; } export class VectorSpec extends Property { static __name__ = "VectorSpec"; __vector__; _value = unset; get_value() { if (this._value !== unset) { return this._value; } else { throw new Error(`${this.obj}.${this.attr} is unset`); } } _update(attr_value) { if (isSpec(attr_value)) { this._value = attr_value; } else { this._value = { value: attr_value }; } // Value<T> if (isPlainObject(this._value)) { const { _value } = this; this._value[serialize] = (serializer) => { const { value, field, expr, transform, units } = _value; return serializer.encode_struct((() => { if (value !== undefined) { return { type: "value", value, transform, units }; } else if (field !== undefined) { return { type: "field", field, transform, units }; } else { return { type: "expr", expr, transform, units }; } })()); }; } if (isValue(this._value)) { this.validate(this._value.value); } } materialize(value) { return value; } v_materialize(values) { return values; } scalar(value, n) { return new UniformScalar(value, n); } vector(values) { return new UniformVector(values); } uniform(source) { const obj = this.get_value(); const n = source.get_length() ?? 1; if (isField(obj)) { const { field, transform } = obj; let array = source.get_column(field); if (array != null) { if (transform != null) { array = transform.v_compute(array); } array = this.v_materialize(array); return this.vector(array); } else { const message = `attempted to retrieve property array for nonexistent field '${field}'`; if (settings.force_fields) { throw new Error(message); } else { logger.warn(message); } return this.scalar(null, n); } } else if (isExpr(obj)) { const { expr, transform } = obj; let array = expr.v_compute(source); if (transform != null) { array = transform.v_compute(array); } array = this.v_materialize(array); return this.vector(array); } else if (isValue(obj)) { const { value, transform } = obj; let result = value; if (transform != null) { result = transform.compute(result); } result = this.materialize(result); return this.scalar(result, n); } else { unreachable(); } } array(source) { let array; const length = source.get_length() ?? 1; const obj = this.get_value(); if (isField(obj)) { const { field } = obj; const column = source.get_column(field); if (column != null) { array = this.normalize(column); } else { const message = `attempted to retrieve property array for nonexistent field '${field}'`; if (settings.force_fields) { throw new Error(message); } else { logger.warn(message); } const missing = new Float64Array(length); missing.fill(NaN); array = missing; } } else if (isExpr(obj)) { const { expr } = obj; array = this.normalize(expr.v_compute(source)); } else { const value = this.normalize([obj.value])[0]; if (isNumber(value)) { const values = new Float64Array(length); values.fill(value); array = values; } else { array = repeat(value, length); } } const { transform } = obj; if (transform != null) { array = transform.v_compute(array); } return array; } } export class DataSpec extends VectorSpec { static __name__ = "DataSpec"; } export class UnitsSpec extends VectorSpec { static __name__ = "UnitsSpec"; _value = unset; _update(attr_value) { super._update(attr_value); if (this._value !== unset) { const { units } = this._value; if (units != null && !includes(this.valid_units, units)) { throw new Error(`units must be one of ${this.valid_units.join(", ")}; got: ${units}`); } } } get units() { return this._value !== unset ? this._value.units ?? this.default_units : this.default_units; } set units(units) { if (this._value !== unset) { if (units != this.default_units) { this._value.units = units; } else { delete this._value.units; } } else { throw new Error(`${this.obj}.${this.attr} is unset`); } } } export class NumberUnitsSpec extends UnitsSpec { static __name__ = "NumberUnitsSpec"; array(source) { return new Float64Array(super.array(source)); } } export class BaseCoordinateSpec extends DataSpec { static __name__ = "BaseCoordinateSpec"; } export class CoordinateSpec extends BaseCoordinateSpec { static __name__ = "CoordinateSpec"; } export class CoordinateSeqSpec extends BaseCoordinateSpec { static __name__ = "CoordinateSeqSpec"; } export class CoordinateSeqSeqSeqSpec extends BaseCoordinateSpec { static __name__ = "CoordinateSeqSeqSeqSpec"; } export class XCoordinateSpec extends CoordinateSpec { static __name__ = "XCoordinateSpec"; dimension = "x"; } export class YCoordinateSpec extends CoordinateSpec { static __name__ = "YCoordinateSpec"; dimension = "y"; } export class XCoordinateSeqSpec extends CoordinateSeqSpec { static __name__ = "XCoordinateSeqSpec"; dimension = "x"; } export class YCoordinateSeqSpec extends CoordinateSeqSpec { static __name__ = "YCoordinateSeqSpec"; dimension = "y"; } export class XCoordinateSeqSeqSeqSpec extends CoordinateSeqSeqSeqSpec { static __name__ = "XCoordinateSeqSeqSeqSpec"; dimension = "x"; } export class YCoordinateSeqSeqSeqSpec extends CoordinateSeqSeqSeqSpec { static __name__ = "YCoordinateSeqSeqSeqSpec"; dimension = "y"; } export class AngleSpec extends NumberUnitsSpec { static __name__ = "AngleSpec"; get default_units() { return "rad"; } get valid_units() { return [...enums.AngleUnits]; } materialize(value) { const coeff = -to_radians_coeff(this.units); return value * coeff; } v_materialize(values) { const coeff = -to_radians_coeff(this.units); const result = new Float32Array(values.length); mul(values, coeff, result); // TODO: in-place? return result; } array(_source) { throw new Error("not supported"); } } export class DistanceSpec extends NumberUnitsSpec { static __name__ = "DistanceSpec"; get default_units() { return "data"; } get valid_units() { return [...enums.SpatialUnits]; } } export class NullDistanceSpec extends DistanceSpec { static __name__ = "NullDistanceSpec"; materialize(value) { return value ?? NaN; } } export class BooleanSpec extends DataSpec { static __name__ = "BooleanSpec"; v_materialize(values) { return new Uint8Array(values); } array(source) { return new Uint8Array(super.array(source)); } } export class IntSpec extends DataSpec { static __name__ = "IntSpec"; v_materialize(values) { return isTypedArray(values) ? values : new Int32Array(values); } array(source) { return new Int32Array(super.array(source)); } } export class NumberSpec extends DataSpec { static __name__ = "NumberSpec"; v_materialize(values) { return isTypedArray(values) ? values : new Float64Array(values); } array(source) { return new Float64Array(super.array(source)); } } export class ScreenSizeSpec extends NumberSpec { static __name__ = "ScreenSizeSpec"; valid(value) { return isNumber(value) && value >= 0; } } export class ColorSpec extends DataSpec { static __name__ = "ColorSpec"; materialize(color) { return encode_rgba(color2rgba(color)); } v_materialize(colors) { if (is_NDArray(colors)) { if (colors.dtype == "uint32" && colors.dimension == 1) { return to_big_endian(colors); } else if (colors.dtype == "uint8" && colors.dimension == 1) { const [n] = colors.shape; const array = new RGBAArray(4 * n); let j = 0; for (const gray of colors) { array[j++] = gray; array[j++] = gray; array[j++] = gray; array[j++] = 255; } return new ColorArray(array.buffer); } else if (colors.dtype == "uint8" && colors.dimension == 2) { const [n, d] = colors.shape; if (d == 4) { return new ColorArray(colors.buffer); } else if (d == 3) { const array = new RGBAArray(4 * n); for (let i = 0, j = 0; i < d * n;) { array[j++] = colors[i++]; array[j++] = colors[i++]; array[j++] = colors[i++]; array[j++] = 255; } return new ColorArray(array.buffer); } } else if ((colors.dtype == "float32" || colors.dtype == "float64") && colors.dimension == 2) { const [n, d] = colors.shape; if (d == 3 || d == 4) { const array = new RGBAArray(4 * n); for (let i = 0, j = 0; i < d * n;) { array[j++] = colors[i++] * 255; array[j++] = colors[i++] * 255; array[j++] = colors[i++] * 255; array[j++] = (d == 3 ? 1 : colors[i++]) * 255; } return new ColorArray(array.buffer); } } else if (colors.dtype == "object" && colors.dimension == 1) { return this._from_css_array(colors); } } else { return this._from_css_array(colors); } throw new Error("invalid color array"); } _from_css_array(colors) { const n = colors.length; const array = new RGBAArray(4 * n); let j = 0; for (const color of colors) { const [r, g, b, a] = color2rgba(color); array[j++] = r; array[j++] = g; array[j++] = b; array[j++] = a; } return new ColorArray(array.buffer); } vector(values) { return new ColorUniformVector(values); } } export class NDArraySpec extends DataSpec { static __name__ = "NDArraySpec"; } /** @deprecated */ export class AnySpec extends DataSpec { static __name__ = "AnySpec"; } export class StringSpec extends DataSpec { static __name__ = "StringSpec"; } export class NullStringSpec extends DataSpec { static __name__ = "NullStringSpec"; } export class ArraySpec extends DataSpec { static __name__ = "ArraySpec"; } export const ExtMarkerType = PrefixedStr("@"); export class MarkerSpec extends DataSpec { static __name__ = "MarkerSpec"; } export class LineJoinSpec extends DataSpec { static __name__ = "LineJoinSpec"; } export class LineCapSpec extends DataSpec { static __name__ = "LineCapSpec"; } export class LineDashSpec extends DataSpec { static __name__ = "LineDashSpec"; } export class FontSpec extends DataSpec { static __name__ = "FontSpec"; _default_override() { return settings.dev ? "Bokeh" : unset; } } export class FontSizeSpec extends DataSpec { static __name__ = "FontSizeSpec"; } export class FontStyleSpec extends DataSpec { static __name__ = "FontStyleSpec"; } export class TextAlignSpec extends DataSpec { static __name__ = "TextAlignSpec"; } export class TextBaselineSpec extends DataSpec { static __name__ = "TextBaselineSpec"; } //# sourceMappingURL=properties.js.map