@bokeh/bokehjs
Version:
Interactive, novel data visualization
136 lines • 5.1 kB
JavaScript
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 HAreaStepView extends AreaView {
static __name__ = "HAreaStepView";
_index_data(index) {
const { min, max } = Math;
const { x1, x2, y } = this;
for (let i = 0; i < this.data_size; i++) {
const x1_i = x1[i];
const x2_i = x2[i];
const y_i = y[i];
index.add_rect(min(x1_i, x2_i), y_i, max(x1_i, x2_i), y_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(sx[i], prev_y);
ctx.lineTo(sx[i], sy[i]);
break;
}
case "after": {
ctx.lineTo(prev_x, sy[i]);
ctx.lineTo(sx[i], sy[i]);
break;
}
case "center": {
const mid_y = (prev_y + sy[i]) / 2;
ctx.lineTo(prev_x, mid_y);
ctx.lineTo(sx[i], mid_y);
ctx.lineTo(sx[i], sy[i]);
break;
}
}
prev_x = sx[i];
prev_y = sy[i];
}
}
_paint(ctx, _indices, data) {
const { sx1, sx2, sy } = { ...this, ...data };
const forward_mode = this.model.step_mode;
const backward_mode = flip_step_mode(this.model.step_mode);
ctx.beginPath();
ctx.moveTo(sx1[0], sy[0]);
this._step_path(ctx, forward_mode, sx1, sy, 0, sy.length);
this._step_path(ctx, backward_mode, sx2, sy, sy.length, -1);
ctx.closePath();
this.visuals.fill.apply(ctx);
this.visuals.hatch.apply(ctx);
}
scenterxy(i) {
const scx = (this.sx1[i] + this.sx2[i]) / 2;
const scy = this.sy[i];
return [scx, scy];
}
_line_selection_for(i) {
return new Selection({ line_indices: [i], selected_glyphs: [this.model], view: this });
}
_hit_point_before(geometry) {
const { sy, sx1, sx2 } = this;
for (let i = 1; i < this.data_size; i++) {
const px = [sx1[i], sx1[i], sx2[i], sx2[i]];
const py = [sy[i - 1], sy[i], sy[i], sy[i - 1]];
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 { sy, sx1, sx2 } = this;
for (let i = 0; i < this.data_size - 1; i++) {
const px = [sx1[i], sx1[i], sx2[i], sx2[i]];
const py = [sy[i], sy[i + 1], sy[i + 1], sy[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 { sy, sx1, sx2 } = this;
for (let i = 0; i < this.data_size; i++) {
const mid_prev_y = (sy[i - 1] + sy[i]) / 2; /* undefined for first */
const mid_next_y = (sy[i] + sy[i + 1]) / 2; /* undefined for last */
const px = [sx1[i], sx1[i], sx2[i], sx2[i]];
const py = (() => {
if (i == 0) {
return [sy[i], mid_next_y, mid_next_y, sy[i]];
}
else if (i == this.data_size - 1) {
return [mid_prev_y, sy[i], sy[i], mid_prev_y];
}
else {
return [mid_prev_y, mid_next_y, mid_next_y, mid_prev_y];
}
})();
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 HAreaStep extends Area {
static __name__ = "HAreaStep";
constructor(attrs) {
super(attrs);
}
static {
this.prototype.default_view = HAreaStepView;
this.define(({}) => ({
x1: [p.XCoordinateSpec, { field: "x1" }],
x2: [p.XCoordinateSpec, { field: "x2" }],
y: [p.YCoordinateSpec, { field: "y" }],
step_mode: [StepMode, "before"],
}));
}
}
//# sourceMappingURL=harea_step.js.map