| import * as zrUtil from 'zrender/src/core/util'; |
| import { parsePercent } from '../../util/number'; |
| import { subPixelOptimize } from '../../util/graphic'; |
| var retrieve2 = zrUtil.retrieve2; |
| export default function (ecModel) { |
| ecModel.eachSeriesByType('candlestick', function (seriesModel) { |
| var coordSys = seriesModel.coordinateSystem; |
| var data = seriesModel.getData(); |
| var candleWidth = calculateCandleWidth(seriesModel, data); |
| var chartLayout = seriesModel.get('layout'); |
| var variableDim = chartLayout === 'horizontal' ? 0 : 1; |
| var constDim = 1 - variableDim; |
| var coordDims = ['x', 'y']; |
| var vDims = []; |
| var cDim; |
| zrUtil.each(data.dimensions, function (dimName) { |
| var dimInfo = data.getDimensionInfo(dimName); |
| var coordDim = dimInfo.coordDim; |
| |
| if (coordDim === coordDims[constDim]) { |
| vDims.push(dimName); |
| } else if (coordDim === coordDims[variableDim]) { |
| cDim = dimName; |
| } |
| }); |
| |
| if (cDim == null || vDims.length < 4) { |
| return; |
| } |
| |
| var dataIndex = 0; |
| data.each([cDim].concat(vDims), function () { |
| var args = arguments; |
| var axisDimVal = args[0]; |
| var idx = args[vDims.length + 1]; |
| var openVal = args[1]; |
| var closeVal = args[2]; |
| var lowestVal = args[3]; |
| var highestVal = args[4]; |
| var ocLow = Math.min(openVal, closeVal); |
| var ocHigh = Math.max(openVal, closeVal); |
| var ocLowPoint = getPoint(ocLow); |
| var ocHighPoint = getPoint(ocHigh); |
| var lowestPoint = getPoint(lowestVal); |
| var highestPoint = getPoint(highestVal); |
| var whiskerEnds = [[subPixelOptimizePoint(highestPoint), subPixelOptimizePoint(ocHighPoint)], [subPixelOptimizePoint(lowestPoint), subPixelOptimizePoint(ocLowPoint)]]; |
| var bodyEnds = []; |
| addBodyEnd(ocHighPoint, 0); |
| addBodyEnd(ocLowPoint, 1); |
| var sign; |
| |
| if (openVal > closeVal) { |
| sign = -1; |
| } else if (openVal < closeVal) { |
| sign = 1; |
| } else { |
| // If close === open, compare with close of last record |
| if (dataIndex > 0) { |
| sign = data.getItemModel(dataIndex - 1).get()[2] <= closeVal ? 1 : -1; |
| } else { |
| // No record of previous, set to be positive |
| sign = 1; |
| } |
| } |
| |
| data.setItemLayout(idx, { |
| chartLayout: chartLayout, |
| sign: sign, |
| initBaseline: openVal > closeVal ? ocHighPoint[constDim] : ocLowPoint[constDim], |
| // open point. |
| bodyEnds: bodyEnds, |
| whiskerEnds: whiskerEnds, |
| brushRect: makeBrushRect() |
| }); |
| ++dataIndex; |
| |
| function getPoint(val) { |
| var p = []; |
| p[variableDim] = axisDimVal; |
| p[constDim] = val; |
| return isNaN(axisDimVal) || isNaN(val) ? [NaN, NaN] : coordSys.dataToPoint(p); |
| } |
| |
| function addBodyEnd(point, start) { |
| var point1 = point.slice(); |
| var point2 = point.slice(); |
| point1[variableDim] = subPixelOptimize(point1[variableDim] + candleWidth / 2, 1, false); |
| point2[variableDim] = subPixelOptimize(point2[variableDim] - candleWidth / 2, 1, true); |
| start ? bodyEnds.push(point1, point2) : bodyEnds.push(point2, point1); |
| } |
| |
| function makeBrushRect() { |
| var pmin = getPoint(Math.min(openVal, closeVal, lowestVal, highestVal)); |
| var pmax = getPoint(Math.max(openVal, closeVal, lowestVal, highestVal)); |
| pmin[variableDim] -= candleWidth / 2; |
| pmax[variableDim] -= candleWidth / 2; |
| return { |
| x: pmin[0], |
| y: pmin[1], |
| width: constDim ? candleWidth : pmax[0] - pmin[0], |
| height: constDim ? pmax[1] - pmin[1] : candleWidth |
| }; |
| } |
| |
| function subPixelOptimizePoint(point) { |
| point[variableDim] = subPixelOptimize(point[variableDim], 1); |
| return point; |
| } |
| }, true); |
| }); |
| } |
| |
| function calculateCandleWidth(seriesModel, data) { |
| var baseAxis = seriesModel.getBaseAxis(); |
| var extent; |
| var bandWidth = baseAxis.type === 'category' ? baseAxis.getBandWidth() : (extent = baseAxis.getExtent(), Math.abs(extent[1] - extent[0]) / data.count()); |
| var barMaxWidth = parsePercent(retrieve2(seriesModel.get('barMaxWidth'), bandWidth), bandWidth); |
| var barMinWidth = parsePercent(retrieve2(seriesModel.get('barMinWidth'), 1), bandWidth); |
| var barWidth = seriesModel.get('barWidth'); |
| return barWidth != null ? parsePercent(barWidth, bandWidth) // Put max outer to ensure bar visible in spite of overlap. |
| : Math.max(Math.min(bandWidth / 2, barMaxWidth), barMinWidth); |
| } |