Skip to content

Commit

Permalink
feat: remove line element from scatter controller (#10439)
Browse files Browse the repository at this point in the history
* feat: remove line element from scatter controller default config

* feat: move common controllers methods to helpers and add types

* feat: mark methods for scatter and line conntrollers as private

* fix: fix error when showline is true at root options and add tests

* feat: remove else inside scatter controller update

* fix: update getStartAndCountOFVisiblePoints helper code
  • Loading branch information
thabarbados committed Aug 1, 2022
1 parent 6feb48b commit 03e9194
Show file tree
Hide file tree
Showing 4 changed files with 296 additions and 57 deletions.
59 changes: 4 additions & 55 deletions src/controllers/controller.line.js
@@ -1,7 +1,7 @@
import DatasetController from '../core/core.datasetController';
import {isNullOrUndef} from '../helpers';
import {_limitValue, isNumber} from '../helpers/helpers.math';
import {_lookupByKey} from '../helpers/helpers.collection';
import {isNumber} from '../helpers/helpers.math';
import {_getStartAndCountOfVisiblePoints, _scaleRangesChanged} from '../helpers/helpers.extras';

export default class LineController extends DatasetController {

Expand All @@ -16,12 +16,12 @@ export default class LineController extends DatasetController {
const {dataset: line, data: points = [], _dataset} = meta;
// @ts-ignore
const animationsDisabled = this.chart._animationsDisabled;
let {start, count} = getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);
let {start, count} = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);

this._drawStart = start;
this._drawCount = count;

if (scaleRangesChanged(meta)) {
if (_scaleRangesChanged(meta)) {
start = 0;
count = points.length;
}
Expand Down Expand Up @@ -133,54 +133,3 @@ LineController.overrides = {
},
}
};

function getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {
const pointCount = points.length;

let start = 0;
let count = pointCount;

if (meta._sorted) {
const {iScale, _parsed} = meta;
const axis = iScale.axis;
const {min, max, minDefined, maxDefined} = iScale.getUserBounds();

if (minDefined) {
start = _limitValue(Math.min(
_lookupByKey(_parsed, iScale.axis, min).lo,
animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo),
0, pointCount - 1);
}
if (maxDefined) {
count = _limitValue(Math.max(
_lookupByKey(_parsed, iScale.axis, max, true).hi + 1,
animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1),
start, pointCount) - start;
} else {
count = pointCount - start;
}
}

return {start, count};
}

function scaleRangesChanged(meta) {
const {xScale, yScale, _scaleRanges} = meta;
const newRanges = {
xmin: xScale.min,
xmax: xScale.max,
ymin: yScale.min,
ymax: yScale.max
};
if (!_scaleRanges) {
meta._scaleRanges = newRanges;
return true;
}
const changed = _scaleRanges.xmin !== xScale.min
|| _scaleRanges.xmax !== xScale.max
|| _scaleRanges.ymin !== yScale.min
|| _scaleRanges.ymax !== yScale.max;

Object.assign(_scaleRanges, newRanges);
return changed;
}
124 changes: 122 additions & 2 deletions src/controllers/controller.scatter.js
@@ -1,7 +1,125 @@
import LineController from './controller.line';
import DatasetController from '../core/core.datasetController';
import {isNullOrUndef} from '../helpers';
import {isNumber} from '../helpers/helpers.math';
import {_getStartAndCountOfVisiblePoints, _scaleRangesChanged} from '../helpers/helpers.extras';
import registry from '../core/core.registry';

export default class ScatterController extends LineController {
export default class ScatterController extends DatasetController {
update(mode) {
const meta = this._cachedMeta;
const {data: points = []} = meta;
// @ts-ignore
const animationsDisabled = this.chart._animationsDisabled;
let {start, count} = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);

this._drawStart = start;
this._drawCount = count;

if (_scaleRangesChanged(meta)) {
start = 0;
count = points.length;
}

if (this.options.showLine) {

const {dataset: line, _dataset} = meta;

// Update Line
line._chart = this.chart;
line._datasetIndex = this.index;
line._decimated = !!_dataset._decimated;
line.points = points;

const options = this.resolveDatasetElementOptions(mode);
options.segment = this.options.segment;
this.updateElement(line, undefined, {
animated: !animationsDisabled,
options
}, mode);
}

// Update Points
this.updateElements(points, start, count, mode);
}

addElements() {
const {showLine} = this.options;

if (!this.datasetElementType && showLine) {
this.datasetElementType = registry.getElement('line');
}

super.addElements();
}

updateElements(points, start, count, mode) {
const reset = mode === 'reset';
const {iScale, vScale, _stacked, _dataset} = this._cachedMeta;
const firstOpts = this.resolveDataElementOptions(start, mode);
const sharedOptions = this.getSharedOptions(firstOpts);
const includeOptions = this.includeOptions(mode, sharedOptions);
const iAxis = iScale.axis;
const vAxis = vScale.axis;
const {spanGaps, segment} = this.options;
const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';
let prevParsed = start > 0 && this.getParsed(start - 1);

for (let i = start; i < start + count; ++i) {
const point = points[i];
const parsed = this.getParsed(i);
const properties = directUpdate ? point : {};
const nullData = isNullOrUndef(parsed[vAxis]);
const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);
const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);

properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;
properties.stop = i > 0 && (Math.abs(parsed[iAxis] - prevParsed[iAxis])) > maxGapLength;
if (segment) {
properties.parsed = parsed;
properties.raw = _dataset.data[i];
}

if (includeOptions) {
properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);
}

if (!directUpdate) {
this.updateElement(point, i, properties, mode);
}

prevParsed = parsed;
}

this.updateSharedOptions(sharedOptions, mode, firstOpts);
}

/**
* @protected
*/
getMaxOverflow() {
const meta = this._cachedMeta;
const data = meta.data || [];

if (!this.options.showLine) {
let max = 0;
for (let i = data.length - 1; i >= 0; --i) {
max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);
}
return max > 0 && max;
}

const dataset = meta.dataset;
const border = dataset.options && dataset.options.borderWidth || 0;

if (!data.length) {
return border;
}

const firstPoint = data[0].size(this.resolveDataElementOptions(0));
const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));
return Math.max(border, firstPoint, lastPoint) / 2;
}
}

ScatterController.id = 'scatter';
Expand All @@ -10,6 +128,8 @@ ScatterController.id = 'scatter';
* @type {any}
*/
ScatterController.defaults = {
datasetElementType: false,
dataElementType: 'point',
showLine: false,
fill: false
};
Expand Down
67 changes: 67 additions & 0 deletions src/helpers/helpers.extras.js
@@ -1,3 +1,5 @@
import {_limitValue} from './helpers.math';
import {_lookupByKey} from './helpers.collection';

export function fontString(pixelSize, fontStyle, fontFamily) {
return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
Expand Down Expand Up @@ -87,3 +89,68 @@ export const _textX = (align, left, right, rtl) => {
const check = rtl ? 'left' : 'right';
return align === check ? right : align === 'center' ? (left + right) / 2 : left;
};

/**
* Return start and count of visible points.
* @param {object} meta - dataset meta.
* @param {array} points - array of point elements.
* @param {boolean} animationsDisabled - if true animation is disabled.
* @returns {{start: number; count: number}}
* @private
*/
export function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {
const pointCount = points.length;

let start = 0;
let count = pointCount;

if (meta._sorted) {
const {iScale, _parsed} = meta;
const axis = iScale.axis;
const {min, max, minDefined, maxDefined} = iScale.getUserBounds();

if (minDefined) {
start = _limitValue(Math.min(
_lookupByKey(_parsed, iScale.axis, min).lo,
animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo),
0, pointCount - 1);
}
if (maxDefined) {
count = _limitValue(Math.max(
_lookupByKey(_parsed, iScale.axis, max, true).hi + 1,
animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1),
start, pointCount) - start;
} else {
count = pointCount - start;
}
}

return {start, count};
}

/**
* Checks if the scale ranges have changed.
* @param {object} meta - dataset meta.
* @returns {boolean}
* @private
*/
export function _scaleRangesChanged(meta) {
const {xScale, yScale, _scaleRanges} = meta;
const newRanges = {
xmin: xScale.min,
xmax: xScale.max,
ymin: yScale.min,
ymax: yScale.max
};
if (!_scaleRanges) {
meta._scaleRanges = newRanges;
return true;
}
const changed = _scaleRanges.xmin !== xScale.min
|| _scaleRanges.xmax !== xScale.max
|| _scaleRanges.ymin !== yScale.min
|| _scaleRanges.ymax !== yScale.max;

Object.assign(_scaleRanges, newRanges);
return changed;
}
103 changes: 103 additions & 0 deletions test/specs/controller.scatter.tests.js
Expand Up @@ -61,4 +61,107 @@ describe('Chart.controllers.scatter', function() {
await jasmine.triggerMouseEvent(chart, 'mousemove', point);
expect(chart.tooltip.body.length).toEqual(1);
});

it('should not create line element by default', function() {
var chart = window.acquireChart({
type: 'scatter',
data: {
datasets: [{
data: [{
x: 10,
y: 15
},
{
x: 12,
y: 10
}],
label: 'dataset1'
},
{
data: [{
x: 20,
y: 10
},
{
x: 4,
y: 8
}],
label: 'dataset2'
}]
},
});

var meta = chart.getDatasetMeta(0);
expect(meta.dataset instanceof Chart.elements.LineElement).toBe(false);
});

it('should create line element if showline is true at datasets options', function() {
var chart = window.acquireChart({
type: 'scatter',
data: {
datasets: [{
showLine: true,
data: [{
x: 10,
y: 15
},
{
x: 12,
y: 10
}],
label: 'dataset1'
},
{
data: [{
x: 20,
y: 10
},
{
x: 4,
y: 8
}],
label: 'dataset2'
}]
},
});

var meta = chart.getDatasetMeta(0);
expect(meta.dataset instanceof Chart.elements.LineElement).toBe(true);
});

it('should create line element if showline is true at root options', function() {
var chart = window.acquireChart({
type: 'scatter',
data: {
datasets: [{
data: [{
x: 10,
y: 15
},
{
x: 12,
y: 10
}],
label: 'dataset1'
},
{
data: [{
x: 20,
y: 10
},
{
x: 4,
y: 8
}],
label: 'dataset2'
}]
},
options: {
showLine: true
}
});

var meta = chart.getDatasetMeta(0);
expect(meta.dataset instanceof Chart.elements.LineElement).toBe(true);
});
});

0 comments on commit 03e9194

Please sign in to comment.