Skip to content

Commit

Permalink
Fix inRange for full circle arc
Browse files Browse the repository at this point in the history
  • Loading branch information
kurkle committed Nov 17, 2021
1 parent 5fca259 commit 512cc47
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 14 deletions.
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

0 comments on commit 512cc47

Please sign in to comment.