Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix inRange for full circle arc #9871

Merged
merged 3 commits into from Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/elements/element.arc.js
@@ -1,6 +1,6 @@
import Element from '../core/core.element';
import {_angleBetween, getAngleFromPoint, TAU, HALF_PI} from '../helpers/index';
import {PI, _limitValue} from '../helpers/helpers.math';
import {_angleBetween, getAngleFromPoint, TAU, HALF_PI, valueOrDefault} from '../helpers/index';
import {PI, _isBetween, _limitValue} from '../helpers/helpers.math';
import {_readValueToProps} from '../helpers/helpers.options';

function clipArc(ctx, element, endAngle) {
Expand Down Expand Up @@ -282,8 +282,9 @@ export default class ArcElement extends Element {
'circumference'
], useFinalPosition);
const rAdjust = this.options.spacing / 2;
const betweenAngles = circumference >= TAU || _angleBetween(angle, startAngle, endAngle);
const withinRadius = (distance >= innerRadius + rAdjust && distance <= outerRadius + rAdjust);
const _circumference = valueOrDefault(circumference, endAngle - startAngle);
const betweenAngles = _circumference >= TAU || _angleBetween(angle, startAngle, endAngle);
const withinRadius = _isBetween(distance, innerRadius + rAdjust, outerRadius + rAdjust);

return (betweenAngles && withinRadius);
}
Expand Down
6 changes: 3 additions & 3 deletions src/elements/element.bar.js
@@ -1,5 +1,5 @@
import Element from '../core/core.element';
import {isObject, _limitValue} from '../helpers';
import {isObject, _isBetween, _limitValue} from '../helpers';
import {addRoundedRectPath} from '../helpers/helpers.canvas';
import {toTRBL, toTRBLCorners} from '../helpers/helpers.options';

Expand Down Expand Up @@ -105,8 +105,8 @@ function inRange(bar, x, y, useFinalPosition) {
const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition);

return bounds
&& (skipX || x >= bounds.left && x <= bounds.right)
&& (skipY || y >= bounds.top && y <= bounds.bottom);
&& (skipX || _isBetween(x, bounds.left, bounds.right))
&& (skipY || _isBetween(y, bounds.top, bounds.bottom));
}

function hasRadius(radius) {
Expand Down
15 changes: 15 additions & 0 deletions src/helpers/helpers.math.js
Expand Up @@ -173,6 +173,21 @@ export function _limitValue(value, min, max) {
return Math.max(min, Math.min(max, value));
}

/**
* @param {number} value
* @private
*/
export function _int16Range(value) {
return _limitValue(value, -32768, 32767);
}

/**
* @param {number} value
* @param {number} start
* @param {number} end
* @param {number} [epsilon]
* @private
*/
export function _isBetween(value, start, end, epsilon = 1e-6) {
return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon;
}
4 changes: 2 additions & 2 deletions src/helpers/helpers.segment.js
@@ -1,4 +1,4 @@
import {_angleBetween, _angleDiff, _normalizeAngle} from './helpers.math';
import {_angleBetween, _angleDiff, _isBetween, _normalizeAngle} from './helpers.math';
import {createContext} from './helpers.options';

/**
Expand All @@ -16,7 +16,7 @@ function propertyFn(property) {
};
}
return {
between: (n, s, e) => n >= Math.min(s, e) && n <= Math.max(e, s),
between: _isBetween,
compare: (a, b) => a - b,
normalize: x => x
};
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/plugin.filler.js
Expand Up @@ -8,7 +8,7 @@ import LineElement from '../elements/element.line';
import {_boundSegment, _boundSegments} from '../helpers/helpers.segment';
import {clipArea, unclipArea} from '../helpers/helpers.canvas';
import {isArray, isFinite, isObject, valueOrDefault} from '../helpers/helpers.core';
import {TAU, _normalizeAngle} from '../helpers/helpers.math';
import {TAU, _isBetween, _normalizeAngle} from '../helpers/helpers.math';

/**
* @typedef { import('../core/core.controller').default } Chart
Expand Down Expand Up @@ -293,7 +293,7 @@ function findPoint(line, sourcePoint, property) {
const segment = segments[i];
const firstValue = linePoints[segment.start][property];
const lastValue = linePoints[segment.end][property];
if (pointValue >= firstValue && pointValue <= lastValue) {
if (_isBetween(pointValue, firstValue, lastValue)) {
first = pointValue === firstValue;
last = pointValue === lastValue;
break;
Expand Down
8 changes: 5 additions & 3 deletions src/plugins/plugin.legend.js
Expand Up @@ -5,7 +5,7 @@ import {addRoundedRectPath, drawPoint, renderText} from '../helpers/helpers.canv
import {
callback as call, valueOrDefault, toFont,
toPadding, getRtlAdapter, overrideTextDirection, restoreTextDirection,
clipArea, unclipArea
clipArea, unclipArea, _isBetween
} from '../helpers/index';
import {_toLeftRightCenter, _alignStartEnd, _textX} from '../helpers/helpers.extras';
import {toTRBLCorners} from '../helpers/helpers.options';
Expand Down Expand Up @@ -493,13 +493,15 @@ export class Legend extends Element {
_getLegendItemAt(x, y) {
let i, hitBox, lh;

if (x >= this.left && x <= this.right && y >= this.top && y <= this.bottom) {
if (_isBetween(x, this.left, this.right)
&& _isBetween(y, this.top, this.bottom)) {
// See if we are touching one of the dataset boxes
lh = this.legendHitBoxes;
for (i = 0; i < lh.length; ++i) {
hitBox = lh[i];

if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
if (_isBetween(x, hitBox.left, hitBox.left + hitBox.width)
&& _isBetween(y, hitBox.top, hitBox.top + hitBox.height)) {
// Touching an element
return this.legendItems[i];
}
Expand Down
33 changes: 33 additions & 0 deletions test/specs/element.arc.tests.js
Expand Up @@ -23,6 +23,39 @@ describe('Arc element tests', function() {
expect(arc.inRange(-1.0 * Math.sqrt(7), Math.sqrt(7))).toBe(false);
});

it ('should determine if in range when full circle', function() {
// Mock out the arc as if the controller put it there
var arc = new Chart.elements.ArcElement({
startAngle: 0,
endAngle: Math.PI * 2,
x: 0,
y: 0,
innerRadius: 5,
outerRadius: 10,
options: {
spacing: 0,
offset: 0,
}
});

for (const radius of [5, 7.5, 10]) {
for (let angle = 0; angle <= 360; angle += 22.5) {
const rad = angle / 180 * Math.PI;
const x = Math.sin(rad) * radius;
const y = Math.cos(rad) * radius;
expect(arc.inRange(x, y)).withContext(`radius: ${radius}, angle: ${angle}`).toBeTrue();
}
}
for (const radius of [4, 11]) {
for (let angle = 0; angle <= 360; angle += 22.5) {
const rad = angle / 180 * Math.PI;
const x = Math.sin(rad) * radius;
const y = Math.cos(rad) * radius;
expect(arc.inRange(x, y)).withContext(`radius: ${radius}, angle: ${angle}`).toBeFalse();
}
}
});

it ('should include spacing for in range check', function() {
// Mock out the arc as if the controller put it there
var arc = new Chart.elements.ArcElement({
Expand Down