Skip to content

Commit

Permalink
Backports for 3.2.1 (#13271)
Browse files Browse the repository at this point in the history
* Improve performance of WebGL line glyph (#13236)

* Improve performance of webgl line glyph

* Update baseline images

* Update ruff repo links (#13242)

* Fix `PropertyValueColumnData._stream()` to handle `rollover=0` (#13239)

* Fix `PropertyValueColumnData._stream()` to handle `rollover=0`

* Check 'event.data' in the unit tests

* Improve docs example in first_steps_8.rst (#13161)

* Improve docs example in first_steps_8.rst

* Update figure in docs example first_steps_8.rst

* Resolve issues with high DPI GridPlot exports (#13253)

* Allow to disable DatePicker (etc.) after creation (#13256)

* Use ../core/kinds instead of core/kinds in imports (#13254)

* Don't paint undisplayed plots (#13250)

* Update theme.py (#13270)

* Fixed Broken Link (#13266)

* Fixed Broken Link

* Update docs/bokeh/source/docs/first_steps/first_steps_9.rst

Co-authored-by: Bryan Van de Ven <bryan@bokeh.org>

---------

Co-authored-by: Bryan Van de Ven <bryan@bokeh.org>

* fix unterminated string literals in example code [skip ci] (#13274)

* Fix Legend's grid layout for uneven number of items (#13263)

* 13272 update legend docs (#13273)

* update docs for two dimensional legends

* Apply suggestions from code review

* Implement cloneable interface in ndarrays (#13232)

* Update docs/bokeh/switcher.json

* Added release notes

---------

Co-authored-by: Ian Thomas <ianthomas23@gmail.com>
Co-authored-by: Xiaoyang Liu <siujoeng.lau@gmail.com>
Co-authored-by: Christoph Deil <Deil.Christoph@gmail.com>
Co-authored-by: Moritz Schreiber <68053396+mosc9575@users.noreply.github.com>
Co-authored-by: Rajat Shenoi <rajatshenoi@outlook.com>
Co-authored-by: Bryan Van de Ven <bryan@bokeh.org>
  • Loading branch information
7 people committed Jul 20, 2023
1 parent 4ad3732 commit 2664c2e
Show file tree
Hide file tree
Showing 120 changed files with 555 additions and 94 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
default_stages: [push]

repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.269
hooks:
- id: ruff
Expand Down
9 changes: 9 additions & 0 deletions bokehjs/src/lib/core/util/bbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,15 @@ export class BBox implements Rect, Equatable {
return new BBox({x: tx + x, y: ty + y, width, height})
}

scale(factor: number): BBox {
return new BBox({
x0: this.x0*factor,
x1: this.x1*factor,
y0: this.y0*factor,
y1: this.y1*factor,
})
}

relativize(x: number, y: number): [number, number] {
return [x - this.x, y - this.y]
}
Expand Down
44 changes: 43 additions & 1 deletion bokehjs/src/lib/core/util/ndarray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {isObject, isNumber} from "./types"
import {BYTE_ORDER} from "./platform"
import type {Equatable, Comparator} from "./eq"
import {equals} from "./eq"
import type {Cloner, Cloneable} from "./cloneable"
import {clone} from "./cloneable"
import type {Serializable, Serializer, ArrayRep, BytesRep, NDArrayRep} from "../serialization"
import {serialize} from "../serialization"

Expand All @@ -19,7 +21,7 @@ function encode_NDArray(array: NDArray, serializer: Serializer): NDArrayRep {
}
}

export interface NDArrayType<T, U=T> extends Arrayable<U>, Equatable, Serializable {
export interface NDArrayType<T, U=T> extends Arrayable<U>, Equatable, Cloneable, Serializable {
readonly [__ndarray__]: boolean
readonly dtype: NDDataType
readonly shape: number[]
Expand All @@ -46,6 +48,10 @@ export class BoolNDArray extends Uint8Array implements NDArrayType<boolean, numb
return cmp.eq(this.shape, that.shape) && cmp.arrays(this, that)
}

[clone](cloner: Cloner): this {
return new BoolNDArray(this, cloner.clone(this.shape)) as this
}

[serialize](serializer: Serializer): unknown {
return encode_NDArray(this, serializer)
}
Expand All @@ -71,6 +77,10 @@ export class Uint8NDArray extends Uint8Array implements NDArrayType<number> {
return cmp.eq(this.shape, that.shape) && cmp.arrays(this, that)
}

[clone](cloner: Cloner): this {
return new Uint8NDArray(this, cloner.clone(this.shape)) as this
}

[serialize](serializer: Serializer): unknown {
return encode_NDArray(this, serializer)
}
Expand All @@ -96,6 +106,10 @@ export class Int8NDArray extends Int8Array implements NDArrayType<number> {
return cmp.eq(this.shape, that.shape) && cmp.arrays(this, that)
}

[clone](cloner: Cloner): this {
return new Int8NDArray(this, cloner.clone(this.shape)) as this
}

[serialize](serializer: Serializer): unknown {
return encode_NDArray(this, serializer)
}
Expand All @@ -121,6 +135,10 @@ export class Uint16NDArray extends Uint16Array implements NDArrayType<number> {
return cmp.eq(this.shape, that.shape) && cmp.arrays(this, that)
}

[clone](cloner: Cloner): this {
return new Uint16NDArray(this, cloner.clone(this.shape)) as this
}

[serialize](serializer: Serializer): unknown {
return encode_NDArray(this, serializer)
}
Expand All @@ -146,6 +164,10 @@ export class Int16NDArray extends Int16Array implements NDArrayType<number> {
return cmp.eq(this.shape, that.shape) && cmp.arrays(this, that)
}

[clone](cloner: Cloner): this {
return new Int16NDArray(this, cloner.clone(this.shape)) as this
}

[serialize](serializer: Serializer): unknown {
return encode_NDArray(this, serializer)
}
Expand All @@ -171,6 +193,10 @@ export class Uint32NDArray extends Uint32Array implements NDArrayType<number> {
return cmp.eq(this.shape, that.shape) && cmp.arrays(this, that)
}

[clone](cloner: Cloner): this {
return new Uint32NDArray(this, cloner.clone(this.shape)) as this
}

[serialize](serializer: Serializer): unknown {
return encode_NDArray(this, serializer)
}
Expand All @@ -196,6 +222,10 @@ export class Int32NDArray extends Int32Array implements NDArrayType<number> {
return cmp.eq(this.shape, that.shape) && cmp.arrays(this, that)
}

[clone](cloner: Cloner): this {
return new Int32NDArray(this, cloner.clone(this.shape)) as this
}

[serialize](serializer: Serializer): unknown {
return encode_NDArray(this, serializer)
}
Expand All @@ -221,6 +251,10 @@ export class Float32NDArray extends Float32Array implements NDArrayType<number>
return cmp.eq(this.shape, that.shape) && cmp.arrays(this, that)
}

[clone](cloner: Cloner): this {
return new Float32NDArray(this, cloner.clone(this.shape)) as this
}

[serialize](serializer: Serializer): unknown {
return encode_NDArray(this, serializer)
}
Expand All @@ -246,6 +280,10 @@ export class Float64NDArray extends Float64Array implements NDArrayType<number>
return cmp.eq(this.shape, that.shape) && cmp.arrays(this, that)
}

[clone](cloner: Cloner): this {
return new Float64NDArray(this, cloner.clone(this.shape)) as this
}

[serialize](serializer: Serializer): unknown {
return encode_NDArray(this, serializer)
}
Expand Down Expand Up @@ -288,6 +326,10 @@ export class ObjectNDArray<T=unknown> extends Array<T> implements NDArrayType<T>
return cmp.eq(this.shape, that.shape) && cmp.arrays(this, that)
}

[clone](cloner: Cloner): this {
return new ObjectNDArray(this, cloner.clone(this.shape)) as this
}

[serialize](serializer: Serializer): unknown {
return encode_NDArray(this, serializer)
}
Expand Down
4 changes: 4 additions & 0 deletions bokehjs/src/lib/core/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export class View implements ISignalable {

protected _has_finished: boolean

mark_finished(): void {
this._has_finished = true
}

/** @internal */
protected _slots = new WeakMap<Slot<any, any>, Slot<any, any>>()

Expand Down
6 changes: 3 additions & 3 deletions bokehjs/src/lib/models/annotations/legend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type {Context2d} from "core/util/canvas"
import {TextBox} from "core/graphics"
import {Column, Row, Grid, ContentLayoutable, Sizeable} from "core/layout"

const {max, floor} = Math
const {max, ceil} = Math

type HitTarget = {type: "entry", entry: LegendEntry}

Expand Down Expand Up @@ -143,14 +143,14 @@ export class LegendView extends AnnotationView {
if (vertical) {
if (nrows != "auto") {
} else if (ncols != "auto")
nrows = floor(n / ncols)
nrows = ceil(n / ncols)
else
nrows = Infinity
ncols = Infinity
} else {
if (ncols != "auto") {
} else if (nrows != "auto")
ncols = floor(n / nrows)
ncols = ceil(n / nrows)
else
ncols = Infinity
nrows = Infinity
Expand Down
4 changes: 3 additions & 1 deletion bokehjs/src/lib/models/canvas/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ async function init_webgl(): Promise<WebGLState | null> {
// We use a global invisible canvas and gl context. By having a global context,
// we avoid the limitation of max 16 contexts that most browsers have.
const canvas = document.createElement("canvas")
const gl = canvas.getContext("webgl", {premultipliedAlpha: true})
const gl = canvas.getContext(
"webgl", {alpha: true, antialias: false, depth: false, premultipliedAlpha: true},
)

// If WebGL is available, we store a reference to the ReGL wrapper on
// the ctx object, because that's what gets passed everywhere.
Expand Down
2 changes: 1 addition & 1 deletion bokehjs/src/lib/models/glyphs/glyph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ export abstract class GlyphView extends View {
}

this._map_data()
this.glglyph?.set_data_changed()
this.glglyph?.set_data_mapped()
}

// This is where specs not included in coords are computed, e.g. radius.
Expand Down
2 changes: 1 addition & 1 deletion bokehjs/src/lib/models/glyphs/hex_tile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export class HexTileView extends GlyphView {
;[this.svx, this.svy] = this._get_unscaled_vertices()

// From overridden GlyphView.map_data()
this.glglyph?.set_data_changed()
this.glglyph?.set_data_mapped()
}

protected _get_unscaled_vertices(): [[number, number, number, number, number, number], [number, number, number, number, number, number]] {
Expand Down
5 changes: 5 additions & 0 deletions bokehjs/src/lib/models/glyphs/webgl/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export abstract class BaseGLGlyph {
protected nvertices: number = 0
protected size_changed: boolean = false
protected data_changed: boolean = false
protected data_mapped: boolean = false
protected visuals_changed: boolean = false

constructor(protected readonly regl_wrapper: ReglWrapper, readonly glyph: GlyphView) {}
Expand All @@ -25,6 +26,10 @@ export abstract class BaseGLGlyph {
this.data_changed = true
}

set_data_mapped(): void {
this.data_mapped = true
}

set_visuals_changed(): void {
this.visuals_changed = true
}
Expand Down
52 changes: 30 additions & 22 deletions bokehjs/src/lib/models/glyphs/webgl/base_line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ export abstract class BaseLineGL extends BaseGLGlyph {
this.visuals_changed = false
}

if (main_gl_glyph.data_changed) {
main_gl_glyph._set_data()
if (main_gl_glyph.data_changed || main_gl_glyph.data_mapped) {
main_gl_glyph._set_data(main_gl_glyph.data_changed)
main_gl_glyph.data_changed = false
main_gl_glyph.data_mapped = false
}

const line_visuals = this._get_visuals().line
Expand Down Expand Up @@ -98,35 +99,42 @@ export abstract class BaseLineGL extends BaseGLGlyph {
return this._line_dash != null && this._line_dash.length > 0
}

protected _set_data(): void {
protected _set_data(data_changed: boolean): void {
// If data_changed is false the underlying glyph data has not changed but has been mapped to
// different canvas coordinates e.g. via pan or zoom. If data_changed is true the data itself
// has changed, which also implies it has been mapped.
const points_array = this._set_data_points()

// Points array includes extra points at each end
const npoints = points_array.length/2 - 2
if (data_changed) {
// Points array includes extra points at each end
const npoints = points_array.length/2 - 2

if (this._show == null)
this._show = new Uint8Buffer(this.regl_wrapper)
const show_array = this._show.get_sized_array(npoints+1)
if (this._show == null)
this._show = new Uint8Buffer(this.regl_wrapper)
const show_array = this._show.get_sized_array(npoints+1)

let start_finite = isFinite(points_array[2]) && isFinite(points_array[3])
for (let i = 1; i < npoints; i++) {
const end_finite = isFinite(points_array[2*i+2]) && isFinite(points_array[2*i+3])
show_array[i] = (start_finite && end_finite) ? 1 : 0
start_finite = end_finite
}
let start_finite = isFinite(points_array[2]) && isFinite(points_array[3])
for (let i = 1; i < npoints; i++) {
const end_finite = isFinite(points_array[2*i+2]) && isFinite(points_array[2*i+3])
show_array[i] = (start_finite && end_finite) ? 1 : 0
start_finite = end_finite
}

if (this._is_closed) {
show_array[0] = show_array[npoints-1]
show_array[npoints] = show_array[1]
} else {
show_array[0] = 0
show_array[npoints] = 0
}
if (this._is_closed) {
show_array[0] = show_array[npoints-1]
show_array[npoints] = show_array[1]
} else {
show_array[0] = 0
show_array[npoints] = 0
}

this._show.update()
this._show.update()
}

if (this._is_dashed()) {
const npoints = points_array.length/2 - 2
const nsegments = npoints-1
const show_array = this._show!.get_sized_array(npoints+1)

if (this._length_so_far == null)
this._length_so_far = new Float32Buffer(this.regl_wrapper)
Expand Down
3 changes: 2 additions & 1 deletion bokehjs/src/lib/models/glyphs/webgl/multi_marker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ export class MultiMarkerGL extends BaseMarkerGL {
// The main glyph has the data, this glyph has the visuals.
const main_gl_glyph = main_glyph.glglyph!

if (main_gl_glyph.data_changed) {
if (main_gl_glyph.data_changed || main_gl_glyph.data_mapped) {
main_gl_glyph.set_data()
main_gl_glyph.data_changed = false
main_gl_glyph.data_mapped = false
}

if (this.visuals_changed) {
Expand Down
3 changes: 2 additions & 1 deletion bokehjs/src/lib/models/glyphs/webgl/single_marker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ export abstract class SingleMarkerGL extends BaseMarkerGL {
}

protected _draw_impl(indices: number[], transform: Transform, main_gl_glyph: SingleMarkerGL, marker_type: GLMarkerType): void {
if (main_gl_glyph.data_changed) {
if (main_gl_glyph.data_changed || main_gl_glyph.data_mapped) {
main_gl_glyph.set_data()
main_gl_glyph.data_changed = false
main_gl_glyph.data_mapped = false
}

if (this.visuals_changed) {
Expand Down
2 changes: 1 addition & 1 deletion bokehjs/src/lib/models/layouts/layout_dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ export abstract class LayoutDOMView extends UIElementView {

for (const view of this.child_views) {
const region = view.export(type, hidpi)
const {x, y} = view.bbox
const {x, y} = view.bbox.scale(composite.pixel_ratio)
composite.ctx.drawImage(region.canvas, x, y)
}

Expand Down
30 changes: 0 additions & 30 deletions bokehjs/src/lib/models/plots/grid_plot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type {ToolbarView} from "../tools/toolbar"
import {Toolbar} from "../tools/toolbar"
import type {UIElement} from "../ui/ui_element"
import {ActionTool} from "../tools/actions/action_tool"
import {CanvasLayer} from "core/util/canvas"
import type {ViewStorage, IterViews} from "core/build_views"
import {build_views, remove_views} from "core/build_views"
import {Location} from "core/enums"
Expand Down Expand Up @@ -110,35 +109,6 @@ export class GridPlotView extends LayoutDOMView {
})()
this.style.append(":host", {flex_direction})
}

override export(type: "auto" | "png" | "svg" = "auto", hidpi: boolean = true): CanvasLayer {
const output_backend = (() => {
switch (type) {
case "auto": // TODO: actually infer the best type
case "png": return "canvas"
case "svg": return "svg"
}
})()

const composite = new CanvasLayer(output_backend, hidpi)

const {x, y, width, height} = this.grid_box_view.bbox.relative()
composite.resize(width, height)
composite.ctx.save()

const bg_color = getComputedStyle(this.el).backgroundColor
composite.ctx.fillStyle = bg_color
composite.ctx.fillRect(x, y, width, height)

for (const view of this.child_views) {
const region = view.export(type, hidpi)
const {x, y} = view.bbox
composite.ctx.drawImage(region.canvas, x, y)
}

composite.ctx.restore()
return composite
}
}

export namespace GridPlot {
Expand Down

0 comments on commit 2664c2e

Please sign in to comment.