Refactoring custom MarkerView from < v2

hello!

so…i’ve been putting this off for a while, but i’m returning to a project that’s been…on hold for a bit and…well…i really want my damn custom marker.

so…when figured out that i couldn’t quite get the stock markers to do exactly what i needed, i popped into…whatever the live chat for bokeh is and one of the devs was kind enough to quickly throw together the below:

from bokeh.models import Marker

JS_CODE = """
import {Marker, MarkerView} from "models/markers/marker"

export class MyTriangleView extends MarkerView
  _render_one: (ctx, i, r, line, fill) ->
    h = r * Math.sqrt(3)
    a = h

    ctx.moveTo(-r, a)
    ctx.lineTo(r, a)
    ctx.lineTo(0, a - h)
    ctx.closePath()

    if (fill.doit)
      fill.set_vectorize(ctx, i)
      ctx.fill()

    if (line.doit)
      line.set_vectorize(ctx, i)
      ctx.stroke()

export class BuySell extends Marker
  default_view: MyTriangleView
  type: "BuySell"
"""
class BuySell(Marker):

    __implementation__ = JS_CODE

i remember seeing the dreaded “we’re going to be deprecating this…so get your shit together with this custom JS marker.” but life and i had to put the project on hiatus. i think even at the time, the documentation for what the dev did…didn’t really exist…sort of an in-house hookup, if you will. I’ve tried giving the documentation another college try, to no avail. i’m either missing something or i just don’t know wtf i’m supposed to be searching for.

that said, i suppose i have the following:

  • how does one implement custom markers with the current stable bokeh release?
  • if there is documentation, please for the love of superman point the way.
  • if there is not, how in the name of kelly clarkson does one refactor the above for python?

i sort of just #yolo’d the below, but i have a feeling it’s…you know…not correct.

class BSTriangle(MarkerView):

    def __init__(self, ctx, i, r, line, fill):

        self.ctx = ctx
        self.i = i
        self.r = r
        self.line = line
        self.fill = fille

    def _render_one(self):

        h = r * math.sqrt(3)
        a = h

        ctx.moveTo(-r, a)
        ctx.lineTo(r, a)
        ctx.lineTo(0, a - h)
        ctx.closePath()

        if fill.doit:
            fill.set_vectorize(ctx, i)

        if line.doit:
            line.set_vectorixe(ctx, i)
            ctx.stroke()

class BuySell(Marker):

    def __init__(self):

        self.default_view = BSTriangle
        self.type = 'BuySell'

help me obi wan(s)!

alright…so i THINK this is where this came from…

if i’m not terribly far off…looks like…the js code is creating a custom glyph and ctx, i, r, line, and fill, refer to the associated properties pertaining to one of the glyphs…or marker glyph…

line and fill have to correspond to the line properties and fill properties.
r is most likely radius.
i, could be inner radius?
wtf is ctx? there’s a cx and cy property…is ctx like…a coord tuple?

oh god my brain is leaking out of my ear again.

This is the most relevant documentation section: Extending Bokeh — Bokeh 2.4.2 Documentation

Note that BokehJS is still not standardized in any way, so whatever you implement might still break with the next update, even a minor one.
Also note that you don’t really need a full-blown custom marker if you can just plot it by combining other markers. I.e. instead of a single custom marker that draws three lines, you can just draw 3 line markers, something like that.

…this might be pure speculation, but wouldn’t a custom marker be…the more optimized route as it’s being rendered on import instead of having to draw three separate lines during figure generation?

the reason for the custom marker was that at the time i initially requested this code, there was no marker which could pivot around an external point. if i recall, the anchor was the central point of INSERTGLYPHVARIANTHERE…there’s a method to the madness. this marker does exactly what i need it to, so i’d just like to know how to convert it to the most…future proof variant (typescript?) at the moment so i can get it working again.

Having a specialized marker would be more efficient, yes. But you should not care about that unless you start having noticeable performance degradation when using multiple markers representing a single one.

As of now, there’s no future proof variant. Many of the relevant things are a subject to change in any future version. Perhaps there will be such a way past Bokeh 3.

the only thing i want is to figure out how to port the old code to work with the current version of bokeh. i’ll cross the bridge of two or three versions down when or if the time comes. so with that in mind, can you help?

from bokeh.util.compiler import JavaScript


class BuySell(Marker):
    __implementation__ = JavaScript("""
import {Marker, MarkerView} from "models/markers/marker"

export class MyTriangleView extends MarkerView {
  _render_one(ctx, i, r, line, fill) {
    const h = r * Math.sqrt(3);
    const a = h;

    ctx.moveTo(-r, a);
    ctx.lineTo(r, a);
    ctx.lineTo(0, a - h);
    ctx.closePath();

    if (fill.doit) {
      fill.set_vectorize(ctx, i);
      ctx.fill();
    }

    if (line.doit) {
      line.set_vectorize(ctx, i);
      ctx.stroke();
    }
  }
}

export class BuySell extends Marker {
  static __name__ = 'BuySell';
  static init_BuySell() {
    this.prototype.default_view = MyTriangleView;
  }
}

BuySell.init_BuySell();
""")

Zhenechkaaaaa.
Spaciba, bratan. :wink:

1 Like

You should consider opening a GitHub feature request. It wasn’t clear to me at first how this was different from a plain triangle but its evident now that this is an “indicator” (i.e. it points at the center, rather than being centered on the center). This would be a simple and useful addition.

i do my best not to have people do extra work unless absolutely necessary, which is why i had…i can’t for the life of me remember who the hell it was, whip this custom marker up for me in the first place. i think the conversation went something along the lines of “hey guys, is it possible to move the anchor point of the triangle to one of the points and then rotate it?” “rotate yes, tip no” “well shit”. and then custom marker. i think one of the suggestions was to use the regular triangle and then rotate and offset it and i was like…that’s just silly. i need like an arrow.

i got no problem submitting a feature request, but if someone says it’s a stupid idea, i’m telling them @Bryan made me do it… :wink:

I’ve opened an issue here: [FEATURE] New Indicator Marker (or annotation?) · Issue #10751 · bokeh/bokeh · GitHub

@Bryan HELP ME OBI-WAN, YOU"RE MY ONLY HOPE!11!

a little late for May 4th, but it is what it is.

any chance you guys could stop moving shit around and breaking my beloved custom markers? :wink:

if i’m reading the output correctly, i just need to change the below import path to wherever you guys squirreled the /models/markers/marker path to between versions 2.2.3 and 2.3.0, correct?

image

p.s. what’s the new path.

thanks in advance.

All individual Marker classes were removed at 2.3.0 in favor of a generic Scatter that can be parameterized by marker type (i.e., amenable to driving type via CDS or marker mapper). As part of this work, everything including markers.ts was consolidated under the models/glyphs in this PR:

Migrate from marker glyphs to scatter glyph by mattpap · Pull Request #10698 · bokeh/bokeh · GitHub

However, unless I am mistaken, you could have (should have) been importing from the top level models index (which imports and re-exports everything underneath it), not from the individual models/markers sub-file. This would insulate you against changes to the internal filesystem structure, which is absolutely not guaranteed to be stable at this point. We have given the same advice on the Python side ("only import from top-level bokeh.models") for years now

That said, it seems like several of the examples in the repo do not follow this advice. There was a round to update all the Python imports to only use bokeh.models but apparently the extension examples were overlooked. I have made an issue to update all of them:

1 Like

if i said that i understood 100% of what you said, my nose would grow so long that i’d be able to turn on the receiver from across the room.

as far as scanning for the information i was looking for, if i replace

import {Marker, MarkerView} from "models/markers/marker"

with

import {Marker, MarkerView} from "models/glyphs"

i should theoretically be good?
the rest of the code for the custom marker is the same as what @p-himik posted earlier in the thread.