@nativescript-community/ui-chart
Version:
A powerful chart / graph plugin, supporting line, bar, pie, radar, bubble, and candlestick charts as well as scaling, panning and animations.
366 lines • 17.4 kB
JavaScript
import { BarBuffer } from '../buffer/BarBuffer';
import { Utils } from '../utils/Utils';
import { BarLineScatterCandleBubbleRenderer } from './BarLineScatterCandleBubbleRenderer';
export class BarChartRenderer extends BarLineScatterCandleBubbleRenderer {
constructor(chart, animator, viewPortHandler) {
super(animator, viewPortHandler);
this.mChart = chart;
}
get highlightPaint() {
if (!this.mHighlightPaint) {
this.mHighlightPaint = Utils.getTemplatePaint('black-fill');
// set alpha after color
this.mHighlightPaint.setAlpha(120);
}
return this.mHighlightPaint;
}
get barBorderPaint() {
if (!this.mBarBorderPaint) {
this.mBarBorderPaint = Utils.getTemplatePaint('black-stroke');
}
return this.mBarBorderPaint;
}
get shadowPaint() {
if (!this.mShadowPaint) {
this.mShadowPaint = Utils.getTemplatePaint('black-fill');
}
return this.mShadowPaint;
}
initBuffers() {
const barData = this.mChart.barData;
this.mBarBuffers = [];
for (let i = 0; i < barData.dataSetCount; i++) {
const set = barData.getDataSetByIndex(i);
this.mBarBuffers.push(new BarBuffer(set.entryCount * 4 * (set.stacked ? set.stackSize : 1), barData.dataSetCount, set.stacked));
}
}
drawData(c) {
const barData = this.mChart.barData;
for (let i = 0; i < barData.dataSetCount; i++) {
const set = barData.getDataSetByIndex(i);
if (set.visible) {
this.drawDataSet(c, set, i);
}
}
}
drawDataSet(c, dataSet, index) {
const chart = this.mChart;
const trans = chart.getTransformer(dataSet.axisDependency);
const drawBorder = dataSet.barBorderWidth > 0;
let borderPaint;
if (drawBorder) {
borderPaint = this.barBorderPaint;
borderPaint.setColor(dataSet.barBorderColor);
borderPaint.setStrokeWidth(dataSet.barBorderWidth);
}
const phaseX = this.animator.phaseX;
const phaseY = this.animator.phaseY;
const barData = chart.barData;
let barWidth = barData.barWidth;
if (barData.fixedBarScale) {
const scaleX = chart.viewPortScaleX;
barWidth /= scaleX;
}
// draw the bar shadow before the values
if (chart.drawBarShadowEnabled) {
const paint = this.shadowPaint;
paint.setColor(dataSet.barShadowColor);
const barWidthHalf = barWidth / 2;
let x;
const barShadowRectBuffer = Utils.getTempRectF();
for (let i = 0, count = Math.min(Math.ceil(dataSet.entryCount * phaseX), dataSet.entryCount); i < count; i++) {
const e = dataSet.getEntryForIndex(i);
x = dataSet.getEntryXValue(e, i);
barShadowRectBuffer.left = x - barWidthHalf;
barShadowRectBuffer.right = x + barWidthHalf;
trans.rectValueToPixel(barShadowRectBuffer);
if (!this.mViewPortHandler.isInBoundsLeft(barShadowRectBuffer.right)) {
continue;
}
if (!this.mViewPortHandler.isInBoundsRight(barShadowRectBuffer.left)) {
break;
}
barShadowRectBuffer.top = this.mViewPortHandler.contentTop;
barShadowRectBuffer.bottom = this.mViewPortHandler.contentBottom;
c.drawRect(barShadowRectBuffer, paint);
}
}
// initialize the buffer
const buffer = this.mBarBuffers[index];
buffer.setPhases(phaseX, phaseY);
buffer.dataSetIndex = index;
buffer.inverted = chart.isInverted(dataSet.axisDependency);
buffer.barWidth = barWidth;
buffer.yAxisMin = chart.getAxis(dataSet.axisDependency).axisMinimum;
buffer.yAxisMax = chart.getAxis(dataSet.axisDependency).axisMaximum;
const barsCount = buffer.feed(dataSet);
trans.pointValuesToPixel(buffer.buffer);
const isSingleColor = !dataSet.colors || dataSet.colors.length === 1;
// const isInverted = chart.isInverted(dataSet.axisDependency);
const renderPaint = this.renderPaint;
const previousShader = renderPaint.getShader();
const shader = dataSet.fillShader;
if (shader) {
renderPaint.setShader(shader);
}
if (isSingleColor) {
renderPaint.setColor(dataSet.color);
}
const customRender = chart.customRenderer;
for (let j = 0; j < barsCount; j += 1) {
const left = buffer.buffer[j * 4];
const top = buffer.buffer[j * 4 + 1];
const right = buffer.buffer[j * 4 + 2];
const bottom = buffer.buffer[j * 4 + 3];
if (!this.mViewPortHandler.isInBoundsLeft(right) || (left === right && top === bottom)) {
continue;
}
if (!this.mViewPortHandler.isInBoundsRight(left)) {
break;
}
if (!isSingleColor) {
// Set the color for the currently drawn value. If the index
// is out of bounds, reuse colors.
renderPaint.setColor(dataSet.getColor(j));
}
if (customRender && customRender.drawBar) {
const e = buffer.entries[j];
customRender.drawBar(c, e, dataSet, left, top, right, bottom, renderPaint);
}
else {
c.drawRect(left, top, right, bottom, renderPaint);
if (drawBorder) {
c.drawRect(left, top, right, bottom, borderPaint);
}
}
}
renderPaint.setShader(previousShader);
return true;
}
prepareBarHighlight(x, y1, y2, barWidthHalf, trans, barRect) {
const left = x - barWidthHalf;
const right = x + barWidthHalf;
const top = y1;
const bottom = y2;
barRect.set(left, top, right, bottom);
trans.rectToPixelPhase(barRect, this.animator.phaseY);
}
drawValues(c) {
const chart = this.mChart;
const data = chart.barData;
const dataSets = data.dataSets;
if (!this.isDrawingValuesAllowed(chart) || dataSets.some((d) => d.drawValuesEnabled || d.drawIconsEnabled) === false) {
return;
}
// if values are drawn
const valueOffsetPlus = 4.5;
let posOffset = 0;
let negOffset = 0;
const drawValueAboveBar = chart.drawValueAboveBarEnabled;
const paint = this.valuePaint;
const customRender = chart.customRenderer;
for (let i = 0; i < chart.barData.dataSetCount; i++) {
const dataSet = dataSets[i];
if (!this.shouldDrawValues(dataSet)) {
continue;
}
const yKey = dataSet.yProperty;
// apply the text-styling defined by the DataSet
this.applyValueTextStyle(dataSet);
const isInverted = chart.isInverted(dataSet.axisDependency);
// calculate the correct offset depending on the draw position of
// the value
const valueTextHeight = Utils.calcTextHeight(paint, '8');
const valuesOffset = dataSet.valuesOffset;
posOffset = drawValueAboveBar ? -(valueOffsetPlus + valuesOffset.y) : valueTextHeight + (valueOffsetPlus + valuesOffset.y);
negOffset = drawValueAboveBar ? valueTextHeight + (valueOffsetPlus + valuesOffset.y) : -(valueOffsetPlus + valuesOffset.y);
if (isInverted) {
posOffset = -posOffset - valueTextHeight;
negOffset = -negOffset - valueTextHeight;
}
// get the buffer
const buffer = this.mBarBuffers[i];
const phaseY = this.animator.phaseY;
const formatter = dataSet.valueFormatter;
const iconsOffset = dataSet.iconsOffset;
// if only single values are drawn (sum)
const isDrawValuesEnabled = dataSet.drawValuesEnabled;
const isDrawIconsEnabled = dataSet.drawIconsEnabled;
if (!dataSet.stacked) {
for (let j = 0; j < buffer.length * this.animator.phaseX; j += 4) {
const x = (buffer.buffer[j] + buffer.buffer[j + 2]) / 2;
if (!this.mViewPortHandler.isInBoundsRight(x)) {
break;
}
const index = j / 4;
const entry = dataSet.getEntryForIndex(index);
const val = entry[yKey];
if (!this.mViewPortHandler.isInBoundsY(buffer.buffer[j + (val >= 0 ? 1 : 3)]) || !this.mViewPortHandler.isInBoundsLeft(x)) {
continue;
}
if (isDrawValuesEnabled) {
this.drawValue(c, chart, dataSet, i, entry, index, (formatter.getBarLabel || formatter.getFormattedValue).call(formatter, val, entry), x + valuesOffset.x, val >= 0 ? buffer.buffer[j + 1] + posOffset : buffer.buffer[j + 3] + negOffset, dataSet.getValueTextColor(index), paint, customRender);
}
if (isDrawIconsEnabled) {
let px = x;
let py = val >= 0 ? buffer.buffer[j + 1] + posOffset : buffer.buffer[j + 3] + negOffset;
px += iconsOffset.x;
py += iconsOffset.y;
this.drawIcon(c, chart, dataSet, i, entry, index, dataSet.getEntryIcon(entry), px, py, customRender);
}
}
// if we have stacks
}
else {
const trans = chart.getTransformer(dataSet.axisDependency);
let bufferIndex = 0;
let index = 0;
while (index < dataSet.entryCount * this.animator.phaseX) {
const entry = dataSet.getEntryForIndex(index);
const vals = entry.yVals;
const x = (buffer.buffer[bufferIndex] + buffer.buffer[bufferIndex + 2]) / 2;
const color = dataSet.getValueTextColor(index);
// we still draw stacked bars, but there is one
// non-stacked
// in between
if (!vals) {
if (!this.mViewPortHandler.isInBoundsRight(x)) {
break;
}
if (!this.mViewPortHandler.isInBoundsY(buffer.buffer[bufferIndex + (entry[yKey] >= 0 ? 1 : 3)]) || !this.mViewPortHandler.isInBoundsLeft(x)) {
continue;
}
if (isDrawValuesEnabled) {
this.drawValue(c, chart, dataSet, i, entry, index, (formatter.getBarLabel || formatter.getFormattedValue).call(formatter, entry[yKey], entry), x, entry[yKey] >= 0 ? buffer.buffer[bufferIndex + 1] + posOffset : buffer.buffer[bufferIndex + 3] + negOffset, color, paint, customRender);
}
if (isDrawIconsEnabled && entry.icon) {
const icon = entry.icon;
let px = x;
let py = entry[yKey] >= 0 ? buffer.buffer[bufferIndex + 1] + posOffset : buffer.buffer[bufferIndex + 3] + negOffset;
px += iconsOffset.x;
py += iconsOffset.y;
this.drawIcon(c, chart, dataSet, i, entry, index, dataSet.getEntryIcon(entry), px, py, customRender);
}
// draw stack values
}
else {
if (!this.mTransformedBuffer || this.mTransformedBuffer.length !== vals.length * 2) {
this.mTransformedBuffer = Utils.createArrayBuffer(vals.length * 2);
}
const transformed = this.mTransformedBuffer;
let posY = 0;
let negY = -entry.negativeSum;
for (let k = 0, idx = 0; k < transformed.length; k += 2, idx++) {
const value = vals[idx];
let y;
if (value === 0 && (posY === 0 || negY === 0)) {
// Take care of the situation of a 0.0 value, which overlaps a non-zero bar
y = value;
}
else if (value >= 0) {
posY += value;
y = posY;
}
else {
y = negY;
negY -= value;
}
transformed[k + 1] = y * phaseY;
}
const points = Utils.pointsFromBuffer(transformed);
trans.pointValuesToPixel(points);
for (let k = 0; k < points.length; k += 2) {
const val = vals[k / 2];
const drawBelow = (val === 0 && negY === 0 && posY > 0) || val < 0;
const y = points[k + 1] + (drawBelow ? negOffset : posOffset);
if (!this.mViewPortHandler.isInBoundsRight(x)) {
break;
}
if (!this.mViewPortHandler.isInBoundsY(y) || !this.mViewPortHandler.isInBoundsLeft(x)) {
continue;
}
if (dataSet.drawValuesEnabled) {
this.drawValue(c, chart, dataSet, i, entry, index, (formatter.getBarStackedLabel || formatter.getFormattedValue).call(formatter, val, entry), x, y, color, paint, customRender);
}
if (dataSet.drawIconsEnabled) {
this.drawIcon(c, chart, dataSet, i, entry, index, dataSet.getEntryIcon(entry), x + iconsOffset.x, y + iconsOffset.y, customRender);
}
}
}
bufferIndex = !vals ? bufferIndex + 4 : bufferIndex + 4 * vals.length;
index++;
}
}
}
}
drawHighlighted(c, indices) {
const barData = this.mChart.barData;
let entry, index;
const barRect = Utils.getTempRectF();
for (let i = 0; i < indices.length; i++) {
const high = indices[i];
const set = barData.getDataSetByIndex(high.dataSetIndex);
if (!set || !set.highlightEnabled) {
continue;
}
if (high.entry) {
entry = high.entry;
index = high.entryIndex;
}
else {
const r = set.getEntryAndIndexForXValue(high.x, high.y);
entry = r.entry;
index = r.index;
}
if (!this.isInBoundsX(entry, set)) {
continue;
}
const yKey = set.yProperty;
const trans = this.mChart.getTransformer(set.axisDependency);
const paint = this.highlightPaint;
paint.setColor(set.highlightColor);
paint.setAlpha(set.highLightAlpha);
const isStack = high.stackIndex >= 0 && entry.isStacked ? true : false;
let y1;
let y2;
if (isStack) {
if (this.mChart.highlightFullBarEnabled) {
y1 = entry.positiveSum;
y2 = -entry.negativeSum;
}
else {
const range = entry.ranges[high.stackIndex];
y1 = range[0];
y2 = range[1];
}
}
else {
const minAxisValue = this.mChart.getAxis(set.axisDependency).axisMinimum;
y1 = entry[yKey];
y2 = minAxisValue >= 0 ? minAxisValue : 0;
}
const x = set.getEntryXValue(entry, index);
this.prepareBarHighlight(x, y1, y2, barData.barWidth / 2, trans, barRect);
this.setHighlightDrawPos(high, barRect);
const customRender = this.mChart.customRenderer;
if (customRender && customRender.drawHighlight) {
const rect = barRect;
customRender.drawHighlight(c, high, rect.left, rect.top, rect.right, rect.bottom, paint);
}
else {
c.drawRect(barRect, paint);
}
}
}
/**
* Sets the drawing position of the highlight object based on the riven bar-rect.
* @param high
* @param bar
*/
setHighlightDrawPos(high, bar) {
high.drawX = bar.centerX();
high.drawY = bar.top;
}
drawExtras(c) { }
}
//# sourceMappingURL=BarChartRenderer.js.map