UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

139 lines 5.09 kB
import { RadialGlyph, RadialGlyphView } from "./radial_glyph"; import { minmax2 } from "../../core/util/arrayable"; import { edge_intersection, point_in_poly, vertex_overlap } from "../../core/hittest"; import * as p from "../../core/properties"; import { Selection } from "../selections/selection"; function ngon(x, y, r, n, angle) { const xs = new Float32Array(n); const ys = new Float32Array(n); const alpha_i = 2 * Math.PI / n; for (let i = 0; i < n; i++) { const alpha = i * alpha_i + angle; xs[i] = x + r * Math.sin(alpha); ys[i] = y + r * -Math.cos(alpha); } return [xs, ys]; } export class NgonView extends RadialGlyphView { static __name__ = "NgonView"; async load_glglyph() { const { NgonGL } = await import("./webgl/ngon"); return NgonGL; } _paint(ctx, indices, data) { const { sx, sy, sradius, angle, n } = { ...this, ...data }; for (const i of indices) { const sx_i = sx[i]; const sy_i = sy[i]; const sradius_i = sradius[i]; const angle_i = angle.get(i); const n_i = n.get(i); if (n_i < 3 || !isFinite(sx_i + sy_i + sradius_i + angle_i + n_i)) { continue; } const [sxs, sys] = ngon(sx_i, sy_i, sradius_i, n_i, angle_i); ctx.beginPath(); ctx.moveTo(sxs[0], sys[0]); for (let i = 1; i <= n_i; i++) { ctx.lineTo(sxs[i], sys[i]); } ctx.closePath(); this.visuals.fill.apply(ctx, i); this.visuals.hatch.apply(ctx, i); this.visuals.line.apply(ctx, i); } } _ngon(index) { const { sx, sy, sradius, angle, n } = { ...this }; const sx_i = sx[index]; const sy_i = sy[index]; const sradius_i = sradius[index]; const angle_i = angle.get(index); const n_i = n.get(index); return ngon(sx_i, sy_i, sradius_i, n_i, angle_i); } _hit_point(geometry) { const x = this.renderer.xscale.invert(geometry.sx); const y = this.renderer.yscale.invert(geometry.sy); const candidates = this.index.indices({ x0: x, y0: y, x1: x, y1: y }); const indices = []; for (const index of candidates) { const [sxs, sys] = this._ngon(index); if (point_in_poly(geometry.sx, geometry.sy, sxs, sys)) { indices.push(index); } } return new Selection({ indices }); } _hit_span(geometry) { const { sx, sy } = geometry; const { x0, x1, y0, y1 } = this.bounds(); const [val, dim, candidates] = (() => { switch (geometry.direction) { case "v": { const y = this.renderer.yscale.invert(sy); const candidates = this.index.indices({ x0, y0: y, x1, y1: y }); return [sy, 1, candidates]; } case "h": { const x = this.renderer.xscale.invert(sx); const candidates = this.index.indices({ x0: x, y0, x1: x, y1 }); return [sx, 0, candidates]; } } })(); const indices = []; for (const index of candidates) { const coords = this._ngon(index)[dim]; for (let i = 0; i < coords.length - 1; i++) { if ((coords[i] <= val && val <= coords[i + 1]) || (coords[i + 1] <= val && val <= coords[i])) { indices.push(index); break; } } } return new Selection({ indices }); } _hit_poly(geometry) { const { sx: gsx, sy: gsy } = geometry; const candidates = (() => { const xs = this.renderer.xscale.v_invert(gsx); const ys = this.renderer.yscale.v_invert(gsy); const [x0, x1, y0, y1] = minmax2(xs, ys); return this.index.indices({ x0, x1, y0, y1 }); })(); const indices = []; for (const index of candidates) { const [sxs, sys] = this._ngon(index); if (vertex_overlap(sxs, sys, gsx, gsy)) { indices.push(index); continue; } if (edge_intersection(sxs, sys, gsx, gsy)) { indices.push(index); continue; } } return new Selection({ indices }); } _hit_rect(geometry) { const { sx0, sx1, sy0, sy1 } = geometry; const sxs = [sx0, sx1, sx1, sx0]; const sys = [sy0, sy0, sy1, sy1]; return this._hit_poly({ type: "poly", sx: sxs, sy: sys }); } } export class Ngon extends RadialGlyph { static __name__ = "Ngon"; constructor(attrs) { super(attrs); } static { this.prototype.default_view = NgonView; this.define(() => ({ angle: [p.AngleSpec, 0], n: [p.NumberSpec, { field: "n" }], })); } } //# sourceMappingURL=ngon.js.map