UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

136 lines 5.1 kB
import { Area, AreaView } from "./area"; import * as hittest from "../../core/hittest"; import * as p from "../../core/properties"; import { StepMode } from "../../core/enums"; import { flip_step_mode } from "../../core/util/flip_step_mode"; import { Selection } from "../selections/selection"; export class VAreaStepView extends AreaView { static __name__ = "VAreaStepView"; _index_data(index) { const { min, max } = Math; const { x, y1, y2 } = this; for (let i = 0; i < this.data_size; i++) { const x_i = x[i]; const y1_i = y1[i]; const y2_i = y2[i]; index.add_rect(x_i, min(y1_i, y2_i), x_i, max(y1_i, y2_i)); } } _step_path(ctx, mode, sx, sy, from_i, to_i) { // Assume the path was already moved to the first point let prev_x = sx[from_i]; let prev_y = sy[from_i]; const idx_dir = from_i < to_i ? 1 : -1; for (let i = from_i + idx_dir; i != to_i; i += idx_dir) { switch (mode) { case "before": { ctx.lineTo(prev_x, sy[i]); ctx.lineTo(sx[i], sy[i]); break; } case "after": { ctx.lineTo(sx[i], prev_y); ctx.lineTo(sx[i], sy[i]); break; } case "center": { const mid_x = (prev_x + sx[i]) / 2; ctx.lineTo(mid_x, prev_y); ctx.lineTo(mid_x, sy[i]); ctx.lineTo(sx[i], sy[i]); break; } } prev_x = sx[i]; prev_y = sy[i]; } } _paint(ctx, _indices, data) { const { sx, sy1, sy2 } = { ...this, ...data }; const forward_mode = this.model.step_mode; const backward_mode = flip_step_mode(this.model.step_mode); ctx.beginPath(); ctx.moveTo(sx[0], sy1[0]); this._step_path(ctx, forward_mode, sx, sy1, 0, sx.length); this._step_path(ctx, backward_mode, sx, sy2, sx.length, -1); ctx.closePath(); this.visuals.fill.apply(ctx); this.visuals.hatch.apply(ctx); } scenterxy(i) { const scx = this.sx[i]; const scy = (this.sy1[i] + this.sy2[i]) / 2; return [scx, scy]; } _line_selection_for(i) { return new Selection({ line_indices: [i], selected_glyphs: [this.model], view: this }); } _hit_point_before(geometry) { const { sx, sy1, sy2 } = this; for (let i = 1; i < this.data_size; i++) { const px = [sx[i - 1], sx[i], sx[i], sx[i - 1]]; const py = [sy1[i], sy1[i], sy2[i], sy2[i]]; if (hittest.point_in_poly(geometry.sx, geometry.sy, px, py)) { return this._line_selection_for(i); } } return new Selection(); } _hit_point_after(geometry) { const { sx, sy1, sy2 } = this; for (let i = 0; i < this.data_size - 1; i++) { const px = [sx[i], sx[i + 1], sx[i + 1], sx[i]]; const py = [sy1[i], sy1[i], sy2[i], sy2[i]]; if (hittest.point_in_poly(geometry.sx, geometry.sy, px, py)) { return this._line_selection_for(i); } } return new Selection(); } _hit_point_center(geometry) { const { sx, sy1, sy2 } = this; for (let i = 0; i < this.data_size; i++) { const mid_prev_x = (sx[i - 1] + sx[i]) / 2; /* undefined for first */ const mid_next_x = (sx[i] + sx[i + 1]) / 2; /* undefined for last */ const px = (() => { if (i == 0) { return [sx[i], mid_next_x, mid_next_x, sx[i]]; } else if (i == this.data_size - 1) { return [mid_prev_x, sx[i], sx[i], mid_prev_x]; } else { return [mid_prev_x, mid_next_x, mid_next_x, mid_prev_x]; } })(); const py = [sy1[i], sy1[i], sy2[i], sy2[i]]; if (hittest.point_in_poly(geometry.sx, geometry.sy, px, py)) { return this._line_selection_for(i); } } return new Selection(); } _hit_point(geometry) { switch (this.model.step_mode) { case "before": return this._hit_point_before(geometry); case "after": return this._hit_point_after(geometry); case "center": return this._hit_point_center(geometry); } } } export class VAreaStep extends Area { static __name__ = "VAreaStep"; constructor(attrs) { super(attrs); } static { this.prototype.default_view = VAreaStepView; this.define(({}) => ({ x: [p.XCoordinateSpec, { field: "x" }], y1: [p.YCoordinateSpec, { field: "y1" }], y2: [p.YCoordinateSpec, { field: "y2" }], step_mode: [StepMode, "before"], })); } } //# sourceMappingURL=varea_step.js.map