@bokeh/bokehjs
Version:
Interactive, novel data visualization
334 lines • 13 kB
JavaScript
import { BaseBar, BaseBarView } from "./base_bar";
import { RadialGlyph } from "../glyphs/radial_glyph";
import { GlyphRenderer } from "../renderers/glyph_renderer";
import { exportable } from "../../core/util/canvas";
import { LinearScale } from "../scales";
import { Range1d } from "../ranges/range1d";
import { LinearAxis } from "../axes/linear_axis";
import * as mixins from "../../core/property_mixins";
import * as uniforms from "../../core/uniforms";
import { ColumnDataSource } from "../sources/column_data_source";
import { isString } from "../../core/util/types";
import { Title } from "../annotations/title";
import { Plot, PlotView } from "../plots/plot";
import { BasicTickFormatter } from "../formatters/basic_tick_formatter";
import { AdaptiveTicker } from "../tickers/adaptive_ticker";
import { FixedTicker } from "../tickers/fixed_ticker";
import { repeat, elementwise } from "../../core/util/array";
import { logger } from "../../core/logging";
import { Circle } from "../glyphs/circle";
import { BBox } from "../../core/util/bbox";
import { BorderLayout } from "../../core/layout/border";
class InternalBorderLayout extends BorderLayout {
static __name__ = "InternalBorderLayout";
offset_position = { x: 0, y: 0 };
set_geometry(viewport) {
const { outer, inner } = this._compute(viewport);
this.offset_position = { x: viewport.x, y: viewport.y };
super.set_geometry(outer, inner);
}
}
class InternalPlotView extends PlotView {
static __name__ = "InternalPlotView";
initialize() {
super.initialize();
this._range_manager.warn_initial_ranges = false;
}
_make_layout() {
return new InternalBorderLayout();
}
_after_resize() { }
}
class InternalPlot extends Plot {
static __name__ = "InternalPlot";
constructor(attrs) {
super(attrs);
}
static {
this.prototype.default_view = InternalPlotView;
}
}
export class SizeBarView extends BaseBarView {
static __name__ = "SizeBarView";
_major_range;
_major_scale;
_minor_range;
_minor_scale;
_size_bar;
_size_bar_view;
_data_source;
_major_axis;
_major_ticker;
_major_formatter;
get align() {
const { location } = this.model;
if (isString(location)) {
switch (location) {
case "top_left": return { v: "start", h: "start" };
case "top":
case "top_center": return { v: "start", h: "center" };
case "top_right": return { v: "start", h: "end" };
case "bottom_left": return { v: "end", h: "start" };
case "bottom":
case "bottom_center": return { v: "end", h: "center" };
case "bottom_right": return { v: "end", h: "end" };
case "left":
case "center_left": return { v: "center", h: "start" };
case "center":
case "center_center": return { v: "center", h: "center" };
case "right":
case "center_right": return { v: "center", h: "end" };
}
}
else {
return { v: "end", h: "start" };
}
}
get orientation() {
const { orientation } = this.model;
const { align } = this;
if (orientation == "auto") {
if (this.panel != null) {
return this.panel.is_horizontal ? "horizontal" : "vertical";
}
else {
if (align.h == "start" || align.h == "end" || ( /*align.h == "center" &&*/align.v == "center")) {
return "vertical";
}
else {
return "horizontal";
}
}
}
else {
return orientation;
}
}
initialize() {
super.initialize();
const { orientation } = this;
this._major_range = new Range1d();
this._major_scale = new LinearScale();
this._minor_range = new Range1d();
this._minor_scale = new LinearScale();
const renderer = this.renderer ?? new GlyphRenderer({ glyph: new Circle() });
const Cls = renderer.glyph.constructor; // expression not constructible
const glyph = new Cls({
x: { field: "x" },
y: { field: "y" },
radius: { field: "s", units: "screen" },
...mixins.attrs_of(this.model, "glyph_", mixins.LineVector),
...mixins.attrs_of(this.model, "glyph_", mixins.FillVector),
...mixins.attrs_of(this.model, "glyph_", mixins.HatchVector),
});
this._data_source = new ColumnDataSource({
data: {
x: [],
y: [],
s: [],
},
});
const circle_renderer = new GlyphRenderer({ data_source: this._data_source, glyph });
const { ticker, formatter } = this.model;
this._major_ticker = ticker != "auto" ? ticker : new FixedTicker({ ticks: [] });
this._major_formatter = formatter != "auto" ? formatter : new BasicTickFormatter();
this._major_axis = new LinearAxis({
ticker: this._major_ticker,
formatter: this._major_formatter,
axis_line_color: null,
major_label_standoff: this.model.label_standoff,
major_tick_in: this.model.major_tick_in,
major_tick_out: this.model.major_tick_out,
minor_tick_in: this.model.minor_tick_in,
minor_tick_out: this.model.minor_tick_out,
major_label_overrides: this.model.major_label_overrides,
major_label_policy: this.model.major_label_policy,
...mixins.attrs_of(this.model, "major_label_", mixins.Text, true),
...mixins.attrs_of(this.model, "major_tick_", mixins.Line, true),
...mixins.attrs_of(this.model, "minor_tick_", mixins.Line, true),
});
const { width, height } = this.model;
const title = new Title({
text: this.model.title ?? undefined,
standoff: this.model.title_standoff,
...mixins.attrs_of(this.model, "title_", mixins.Text, false),
});
const plot_attrs = {
renderers: [circle_renderer],
toolbar_location: null,
title,
...mixins.attrs_of(this.model, "background_", mixins.Fill, true),
...mixins.attrs_of(this.model, "background_", mixins.Hatch, true),
...mixins.attrs_of(this.model, "border_", mixins.Line, true),
...mixins.attrs_of(this.model, "bar_", mixins.Line, "outline_"),
};
switch (orientation) {
case "horizontal": {
this._size_bar = new InternalPlot({
width_policy: width == "max" ? "max" : "fit",
height_policy: height == "max" ? "max" : "fit",
frame_width: width == "max" ? undefined : width,
frame_height: height == "max" ? undefined : height,
below: [this._major_axis],
x_range: this._major_range,
y_range: this._minor_range,
x_scale: this._major_scale,
y_scale: this._minor_scale,
...plot_attrs,
});
break;
}
case "vertical": {
this._size_bar = new InternalPlot({
width_policy: height == "max" ? "max" : "fit",
height_policy: width == "max" ? "max" : "fit",
frame_width: height == "max" ? undefined : height,
frame_height: width == "max" ? undefined : width,
right: [this._major_axis],
x_range: this._minor_range,
y_range: this._major_range,
x_scale: this._minor_scale,
y_scale: this._major_scale,
...plot_attrs,
});
break;
}
}
}
get computed_elements() {
return [...super.computed_elements, this._size_bar];
}
async lazy_initialize() {
await super.lazy_initialize();
this._size_bar_view = this._element_views.get(this._size_bar);
}
_last_bbox = new BBox();
update_layout() {
this.layout = this._size_bar_view.layout;
this.layout.on_resize((outer) => {
if (!outer.equals(this._last_bbox)) {
this._last_bbox = outer;
this.parent.request_layout(true);
}
});
}
get renderer() {
const { renderer } = this.model;
if (renderer == "auto") {
const renderers = this.plot_view.model.renderers.filter((r) => {
return r instanceof GlyphRenderer && r.glyph instanceof RadialGlyph;
});
switch (renderers.length) {
case 0: {
logger.warn("can't find any radial glyph renderers");
return null;
}
case 1: {
return renderers[0];
}
default: {
logger.warn("found multiple radial glyph renderers; choosing the first one");
return renderers[0];
}
}
}
else {
return renderer;
}
}
get glyph_view() {
const { renderer } = this;
if (renderer == null) {
return null;
}
else {
const rv = this.plot_view.views.get_one(renderer);
return rv.glyph_view;
}
}
_paint(_ctx) {
const { glyph_view } = this;
if (glyph_view == null) {
return;
}
const bounds = (() => {
const { bounds } = this.model;
return bounds == "auto" ? [-Infinity, Infinity] : bounds;
})();
const r_min = Math.max(uniforms.min(glyph_view.radius), bounds[0]);
const r_max = Math.min(uniforms.max(glyph_view.radius), bounds[1]);
const equal = r_max == r_min;
const eps = 0.000001;
const start = equal ? Math.max(r_min - eps, 0) : r_min;
const end = equal ? r_max + eps : r_max;
const n_ticks = equal ? 1 : 5;
const t = new AdaptiveTicker({ desired_num_ticks: n_ticks });
const ticks = t.get_ticks(start == 0 ? end * eps : start, end, new Range1d(), NaN);
const radii = ticks.major;
if (this.model.ticker == "auto" && this._major_ticker instanceof FixedTicker) {
this._major_ticker.ticks = radii;
}
const x = radii;
const y = repeat(0, x.length);
const s = (() => {
if (glyph_view.model.properties.radius.units == "data") {
const sradius_x = () => glyph_view.sdist(glyph_view.renderer.xscale, repeat(0, radii.length), new uniforms.UniformVector(radii));
const sradius_y = () => glyph_view.sdist(glyph_view.renderer.yscale, repeat(0, radii.length), new uniforms.UniformVector(radii));
const { radius_dimension } = glyph_view.model;
switch (radius_dimension) {
case "x": {
return sradius_x();
}
case "y": {
return sradius_y();
}
case "min":
case "max": {
return elementwise(sradius_x(), sradius_y(), Math[radius_dimension]);
}
}
}
else {
return radii;
}
})();
this._major_range.setv({ start, end });
this._minor_range.setv({ start: -end, end });
this._data_source.data = (() => {
switch (this.orientation) {
case "horizontal": return { x, y, s };
case "vertical": return { x: y, y: x, s };
}
})();
}
[exportable] = true;
export(type = "auto", hidpi = true) {
return this._size_bar_view.export(type, hidpi);
}
get bbox() {
const { bbox, offset_position: { x, y } } = this.layout;
return bbox.translate(x, y);
}
}
export class SizeBar extends BaseBar {
static __name__ = "SizeBar";
constructor(attrs) {
super(attrs);
}
static {
this.prototype.default_view = SizeBarView;
this.mixins([
["glyph_", mixins.LineVector],
["glyph_", mixins.FillVector],
["glyph_", mixins.HatchVector],
]);
this.override({
glyph_line_color: null,
});
this.define(({ Ref, Auto, Or, Float, Tuple }) => ({
renderer: [Or(Ref(GlyphRenderer), Auto), "auto"],
bounds: [Or(Tuple(Float, Float), Auto), "auto"],
}));
}
}
//# sourceMappingURL=size_bar.js.map