Search code examples
d3fc

d3fc webgl candlesticks - setting fillStyle equivelant in webgl


I'm using d3fc+canvas to render candlesticks to a chart and change their fill color based on whether it's an up or down candle. The series generator looks like this

const generator = fc.seriesCanvasCandlestick()
        .crossValue(d => d.date)
        .openValue(d => d.open)
        .highValue(d => d.high)
        .lowValue(d => d.low)
        .closeValue(d => d.close)
        .decorate((context, datum, index) => {

            context.fillStyle = datum.close > datum.open ? 'blue' : 'red'
        });

I'm now trying to convert this to webGl rendering (using seriesWebglCandlestick) but I don't know how to set the fill color in the same way.

How can I convert my decorate function to color the webgl candlesticks?

It seems like I can get the Webgl context using context.context() but setting the fillStyle on it doesnt do anything.

I saw another stackoverflow answer suggest:

renderer.setClearColor(0xcc3dca, 0.7)
renderer.clear()

But there seems to be no setClearColor function on the Webgl context (though I do see clear())

I also do see clearColor() which expects 4 arguments so I tried passing rgba values like clearColor(197,197,197,0.7) but also has no effect.


Solution

  • Now this functionality has been released, the code should look like this -

    const fillColor = fc
        .webglFillColor()
        .data(data)
        .value(d => 
             d.close > d.open ? 
                 [0, 0, 1, 1] : [1, 0, 0, 1]
        );
    
    const series = fc
        .seriesWebglCandlestick()
        .xScale(xScale)
        .yScale(yScale)
        .context(gl)
        .crossValue((_, i) => i)
        .highValue(d => d.high)
        .lowValue(d => d.low)
        .openValue(d => d.open)
        .closeValue(d => d.close)
        .decorate(program => {
            fillColor(program);
        });
    

    N.B. webglFillColor will have no effect if there's no fill e.g. for ohlc. In that case you'll need to use webglStrokeColor .

    Original answer below.


    Unfortunately this isn't currently easy with the WebGL series in d3fc but we do expect that to change shortly.

    As it stands this requires delving pretty deeply into the internals of the d3fc-webgl package. We don't recommend doing this currently (until the issue above is fixed) because the naming and exact functionality of components is liable to change.

    However, if you're very keen to try this out here's the code required in the current version -

    This code will only run if you build your own version of the library to export elementConstantAttributeBuilder from packages/d3fc-webgl/index.js

    const fillColorAttribute = fc
        .elementConstantAttributeBuilder()
        .size(4)
        .data(data)
        .value((data, element) => 
             data[element].close > data[element].open ? 
                 [0, 0, 1, 1] : [1, 0, 0, 1]
        );
    
    const series = fc
        .seriesWebglCandlestick()
        .xScale(xScale)
        .yScale(yScale)
        .context(gl)
        .crossValue((_, i) => i)
        .highValue(d => d.high)
        .lowValue(d => d.low)
        .openValue(d => d.open)
        .closeValue(d => d.close)
        .decorate(program => {
            program
                .vertexShader()
                .appendHeader('attribute vec4 aFillColor;')
                .appendHeader('varying vec4 vFillColor;')
                .appendBody('vFillColor = aFillColor;');
            program
                .fragmentShader()
                .appendHeader('varying vec4 vFillColor;')
                .appendBody('gl_FragColor = vFillColor;');
            program
                .buffers()
                .attribute('aFillColor', fillColorAttribute);
        });
    

    Which produces the following -

    candlestick example showing blue/red fill color