UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

140 lines 5.57 kB
import { XYGlyph, XYGlyphView } from "./xy_glyph"; import { LineVector, FillVector, HatchVector } from "../../core/property_mixins"; import * as hittest from "../../core/hittest"; import * as p from "../../core/properties"; import { minmax2 } from "../../core/util/arrayable"; import { Selection } from "../selections/selection"; export class MarkerView extends XYGlyphView { static __name__ = "MarkerView"; _render_one; _paint(ctx, indices, data) { const { sx, sy, size, angle } = { ...this, ...data }; for (const i of indices) { const sx_i = sx[i]; const sy_i = sy[i]; const size_i = size.get(i); const angle_i = angle.get(i); if (!isFinite(sx_i + sy_i + size_i + angle_i)) { continue; } const r = size_i / 2; ctx.beginPath(); ctx.translate(sx_i, sy_i); if (angle_i != 0) { ctx.rotate(angle_i); } this._render_one(ctx, i, r, this.visuals); if (angle_i != 0) { ctx.rotate(-angle_i); } ctx.translate(-sx_i, -sy_i); } } _mask_data() { // dilate the inner screen region by max_size and map back to data space for use in spatial query const { x_target, y_target } = this.renderer.plot_view.frame; const hr = x_target.widen(this.max_size).map((x) => this.renderer.xscale.invert(x)); const vr = y_target.widen(this.max_size).map((y) => this.renderer.yscale.invert(y)); return this.index.indices({ x0: hr.start, x1: hr.end, y0: vr.start, y1: vr.end, }); } _hit_point(geometry) { const { sx, sy } = geometry; const { max_size } = this; const { hit_dilation } = this.model; const sx0 = sx - max_size * hit_dilation; const sx1 = sx + max_size * hit_dilation; const [x0, x1] = this.renderer.xscale.r_invert(sx0, sx1); const sy0 = sy - max_size * hit_dilation; const sy1 = sy + max_size * hit_dilation; const [y0, y1] = this.renderer.yscale.r_invert(sy0, sy1); const candidates = this.index.indices({ x0, x1, y0, y1 }); const indices = []; for (const i of candidates) { const s2 = this.size.get(i) / 2 * hit_dilation; if (Math.abs(this.sx[i] - sx) <= s2 && Math.abs(this.sy[i] - sy) <= s2) { indices.push(i); } } return new Selection({ indices }); } _hit_span(geometry) { const { sx, sy } = geometry; const bounds = this.bounds(); const half_size = this.max_size / 2; const [x0, x1, y0, y1] = (() => { if (geometry.direction == "h") { const { y0, y1 } = bounds; const sx0 = sx - half_size; const sx1 = sx + half_size; const [x0, x1] = this.renderer.xscale.r_invert(sx0, sx1); return [x0, x1, y0, y1]; } else { const { x0, x1 } = bounds; const sy0 = sy - half_size; const sy1 = sy + half_size; const [y0, y1] = this.renderer.yscale.r_invert(sy0, sy1); return [x0, x1, y0, y1]; } })(); const indices = [...this.index.indices({ x0, x1, y0, y1 })]; return new Selection({ indices }); } _hit_rect(geometry) { const { sx0, sx1, sy0, sy1 } = geometry; const [x0, x1] = this.renderer.xscale.r_invert(sx0, sx1); const [y0, y1] = this.renderer.yscale.r_invert(sy0, sy1); const indices = [...this.index.indices({ x0, x1, y0, y1 })]; return new Selection({ indices }); } _hit_poly(geometry) { const { sx: sxs, sy: sys } = geometry; const candidates = (() => { const xs = this.renderer.xscale.v_invert(sxs); const ys = this.renderer.yscale.v_invert(sys); const [x0, x1, y0, y1] = minmax2(xs, ys); return this.index.indices({ x0, x1, y0, y1 }); })(); const indices = []; for (const i of candidates) { if (hittest.point_in_poly(this.sx[i], this.sy[i], sxs, sys)) { indices.push(i); } } return new Selection({ indices }); } _get_legend_args({ x0, x1, y0, y1 }, index) { // using objects like this seems a little wonky, since the keys are coerced to strings, but it works const n = index + 1; const sx = new Array(n); const sy = new Array(n); sx[index] = (x0 + x1) / 2; sy[index] = (y0 + y1) / 2; const vsize = Math.min(Math.abs(x1 - x0), Math.abs(y1 - y0)) * 0.4; const size = new p.UniformScalar(vsize, n); const angle = new p.UniformScalar(0, n); // don't attempt to match glyph angle return { sx, sy, size, angle }; } draw_legend_for_index(ctx, { x0, x1, y0, y1 }, index) { const args = this._get_legend_args({ x0, x1, y0, y1 }, index); this._paint(ctx, [index], args); } } export class Marker extends XYGlyph { static __name__ = "Marker"; constructor(attrs) { super(attrs); } static { this.mixins([LineVector, FillVector, HatchVector]); this.define(({ Float }) => ({ size: [p.ScreenSizeSpec, { value: 4 }], angle: [p.AngleSpec, 0], hit_dilation: [Float, 1.0], })); } } //# sourceMappingURL=marker.js.map