UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

183 lines 6.41 kB
import { Glyph, GlyphView } from "./glyph"; import { generic_area_vector_legend } from "./utils"; import { minmax2, sum } from "../../core/util/arrayable"; import { LineVector, FillVector, HatchVector } from "../../core/property_mixins"; import * as hittest from "../../core/hittest"; import * as p from "../../core/properties"; import { Selection } from "../selections/selection"; import { unreachable } from "../../core/util/assert"; export class PatchesView extends GlyphView { static __name__ = "PatchesView"; _project_data() { this._project_xy("xs", this.xs.data, "ys", this.ys.data); } _index_data(index) { const { data_size } = this; for (let i = 0; i < data_size; i++) { const xsi = this.xs.get(i); const ysi = this.ys.get(i); const [x0, x1, y0, y1] = minmax2(xsi, ysi); index.add_rect(x0, y0, x1, y1); } } _mask_data() { const { x_source, y_source } = this.renderer.coordinates; return this.index.indices({ x0: x_source.min, x1: x_source.max, y0: y_source.min, y1: y_source.max, }); } _paint(ctx, indices, data) { const { sxs, sys } = { ...this, ...data }; for (const i of indices) { const sx_i = sxs.get(i); const sy_i = sys.get(i); let move = true; ctx.beginPath(); const n = Math.min(sx_i.length, sy_i.length); for (let j = 0; j < n; j++) { const sx_j = sx_i[j]; const sy_j = sy_i[j]; if (!isFinite(sx_j + sy_j)) { ctx.closePath(); move = true; } else { if (move) { ctx.moveTo(sx_j, sy_j); move = false; } else { ctx.lineTo(sx_j, sy_j); } } } ctx.closePath(); this.visuals.fill.apply(ctx, i); this.visuals.hatch.apply(ctx, i); this.visuals.line.apply(ctx, i); } } _hit_poly(geometry) { const { sx: sxs, sy: sys, greedy = false } = 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) { const sxs_i = this.sxs.get(i); const sys_i = this.sys.get(i); const n = sxs_i.length; if (n == 0) { continue; } let hit = !greedy; for (let j = 0; j < n; j++) { const sx = sxs_i[j]; const sy = sys_i[j]; if (!hittest.point_in_poly(sx, sy, sxs, sys)) { if (!greedy) { hit = false; break; } } else { if (greedy) { hit = true; break; } } } if (hit) { indices.push(i); } } return new Selection({ indices }); } _hit_rect(geometry) { const { sx0, sx1, sy0, sy1, greedy } = geometry; const sxs = [sx0, sx1, sx1, sx0]; const sys = [sy0, sy0, sy1, sy1]; return this._hit_poly({ type: "poly", sx: sxs, sy: sys, greedy }); } _hit_point(geometry) { const { sx, sy } = geometry; const x = this.renderer.xscale.invert(sx); const y = this.renderer.yscale.invert(sy); const candidates = this.index.indices({ x0: x, y0: y, x1: x, y1: y }); const indices = []; for (const index of candidates) { const sxsi = this.sxs.get(index); const sysi = this.sys.get(index); const n = sxsi.length; for (let k = 0, j = 0;; j++) { if (isNaN(sxsi[j]) || j == n) { const sxsi_kj = sxsi.subarray(k, j); const sysi_kj = sysi.subarray(k, j); if (hittest.point_in_poly(sx, sy, sxsi_kj, sysi_kj)) { indices.push(index); break; } k = j + 1; } if (j == n) { break; } } } return new Selection({ indices }); } _get_snap_coord(array) { return sum(array) / array.length; } scenterxy(i, sx, sy) { const sxsi = this.sxs.get(i); const sysi = this.sys.get(i); const n = sxsi.length; let has_nan = false; for (let k = 0, j = 0;; j++) { const this_nan = isNaN(sxsi[j]); has_nan = has_nan || this_nan; if (j == n && !has_nan) { const scx = this._get_snap_coord(sxsi); const scy = this._get_snap_coord(sysi); return [scx, scy]; } if (this_nan || j == n) { const sxsi_kj = sxsi.subarray(k, j); const sysi_kj = sysi.subarray(k, j); if (hittest.point_in_poly(sx, sy, sxsi_kj, sysi_kj)) { const scx = this._get_snap_coord(sxsi_kj); const scy = this._get_snap_coord(sysi_kj); return [scx, scy]; } k = j + 1; } if (j == n) { break; } } unreachable(); } draw_legend_for_index(ctx, bbox, index) { generic_area_vector_legend(this.visuals, ctx, bbox, index); } } export class Patches extends Glyph { static __name__ = "Patches"; constructor(attrs) { super(attrs); } static { this.prototype.default_view = PatchesView; this.define(({}) => ({ xs: [p.XCoordinateSeqSpec, { field: "xs" }], ys: [p.YCoordinateSeqSpec, { field: "ys" }], })); this.mixins([LineVector, FillVector, HatchVector]); } } //# sourceMappingURL=patches.js.map