UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

187 lines 7.28 kB
import { Glyph, GlyphView } from "./glyph"; import * as hittest from "../../core/hittest"; import * as p from "../../core/properties"; import { LineVector, FillVector, HatchVector } from "../../core/property_mixins"; import { HexTileOrientation } from "../../core/enums"; import { generic_area_vector_legend } from "./utils"; import { Selection } from "../selections/selection"; export class HexTileView extends GlyphView { static __name__ = "HexTileView"; async load_glglyph() { const { HexTileGL } = await import("./webgl/hex_tile"); return HexTileGL; } scenterxy(i) { const scx = this.sx[i]; const scy = this.sy[i]; return [scx, scy]; } _set_data() { const { orientation, size, aspect_scale } = this.model; const { q, r } = this; const n = this.q.length; const x = new Float64Array(n); const y = new Float64Array(n); const sqrt3 = Math.sqrt(3); if (orientation == "pointytop") { for (let i = 0; i < n; i++) { const q_i = q.get(i); const r2_i = r.get(i) / 2; x[i] = size * sqrt3 * (q_i + r2_i) / aspect_scale; y[i] = -3 * size * r2_i; } } else { for (let i = 0; i < n; i++) { const q2_i = q.get(i) / 2; const r_i = r.get(i); x[i] = 3 * size * q2_i; y[i] = -size * sqrt3 * (r_i + q2_i) * aspect_scale; } } this._define_attr("x", x); this._define_attr("y", y); } _project_data() { this._project_xy("x", this.x, "y", this.y); } _index_data(index) { let ysize = this.model.size; let xsize = Math.sqrt(3) * ysize / 2; if (this.model.orientation == "flattop") { [xsize, ysize] = [ysize, xsize]; ysize *= this.model.aspect_scale; } else { xsize /= this.model.aspect_scale; } const { x, y, data_size } = this; for (let i = 0; i < data_size; i++) { const x_i = x[i]; const y_i = y[i]; index.add_rect(x_i - xsize, y_i - ysize, x_i + xsize, y_i + ysize); } } // overriding map_data instead of _map_data because the default automatic mappings // for other glyphs (with cartesian coordinates) is not useful map_data() { const [sx, sy] = this.renderer.coordinates.map_to_screen(this.x, this.y); const [svx, svy] = this._get_unscaled_vertices(); this._define_attr("sx", sx); this._define_attr("sy", sy); this._define_attr("svx", svx); this._define_attr("svy", svy); // From overridden GlyphView.map_data() if (this.has_webgl()) { this.glglyph.set_data_mapped(); } } _get_unscaled_vertices() { const size = this.model.size; const aspect_scale = this.model.aspect_scale; if (this.model.orientation == "pointytop") { const rscale = this.renderer.yscale; const hscale = this.renderer.xscale; const r = Math.abs(rscale.compute(0) - rscale.compute(size)); // assumes linear scale const h = Math.sqrt(3) / 2 * Math.abs(hscale.compute(0) - hscale.compute(size)) / aspect_scale; // assumes linear scale const r2 = r / 2.0; const svx = [0, -h, -h, 0, h, h]; const svy = [r, r2, -r2, -r, -r2, r2]; return [svx, svy]; } else { const rscale = this.renderer.xscale; const hscale = this.renderer.yscale; const r = Math.abs(rscale.compute(0) - rscale.compute(size)); // assumes linear scale const h = Math.sqrt(3) / 2 * Math.abs(hscale.compute(0) - hscale.compute(size)) * aspect_scale; // assumes linear scale const r2 = r / 2.0; const svx = [r, r2, -r2, -r, -r2, r2]; const svy = [0, -h, -h, 0, h, h]; return [svx, svy]; } } _paint(ctx, indices, data) { const { sx, sy, svx, svy, scale } = { ...this, ...data }; for (const i of indices) { const sx_i = sx[i]; const sy_i = sy[i]; const scale_i = scale.get(i); if (!isFinite(sx_i + sy_i + scale_i)) { continue; } ctx.translate(sx_i, sy_i); ctx.beginPath(); for (let j = 0; j < 6; j++) { ctx.lineTo(svx[j] * scale_i, svy[j] * scale_i); } ctx.closePath(); ctx.translate(-sx_i, -sy_i); this.visuals.fill.apply(ctx, i); this.visuals.hatch.apply(ctx, i); this.visuals.line.apply(ctx, i); } } _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 i of candidates) { if (hittest.point_in_poly(sx - this.sx[i], sy - this.sy[i], this.svx, this.svy)) { indices.push(i); } } return new Selection({ indices }); } _hit_span(geometry) { const rect = (() => { if (geometry.direction == "v") { const { sy } = geometry; const y = this.renderer.yscale.invert(sy); const hr = this.renderer.plot_view.frame.bbox.h_range; const [x0, x1] = this.renderer.xscale.r_invert(hr.start, hr.end); return { x0, y0: y, x1, y1: y }; } else { const { sx } = geometry; const x = this.renderer.xscale.invert(sx); const vr = this.renderer.plot_view.frame.bbox.v_range; const [y0, y1] = this.renderer.yscale.r_invert(vr.start, vr.end); return { x0: x, y0, x1: x, y1 }; } })(); const indices = [...this.index.indices(rect)]; 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 }); } draw_legend_for_index(ctx, bbox, index) { generic_area_vector_legend(this.visuals, ctx, bbox, index); } } export class HexTile extends Glyph { static __name__ = "HexTile"; constructor(attrs) { super(attrs); } static { this.prototype.default_view = HexTileView; this.mixins([LineVector, FillVector, HatchVector]); this.define(({ Float }) => ({ r: [p.NumberSpec, { field: "r" }], q: [p.NumberSpec, { field: "q" }], scale: [p.NumberSpec, 1.0], size: [Float, 1.0], aspect_scale: [Float, 1.0], orientation: [HexTileOrientation, "pointytop"], })); this.override({ line_color: null }); } } //# sourceMappingURL=hex_tile.js.map