UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

334 lines 13 kB
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