Custom annotation extension: how to create fill_color ColorSpec

I am trying to make a custom annotation through a TypeScript extension. My annotation is a combination of a horizontal bar with alternating colors and horizontal line. Everything in screen units. The figure below shows what I am aiming for (this has been created with the patches and line glyphs).
In the TypeScript code I make 3 arrays: bar_x and bar_y (similar to list of lists) and one array for the alternating colors (the colors are defined within the code, not passed as a property).
This gives an error in the terminal because of the wrong assignment of color (in the code: this.model.fill_color = scale_color).

How to specify fill_color ColorSpec property? How can I easily convert an array of colors eg ['blue', 'orange', 'blue', 'orange', 'blue'] to the correct ColorSpec format?

Compilation failed:

bar.ts:38:5 - error TS2322: Type 'string[]' is not assignable to type 'Color | Vector<Color | null> | null'.
  Type 'string[]' is not assignable to type '[R: number, G: number, B: number, A?: number | undefined]'.
    Target requires 3 element(s) but source may have fewer.

38     this.model.fill_color = scale_color
       ~~~~~~~~~~~~~~~~~~~~~

Also, how do I handle multiple annotations in the same Class in TS? Is there not a risk of mixing properties between the bar and line I try to make?

This is my very first attempt into more advanced extensions so please comment if my approach is completely wrong. I am here to learn.

python code

from bokeh.models import Annotation
from bokeh.plotting import figure, output_file, save
from bokeh.layouts import row

output_file("bar.html", title="Annotation extension")


class Bar(Annotation):
    ''' Render a shaded polygonal region as an annotation.
    See :ref:`userguide_annotations_polygon_annotations` for information on
    plotting polygon annotations.
    '''

    __implementation__ = 'bar.ts'


xs = []
[xs.append([-1+i,0+i, 0+i, -1+i]) for i in range(0,5)]
ys = [[0, 0, 5, 5]]*5

color1 = 'blue'
color2 = 'orange'
color = [color1, color2, color1, color2, color1]
print(color)

# range bounds supplied in web mercator coordinates
p = figure(plot_height = 300, x_range=(-2, 6), y_range=(-10, 70))
p.patches(xs = xs, ys = ys, fill_color = color)
p.line(x = [-1, 4], y = [9, 9], line_color = 'black', line_width = 3)

bar = Bar()
p.add_layout(bar)

save(row(p))

bar.ts

import {Annotation, AnnotationView} from "models/annotations/annotation"
import * as mixins from "core/property_mixins"
import * as visuals from "core/visuals"
import * as p from "core/properties"

export class BarView extends AnnotationView {
  override model: Bar
  override visuals: Bar.Visuals

  protected _render(): void {
    const {frame} = this.plot_view
    const {ctx} = this.layer

    // use screen units for the drawing of the bar and line 
    const x_offset = 15
    const y_offset = 15
    const dy = 7
    const sub_bar = 25

    const color1 = 'blue'
    const color2 = 'orange'
    const scale_color = [color1, color2, color1, color2, color1]

    var bar_x = [] 
    var bar_y = []
        
    const y0 = y_offset
    const y1 = y_offset + dy

    for (var i = 0; i < 5; i++) {
      var x0 = Math.round(x_offset + frame.bbox.left + sub_bar * i)
      var x1 = Math.round(x_offset + frame.bbox.left + sub_bar * (i+1))
      bar_x.push([x0, x1, x1, x0])
      bar_y.push([y0, y0, y1, y1])
    }
    
    // How do I specify the color array for this.visuals.fill????
    this.model.fill_color = scale_color

    // draw bar's
    for (let i = 0; i < bar_x.length; i++) {
      ctx.beginPath()

      const sx_i = bar_x[i]
      const sy_i = bar_y[i]
      
      for (let j = 0; j < sx_i.length; j++) {
        const sx_j = sx_i[j]
        const sy_j = sy_i[j]

        if (j === 0) {
          ctx.moveTo(sx_j, sy_j)
        } else {
          ctx.lineTo(sx_j, sy_j)
        }
      }
      ctx.closePath()

      this.visuals.fill.apply(ctx, i)
    }

    // draw line
    ctx.beginPath()
    ctx.lineTo(x_offset + frame.bbox.left, 35)
    ctx.lineTo(x_offset + frame.bbox.left+70, 35)
    ctx.closePath


    // How do I make sure line properties for the line are not the same as the line for the bar?
    this.model.fill_color = 'black'
    this.visuals.line.apply(ctx)
  }
}

export namespace Bar {
  export type Attrs = p.AttrsOf<Props>

  export type Props = Annotation.Props & Mixins

  export type Mixins = mixins.Line & mixins.FillVector

  export type Visuals = Annotation.Visuals & {line: visuals.Line, fill: visuals.FillVector}
}

export interface Bar extends Bar.Attrs {}

export class Bar extends Annotation {
  override properties: Bar.Props
  override __view_type__: BarView

  constructor(attrs?: Partial<Bar.Attrs>) {
    super(attrs)
  }

  static {
    this.prototype.default_view = BarView

    this.mixins<Bar.Mixins>([mixins.Line, mixins.FillVector])
  }
}

System info

Python version      :  3.9.12 (main, Mar 26 2022, 15:52:10) 
IPython version     :  (not installed)
Tornado version     :  6.1
Bokeh version       :  2.4.2
BokehJS static path :  /.venv/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  v16.14.2
npm version         :  8.5.0
Operating system    :  macOS-11.6-x86_64-i386-64bit

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.