Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make GlyphView.mask_data() always narrow indices to the viewport #13332

Open
wants to merge 6 commits into
base: branch-3.5
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion bokehjs/src/lib/models/annotations/legend_item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class LegendItem extends Model {
}

const {index} = this
if (index != null && this.renderers.every((r) => !(index in r.view.indices_map))) {
if (index != null && this.renderers.every((r) => !r.view.indices_map.has(index))) {
// this index points to nowhere, so skip this item altogether from its legend
return []
}
Expand Down
2 changes: 1 addition & 1 deletion bokehjs/src/lib/models/glyphs/circle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export class CircleView extends XYGlyphView {
}
}

protected override _mask_data(): Indices {
override mask_data(): Indices {
const {frame} = this.renderer.plot_view

const shr = frame.x_target
Expand Down
17 changes: 9 additions & 8 deletions bokehjs/src/lib/models/glyphs/glyph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import type {Anchor} from "core/enums"
import type {ViewStorage, IterViews} from "core/build_views"
import {build_views} from "core/build_views"
import {logger} from "core/logging"
import type {Arrayable, Rect, FloatArray} from "core/types"
import {ScreenArray, Indices} from "core/types"
import type {Arrayable, Rect, FloatArray, Indices} from "core/types"
import {ScreenArray} from "core/types"
import {isString} from "core/util/types"
import {RaggedArray} from "core/util/ragged_array"
import {inplace_map, max} from "core/util/arrayable"
Expand Down Expand Up @@ -365,13 +365,14 @@ export abstract class GlyphView extends View {

mask_data(): Indices {
/** Returns subset indices in the viewport. */
if (this._mask_data == null)
return Indices.all_set(this.data_size)
else
return this._mask_data()
}
const {frame} = this.renderer.plot_view
const [xr, yr] = frame.bbox.ranges

protected _mask_data?(): Indices
const [x0, x1] = this.renderer.x_scale.r_invert(xr.start, xr.end)
const [y0, y1] = this.renderer.y_scale.r_invert(yr.start, yr.end)

return this.index.indices({x0, x1, y0, y1})
}

map_data(): void {
const self = this as any
Expand Down
42 changes: 31 additions & 11 deletions bokehjs/src/lib/models/glyphs/image_url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ export type ImageURLData = XYGlyphData & {

image: (CanvasImage | undefined)[]
rendered: Indices

anchor: XY<number>
}

export interface ImageURLView extends ImageURLData {}
Expand All @@ -51,11 +49,33 @@ export class ImageURLView extends XYGlyphView {
const {data_size} = this

for (let i = 0; i < data_size; i++) {
// TODO: add a proper implementation (same as ImageBase?)
index.add_empty()
const [l, r, t, b] = this._lrtb(i)
index.add_rect(l, b, r, t)
}
}

_lrtb(i: number): [number, number, number, number] {
const dw_i = this.w.get(i)
const dh_i = this.h.get(i)

const x_i = this._x[i]
const y_i = this._y[i]

const {xy_anchor} = this

const [x0, x1] = [x_i - xy_anchor.x*dw_i, x_i + (1 - xy_anchor.x)*dw_i]
const [y0, y1] = [y_i + xy_anchor.y*dh_i, y_i - (1 - xy_anchor.y)*dh_i]

const [l, r] = x0 <= x1 ? [x0, x1] : [x1, x0]
const [b, t] = y0 <= y1 ? [y0, y1] : [y1, y0]
return [l, r, t, b]
}

private _xy_anchor: XY<number>
get xy_anchor(): XY<number> {
return this._xy_anchor
}

private _set_data_iteration: number = 0

protected override _set_data(): void {
Expand Down Expand Up @@ -99,15 +119,15 @@ export class ImageURLView extends XYGlyphView {
const xs = new ScreenArray(w_data ? 2*n : n)
const ys = new ScreenArray(h_data ? 2*n : n)

this.anchor = resolve.anchor(this.model.anchor)
const {x: x_anchor, y: y_anchor} = this.anchor
this._xy_anchor = resolve.anchor(this.model.anchor)
const {xy_anchor} = this

function x0x1(x: number, w: number) {
const x0 = x - x_anchor*w
const x0 = x - xy_anchor.x*w
return [x0, x0 + w]
}
function y0y1(y: number, h: number) {
const y0 = y + y_anchor*h
const y0 = y + xy_anchor.y*h
return [y0, y0 - h]
}

Expand Down Expand Up @@ -192,9 +212,9 @@ export class ImageURLView extends XYGlyphView {
const sw_i = sw[i]
const sh_i = sh[i]

const {anchor} = this
const dx_i = anchor.x*sw_i
const dy_i = anchor.y*sh_i
const {xy_anchor} = this
const dx_i = xy_anchor.x*sw_i
const dy_i = xy_anchor.y*sh_i

const sx_i = sx[i] - dx_i
const sy_i = sy[i] - dy_i
Expand Down
2 changes: 1 addition & 1 deletion bokehjs/src/lib/models/glyphs/marker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export abstract class MarkerView extends XYGlyphView {
}
}

protected override _mask_data(): Indices {
override mask_data(): Indices {
// dilate the inner screen region by max_size and map back to data space for use in spatial query
const {x_target, y_target} = this.renderer.plot_view.frame

Expand Down
10 changes: 1 addition & 9 deletions bokehjs/src/lib/models/glyphs/multi_polygons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Glyph, GlyphView} from "./glyph"
import {generic_area_vector_legend} from "./utils"
import {minmax} from "core/util/arrayable"
import {sum} from "core/util/arrayable"
import type {Arrayable, Rect, FloatArray, ScreenArray, Indices} from "core/types"
import type {Arrayable, Rect, FloatArray, ScreenArray} from "core/types"
import type {PointGeometry, RectGeometry} from "core/geometry"
import type {Context2d} from "core/util/canvas"
import {LineVector, FillVector, HatchVector} from "core/property_mixins"
Expand Down Expand Up @@ -115,14 +115,6 @@ export class MultiPolygonsView extends GlyphView {
return index
}

protected override _mask_data(): Indices {
const {x_range, y_range} = this.renderer.plot_view.frame
return this.index.indices({
x0: x_range.min, x1: x_range.max,
y0: y_range.min, y1: y_range.max,
})
}

protected _render(ctx: Context2d, indices: number[], data?: MultiPolygonsData): void {
if (!this.visuals.fill.doit && !this.visuals.line.doit)
return
Expand Down
10 changes: 1 addition & 9 deletions bokehjs/src/lib/models/glyphs/patches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {GlyphData} from "./glyph"
import {Glyph, GlyphView} from "./glyph"
import {generic_area_vector_legend} from "./utils"
import {minmax2, sum} from "core/util/arrayable"
import type {Arrayable, Rect, RaggedArray, FloatArray, ScreenArray, Indices} from "core/types"
import type {Arrayable, Rect, RaggedArray, FloatArray, ScreenArray} from "core/types"
import type {PointGeometry, RectGeometry} from "core/geometry"
import type {Context2d} from "core/util/canvas"
import {LineVector, FillVector, HatchVector} from "core/property_mixins"
Expand Down Expand Up @@ -44,14 +44,6 @@ export class PatchesView extends GlyphView {
}
}

protected override _mask_data(): Indices {
const {x_range, y_range} = this.renderer.plot_view.frame
return this.index.indices({
x0: x_range.min, x1: x_range.max,
y0: y_range.min, y1: y_range.max,
})
}

protected _render(ctx: Context2d, indices: number[], data?: PatchesData): void {
const {sxs, sys} = data ?? this

Expand Down
30 changes: 20 additions & 10 deletions bokehjs/src/lib/models/glyphs/step.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import * as mixins from "core/property_mixins"
import type * as visuals from "core/visuals"
import type * as p from "core/properties"
import type {Rect} from "core/types"
import {Indices} from "core/types"
import {StepMode} from "core/enums"
import type {Context2d} from "core/util/canvas"
import {unreachable} from "core/util/assert"
import type {StepGL} from "./webgl/step"

export type StepData = XYGlyphData
Expand All @@ -26,6 +26,11 @@ export class StepView extends XYGlyphView {
return StepGL
}

override mask_data(): Indices {
// TODO _render() doesn't like non-NaN holes in data
return Indices.all_set(this.data_size)
}

protected _render(ctx: Context2d, indices: number[], data?: StepData): void {
const npoints = indices.length
if (npoints < 2)
Expand All @@ -40,35 +45,40 @@ export class StepView extends XYGlyphView {
let prev_finite = false
const i = indices[0]
let is_finite = isFinite(sx[i] + sy[i])
if (mode == "center")
if (mode == "center") {
drawing = this._render_xy(ctx, drawing, is_finite ? sx[i] : NaN, sy[i])
}

for (const i of indices) {
const next_finite = isFinite(sx[i+1] + sy[i+1])
switch (mode) {
case "before":
case "before": {
drawing = this._render_xy(ctx, drawing, is_finite ? sx[i] : NaN, sy[i])
if (i < sx.length-1)
if (i < sx.length-1) {
drawing = this._render_xy(ctx, drawing, is_finite && next_finite ? sx[i] : NaN, sy[i+1])
}
break
case "after":
}
case "after": {
drawing = this._render_xy(ctx, drawing, is_finite ? sx[i] : NaN, sy[i])
if (i < sx.length-1)
if (i < sx.length-1) {
drawing = this._render_xy(ctx, drawing, is_finite && next_finite ? sx[i+1] : NaN, sy[i])
}
break
case "center":
}
case "center": {
if (is_finite && next_finite) {
const midx = (sx[i] + sx[i+1])/2
drawing = this._render_xy(ctx, drawing, midx, sy[i])
drawing = this._render_xy(ctx, drawing, midx, sy[i+1])
} else {
if (prev_finite)
if (prev_finite) {
drawing = this._render_xy(ctx, drawing, is_finite ? sx[i] : NaN, sy[i])
}
drawing = this._render_xy(ctx, drawing, next_finite ? sx[i+1] : NaN, sy[i+1])
}
break
default:
unreachable()
}
}
prev_finite = is_finite
is_finite = next_finite
Expand Down
24 changes: 24 additions & 0 deletions bokehjs/src/lib/models/glyphs/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import type {TextAnchor} from "../common/kinds"
import {BorderRadius, Padding} from "../common/kinds"
import * as resolve from "../common/resolve"
import {round_rect} from "../common/painting"
import {max} from "core/util/arrayable"
import type {Indices} from "core/types"

class TextAnchorSpec extends p.DataSpec<TextAnchor> {}

Expand All @@ -32,6 +34,9 @@ export type TextData = XYGlyphData & p.UniformsOf<Text.Mixins> & {
swidth: Float32Array
sheight: Float32Array

max_swidth: number
max_sheight: number

anchor_: p.Uniform<XY<number>> // can't resolve in v_materialize() due to dependency on other properties
padding: LRTB<number>
border_radius: Corners<number>
Expand All @@ -43,6 +48,21 @@ export class TextView extends XYGlyphView {
declare model: Text
declare visuals: Text.Visuals

override mask_data(): Indices {
const {frame} = this.renderer.plot_view

const sxr = frame.x_target
const syr = frame.y_target

const xr = sxr.widen(this.max_swidth).map((x) => this.renderer.xscale.invert(x))
const yr = syr.widen(this.max_sheight).map((y) => this.renderer.yscale.invert(y))

return this.index.indices({
x0: xr.start, x1: xr.end,
y0: yr.start, y1: yr.end,
})
}

override after_visuals(): void {
super.after_visuals()

Expand Down Expand Up @@ -99,6 +119,10 @@ export class TextView extends XYGlyphView {
this.swidth[i] = width
this.sheight[i] = height
}

// TODO consider rotation
this.max_swidth = max(this.swidth)
this.max_sheight = max(this.sheight)
}

protected _render(ctx: Context2d, indices: number[], data?: TextData): void {
Expand Down
4 changes: 1 addition & 3 deletions bokehjs/src/lib/models/ranges/data_range1d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class DataRange1d extends DataRange {
protected _initial_follow_interval: number | null
protected _initial_default_span: number

protected _plot_bounds: Map<PlotView, Rect>
protected readonly _plot_bounds: Map<PlotView, Rect> = new Map()

override have_updated_interactively: boolean = false

Expand All @@ -91,8 +91,6 @@ export class DataRange1d extends DataRange {
this._initial_follow = this.follow
this._initial_follow_interval = this.follow_interval
this._initial_default_span = this.default_span

this._plot_bounds = new Map()
}

get min(): number {
Expand Down
14 changes: 12 additions & 2 deletions bokehjs/src/lib/models/renderers/data_renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,24 @@ export abstract class DataRendererView extends RendererView implements AutoRange
declare model: DataRenderer
declare visuals: DataRenderer.Visuals

get xscale(): Scale {
get x_scale(): Scale {
return this.coordinates.x_scale
}

get yscale(): Scale {
get y_scale(): Scale {
return this.coordinates.y_scale
}

// TODO remove this
get xscale(): Scale {
return this.x_scale
}

// TODO remove this
get yscale(): Scale {
return this.y_scale
}

protected abstract get glyph_view(): GlyphView

readonly [auto_ranged] = true
Expand Down
11 changes: 6 additions & 5 deletions bokehjs/src/lib/models/renderers/glyph_renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type {Color} from "core/types"
import {Indices} from "core/types"
import type * as p from "core/properties"
import {filter} from "core/util/arrayable"
import {extend, clone, entries} from "core/util/object"
import {extend, clone} from "core/util/object"
import type {HitTestResult} from "core/hittest"
import type {Geometry} from "core/geometry"
import type {SelectionManager} from "core/selection_manager"
Expand Down Expand Up @@ -440,9 +440,10 @@ export class GlyphRendererView extends DataRendererView {
if (field != null) {
const array = this.model.data_source.get_column(field)
if (array != null) {
for (const [key, index] of entries(this.model.view.indices_map)) {
if (array[parseInt(key)] == value)
return index
for (const [fullset_index, subset_index] of this.model.view.indices_map) {
if (array[fullset_index] == value) {
return subset_index
}
}
}
}
Expand All @@ -458,7 +459,7 @@ export class GlyphRendererView extends DataRendererView {
return this.get_reference_point(field, label)
else {
const {indices_map} = this.model.view
return index in indices_map ? indices_map[index] : null
return indices_map.get(index) ?? null
}
})()
if (subset_index != null) {
Expand Down