I’m not sure why data is undefined at that point, maybe @mateusz can comment if that is expected. However, in your code you have not added a “color” column to the CDS anwyay, so it will not be in source.data regardless.
All that said, you should generally not set visual properties (i.e. HTML canvas context drawing properties) manually. That is because there are two ways to specify values for these kinds of properties:
- a vector of values — goes in a CDS
- a single scalar value — does not go in a CDS
If you are familiar with OpenGL at all, this should be reminiscent of the uniform / vector dichotomy. Bokeh has special “Visual” classes for configuring the HTML context that will do the right thing, regardless of whether there is a single scalar value, or a vector of values. You can see this in action in e.g. lrtb.ts (the base class for all “rect-like” glyphs).
Here is a version your code, simplified and updated to allow the line visual class to do all the work for configuring the context. This “works”, but is technically not really correct — see my final note at the bottom.
import {Scatter, ScatterView} from "models/glyphs/scatter"
import * as p from "core/properties"
import type * as visuals from "core/visuals"
import type {Context2d} from "core/util/canvas"
import {catmullrom_spline} from "core/util/interpolation"
export class VertexView extends ScatterView {
declare model: Vertex
declare visuals: Vertex.Visuals
connect_signals(): void {
super.connect_signals()
}
protected _paint(ctx: Context2d, indices: number[], data?: Partial<Scatter.Data>): void {
super._paint(ctx, indices, data)
const {sx, sy} = {...this, ...data}
if (sx && sy && sx.length > 1 && sy.length > 1) {
const [xt, yt] = catmullrom_spline(sx, sy, 10, 0.5, false)
ctx.beginPath()
ctx.moveTo(xt[0], yt[0])
for (let i = 1; i < xt.length; i++) {
ctx.lineTo(xt[i], yt[i])
this.visuals.line.apply(ctx, i)
}
ctx.stroke()
}
}
render(): void {
super.render()
}
}
export namespace Vertex {
export type Attrs = p.AttrsOf<Props>
export type Visuals = Scatter.Visuals & {line: visuals.LineVector, fill: visuals.FillVector, hatch: visuals.HatchVector}
export type Props = Scatter.Props & {}
}
export interface Vertex extends Vertex.Attrs {}
export class Vertex extends Scatter {
declare properties: Vertex.Props
declare __view_type__: VertexView
constructor(attrs?: Partial<Vertex.Attrs>) {
super(attrs)
}
static {
this.prototype.default_view = VertexView
this.define<Vertex.Props>(({}) => ({ }))
}
}
After changing line_color to "red" (instead of None) in vertex.py that results in:
So why is this not correct? Because while you have a vector of x- and y- points, you are drawing only a single line. It does not actually make sense to have “vector” visuals for the line. So this class should really be changed in one of two ways:
- Make a spline model more like
Line which only has scalar visual properties. This extension would only draw the spline line, and not any of the vertices. To draw those, you’d combine with a separate call to normal scatter, just like you’d combine a call to line and scatter in typical bokeh code.
- Make a spline model that is responsible for both the spline line and the spline verices. That is possible, but more complicated. You l will need separate vector fill and hatch and line properties for the vertices, and scalar line properties for the spline line,
Unfortunately I don’t have any specific helpful example or docs to point to for the latter. Extensions are considered advanced / experimental. The combination of
- very few people asking needing them or asking about them (you are the first person in a couple of years that I can think of)
- the effort to fully develop more with better documentation being a large lift
Means that the cost / benefit computation does not really work out for prioritizing very limited developer resources / time in this area.