UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

126 lines 4.7 kB
import { XYGlyph, XYGlyphView } from "./xy_glyph"; import { generic_line_scalar_legend, line_interpolation } from "./utils"; import * as mixins from "../../core/property_mixins"; import * as hittest from "../../core/hittest"; import { Selection } from "../selections/selection"; export class LineView extends XYGlyphView { static __name__ = "LineView"; async load_glglyph() { const { LineGL } = await import("./webgl/line_gl"); return LineGL; } _paint(ctx, indices, data) { const { sx, sy } = { ...this, ...data }; const nonselection = this.parent.nonselection_glyph == this; let iprev = null; const gap = (i) => iprev != null && i - iprev != 1; let move = true; ctx.beginPath(); for (const i of indices) { const sx_i = sx[i]; const sy_i = sy[i]; if (nonselection && !move && iprev != null && i - iprev > 1 && isFinite(sx[iprev + 1] + sy[iprev + 1])) { ctx.lineTo(sx[iprev + 1], sy[iprev + 1]); // End of previous line } if (!isFinite(sx_i + sy_i)) { move = true; } else { if (move || gap(i)) { if (nonselection && i > 0 && isFinite(sx[i - 1] + sy[i - 1])) { ctx.moveTo(sx[i - 1], sy[i - 1]); // Start of new line ctx.lineTo(sx_i, sy_i); } else { ctx.moveTo(sx_i, sy_i); } move = false; } else { ctx.lineTo(sx_i, sy_i); } iprev = i; } } if (nonselection && !move && iprev != null) { const n = sx.length; if (iprev < n - 1 && isFinite(sx[iprev + 1] + sy[iprev + 1])) { ctx.lineTo(sx[iprev + 1], sy[iprev + 1]); // End of final line } } this.visuals.line.set_value(ctx); ctx.stroke(); } _hit_point(geometry) { /* Check if the point geometry hits this line glyph and return an object that describes the hit result: Args: * geometry (object): object with the following keys * sx (float): screen x coordinate of the point * sy (float): screen y coordinate of the point * type (str): type of geometry (in this case it's a point) */ const result = new Selection(); const point = { x: geometry.sx, y: geometry.sy }; let shortest = 9999; const threshold = Math.max(2, this.line_width.value / 2); for (let i = 0, end = this.sx.length - 1; i < end; i++) { const p0 = { x: this.sx[i], y: this.sy[i] }; const p1 = { x: this.sx[i + 1], y: this.sy[i + 1] }; const dist = hittest.dist_to_segment(point, p0, p1); if (dist < threshold && dist < shortest) { shortest = dist; result.add_to_selected_glyphs(this.model); result.view = this; result.line_indices = [i]; } } return result; } _hit_span(geometry) { const { sx, sy } = geometry; let val; let values; if (geometry.direction == "v") { val = this.renderer.yscale.invert(sy); values = this.y; } else { val = this.renderer.xscale.invert(sx); values = this.x; } const indices = []; for (let i = 0, end = values.length - 1; i < end; i++) { const curr = values[i]; const next = values[i + 1]; if ((curr <= val && val <= next) || (next <= val && val <= curr)) { indices.push(i); } } const result = new Selection(); if (indices.length != 0) { result.add_to_selected_glyphs(this.model); result.view = this; result.line_indices = indices; } return result; } get_interpolation_hit(i, geometry) { const [x2, y2, x3, y3] = [this.x[i], this.y[i], this.x[i + 1], this.y[i + 1]]; return line_interpolation(this.renderer, geometry, x2, y2, x3, y3); } draw_legend_for_index(ctx, bbox, _index) { generic_line_scalar_legend(this.visuals, ctx, bbox); } } export class Line extends XYGlyph { static __name__ = "Line"; constructor(attrs) { super(attrs); } static { this.prototype.default_view = LineView; this.mixins(mixins.LineScalar); } } //# sourceMappingURL=line.js.map