/**
* @class Ext.chart.series.sprite.CandleStick
* @extends Ext.chart.series.sprite.Aggregative
*
* CandleStick series sprite.
*/
Ext.define('Ext.chart.series.sprite.CandleStick', {
alias: 'sprite.candlestickSeries',
extend: 'Ext.chart.series.sprite.Aggregative',
inheritableStatics: {
def: {
processors: {
raiseStyle: function(n, o) {
return Ext.merge({}, o || {}, n);
},
dropStyle: function(n, o) {
return Ext.merge({}, o || {}, n);
},
/**
* @cfg {Number} [barWidth=15] The bar width of the candles.
*/
barWidth: 'number',
/**
* @cfg {Number} [padding=3] The amount of padding between candles.
*/
padding: 'number',
/**
* @cfg {String} [ohlcType='candlestick'] Determines whether candlestick
* or ohlc is used.
*/
ohlcType: 'enums(candlestick,ohlc)'
},
defaults: {
raiseStyle: {
strokeStyle: 'green',
fillStyle: 'green'
},
dropStyle: {
strokeStyle: 'red',
fillStyle: 'red'
},
barWidth: 15,
padding: 3,
lineJoin: 'miter',
miterLimit: 5,
ohlcType: 'candlestick'
},
triggers: {
raiseStyle: 'raiseStyle',
dropStyle: 'dropStyle'
},
updaters: {
raiseStyle: function() {
var me = this,
tpl = me.raiseTemplate;
if (tpl) {
tpl.setAttributes(me.attr.raiseStyle);
}
},
dropStyle: function() {
var me = this,
tpl = me.dropTemplate;
if (tpl) {
tpl.setAttributes(me.attr.dropStyle);
}
}
}
}
},
candlestick: function(ctx, open, high, low, close, mid, halfWidth) {
var minOC = Math.min(open, close),
maxOC = Math.max(open, close);
// lower stick
ctx.moveTo(mid, low);
ctx.lineTo(mid, minOC);
// body rect
ctx.moveTo(mid + halfWidth, maxOC);
ctx.lineTo(mid + halfWidth, minOC);
ctx.lineTo(mid - halfWidth, minOC);
ctx.lineTo(mid - halfWidth, maxOC);
ctx.closePath();
// upper stick
ctx.moveTo(mid, high);
ctx.lineTo(mid, maxOC);
},
ohlc: function(ctx, open, high, low, close, mid, halfWidth) {
ctx.moveTo(mid, high);
ctx.lineTo(mid, low);
ctx.moveTo(mid, open);
ctx.lineTo(mid - halfWidth, open);
ctx.moveTo(mid, close);
ctx.lineTo(mid + halfWidth, close);
},
constructor: function() {
var me = this,
Rect = Ext.draw.sprite.Rect;
me.callParent(arguments);
me.raiseTemplate = new Rect({ parent: me });
me.dropTemplate = new Rect({ parent: me });
},
getGapWidth: function() {
var attr = this.attr,
barWidth = attr.barWidth,
padding = attr.padding;
return barWidth + padding;
},
renderAggregates: function(aggregates, start, end, surface, ctx, clip) {
var me = this,
attr = me.attr,
ohlcType = attr.ohlcType,
series = me.getSeries(),
matrix = attr.matrix,
xx = matrix.getXX(),
yy = matrix.getYY(),
dx = matrix.getDX(),
dy = matrix.getDY(),
halfWidth = Math.round(attr.barWidth * 0.5),
dataX = attr.dataX,
opens = aggregates.open,
closes = aggregates.close,
maxYs = aggregates.maxY,
minYs = aggregates.minY,
startIdxs = aggregates.startIdx,
pixelAdjust = attr.lineWidth * surface.devicePixelRatio / 2,
renderer = attr.renderer,
rendererConfig = renderer && {},
rendererParams, rendererChanges,
open, high, low, close, mid,
i, template;
me.rendererData = me.rendererData || { store: me.getStore() };
pixelAdjust -= Math.floor(pixelAdjust);
// Render raises.
ctx.save();
template = me.raiseTemplate;
template.useAttributes(ctx, clip);
if (!renderer) {
ctx.beginPath();
}
for (i = start; i < end; i++) {
if (opens[i] <= closes[i]) {
open = Math.round(opens[i] * yy + dy) + pixelAdjust;
high = Math.round(maxYs[i] * yy + dy) + pixelAdjust;
low = Math.round(minYs[i] * yy + dy) + pixelAdjust;
close = Math.round(closes[i] * yy + dy) + pixelAdjust;
mid = Math.round(dataX[startIdxs[i]] * xx + dx) + pixelAdjust;
if (renderer) {
ctx.save();
ctx.beginPath();
rendererConfig.open = open;
rendererConfig.high = high;
rendererConfig.low = low;
rendererConfig.close = close;
rendererConfig.mid = mid;
rendererConfig.halfWidth = halfWidth;
rendererParams = [me, rendererConfig, me.rendererData, i];
rendererChanges = Ext.callback(renderer, null, rendererParams, 0, series);
Ext.apply(ctx, rendererChanges);
}
me[ohlcType](ctx, open, high, low, close, mid, halfWidth);
if (renderer) {
ctx.fillStroke(template.attr);
ctx.restore();
}
}
}
if (!renderer) {
ctx.fillStroke(template.attr);
}
ctx.restore();
// Render drops.
ctx.save();
template = me.dropTemplate;
template.useAttributes(ctx, clip);
if (!renderer) {
ctx.beginPath();
}
for (i = start; i < end; i++) {
if (opens[i] > closes[i]) {
open = Math.round(opens[i] * yy + dy) + pixelAdjust;
high = Math.round(maxYs[i] * yy + dy) + pixelAdjust;
low = Math.round(minYs[i] * yy + dy) + pixelAdjust;
close = Math.round(closes[i] * yy + dy) + pixelAdjust;
mid = Math.round(dataX[startIdxs[i]] * xx + dx) + pixelAdjust;
if (renderer) {
ctx.save();
ctx.beginPath();
rendererConfig.open = open;
rendererConfig.high = high;
rendererConfig.low = low;
rendererConfig.close = close;
rendererConfig.mid = mid;
rendererConfig.halfWidth = halfWidth;
rendererParams = [me, rendererConfig, me.rendererData, i];
rendererChanges =
Ext.callback(renderer, null, rendererParams, 0, me.getSeries());
Ext.apply(ctx, rendererChanges);
}
me[ohlcType](ctx, open, high, low, close, mid, halfWidth);
if (renderer) {
ctx.fillStroke(template.attr);
ctx.restore();
}
}
}
if (!renderer) {
ctx.fillStroke(template.attr);
}
ctx.restore();
}
});