@bokeh/bokehjs
Version:
Interactive, novel data visualization
141 lines • 4.8 kB
JavaScript
import { LineVector } from "../../core/property_mixins";
import * as hittest from "../../core/hittest";
import * as p from "../../core/properties";
import { minmax2 } from "../../core/util/arrayable";
import { Glyph, GlyphView } from "./glyph";
import { generic_line_vector_legend, line_interpolation } from "./utils";
import { Selection } from "../selections/selection";
export class MultiLineView extends GlyphView {
static __name__ = "MultiLineView";
async load_glglyph() {
const { MultiLineGL } = await import("./webgl/multi_line");
return MultiLineGL;
}
_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);
}
}
_paint(ctx, indices, data) {
const { sxs, sys } = { ...this, ...data };
for (const i of indices) {
const sx = sxs.get(i);
const sy = sys.get(i);
const n = Math.min(sx.length, sy.length);
let move = true;
ctx.beginPath();
for (let j = 0; j < n; j++) {
const sx_j = sx[j];
const sy_j = sy[j];
if (!isFinite(sx_j + sy_j)) {
move = true;
}
else {
if (move) {
ctx.moveTo(sx_j, sy_j);
move = false;
}
else {
ctx.lineTo(sx_j, sy_j);
}
}
}
this.visuals.line.set_vectorize(ctx, i);
ctx.stroke();
}
}
_hit_point(geometry) {
const point = { x: geometry.sx, y: geometry.sy };
let shortest = 9999;
const hits = new Map();
for (let i = 0, end = this.sxs.length; i < end; i++) {
const threshold = Math.max(2, this.line_width.get(i) / 2);
const sxsi = this.sxs.get(i);
const sysi = this.sys.get(i);
let points = null;
for (let j = 0, endj = sxsi.length - 1; j < endj; j++) {
const p0 = { x: sxsi[j], y: sysi[j] };
const p1 = { x: sxsi[j + 1], y: sysi[j + 1] };
const dist = hittest.dist_to_segment(point, p0, p1);
if (dist < threshold && dist < shortest) {
shortest = dist;
points = [j];
}
}
if (points != null) {
hits.set(i, points);
}
}
return new Selection({
indices: [...hits.keys()],
multiline_indices: hits,
});
}
_hit_span(geometry) {
const { sx, sy } = geometry;
let val;
let vs;
if (geometry.direction == "v") {
val = this.renderer.yscale.invert(sy);
vs = this.ys;
}
else {
val = this.renderer.xscale.invert(sx);
vs = this.xs;
}
const hits = new Map();
for (let i = 0, end = vs.length; i < end; i++) {
const vsi = vs.get(i);
const points = [];
for (let j = 0, endj = vsi.length - 1; j < endj; j++) {
if (vsi[j] <= val && val <= vsi[j + 1]) {
points.push(j);
}
}
if (points.length > 0) {
hits.set(i, points);
}
}
return new Selection({
indices: [...hits.keys()],
multiline_indices: hits,
});
}
get_interpolation_hit(i, point_i, geometry) {
const xsi = this.xs.get(i);
const ysi = this.ys.get(i);
const x2 = xsi[point_i];
const y2 = ysi[point_i];
const x3 = xsi[point_i + 1];
const y3 = ysi[point_i + 1];
return line_interpolation(this.renderer, geometry, x2, y2, x3, y3);
}
draw_legend_for_index(ctx, bbox, index) {
generic_line_vector_legend(this.visuals, ctx, bbox, index);
}
scenterxy() {
throw new Error(`${this}.scenterxy() is not implemented`);
}
}
export class MultiLine extends Glyph {
static __name__ = "MultiLine";
constructor(attrs) {
super(attrs);
}
static {
this.prototype.default_view = MultiLineView;
this.define(({}) => ({
xs: [p.XCoordinateSeqSpec, { field: "xs" }],
ys: [p.YCoordinateSeqSpec, { field: "ys" }],
}));
this.mixins(LineVector);
}
}
//# sourceMappingURL=multi_line.js.map