Skip to content

Commit

Permalink
chore: merge master into feat/annotation-rect
Browse files Browse the repository at this point in the history
  • Loading branch information
emmacunningham committed May 2, 2019
2 parents 73c33cd + f971d05 commit 921468f
Show file tree
Hide file tree
Showing 13 changed files with 337 additions and 154 deletions.
9 changes: 7 additions & 2 deletions src/lib/utils/scales/scale_band.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ScaleBand } from './scale_band';

describe.only('Scale Band', () => {
describe('Scale Band', () => {
it('shall clone domain and range arrays', () => {
const domain = [0, 1, 2, 3];
const range = [0, 100] as [number, number];
Expand Down Expand Up @@ -79,7 +79,12 @@ describe.only('Scale Band', () => {
expect(scale2.scale(3)).toBe(81.25);
// an empty 1/2 step place at the end
});
test('shall invert all values in range', () => {
it('shall not scale scale null values', () => {
const scale = new ScaleBand([0, 1, 2], [0, 120], undefined, 0.5);
expect(scale.scale(-1)).toBeUndefined();
expect(scale.scale(3)).toBeUndefined();
});
it('shall invert all values in range', () => {
const domain = ['a', 'b', 'c', 'd'];
const minRange = 0;
const maxRange = 100;
Expand Down
3 changes: 1 addition & 2 deletions src/lib/utils/scales/scale_band.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { scaleBand, scaleQuantize, ScaleQuantize } from 'd3-scale';
import { clamp } from '../commons';
import { StepType } from './scale_continuous';
import { ScaleType } from './scales';
import { Scale } from './scales';

Expand Down Expand Up @@ -61,7 +60,7 @@ export class ScaleBand implements Scale {
invert(value: any) {
return this.invertedScale(value);
}
invertWithStep(value: any, stepType?: StepType) {
invertWithStep(value: any) {
return this.invertedScale(value);
}
}
Expand Down
104 changes: 102 additions & 2 deletions src/lib/utils/scales/scale_continuous.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { DateTime } from 'luxon';
import { XDomain } from '../../series/domains/x_domain';
import { computeXScale } from '../../series/scales';
import { Domain } from '../domain';
import { ScaleBand } from './scale_band';
import { isLogarithmicScale, ScaleContinuous } from './scale_continuous';
import { ScaleType } from './scales';

describe('Scale Continuous', () => {
test('shall invert on continuous scale linear', () => {
const domain = [0, 2];
const domain: Domain = [0, 2];
const minRange = 0;
const maxRange = 100;
const scale = new ScaleContinuous(ScaleType.Linear, domain, [minRange, maxRange]);
Expand All @@ -26,7 +29,7 @@ describe('Scale Continuous', () => {
expect(scale.invert(100)).toBe(endTime.toMillis());
});
test('check if a scale is log scale', () => {
const domain = [0, 2];
const domain: Domain = [0, 2];
const range: [number, number] = [0, 100];
const scaleLinear = new ScaleContinuous(ScaleType.Linear, domain, range);
const scaleLog = new ScaleContinuous(ScaleType.Log, domain, range);
Expand All @@ -39,4 +42,101 @@ describe('Scale Continuous', () => {
expect(isLogarithmicScale(scaleSqrt)).toBe(false);
expect(isLogarithmicScale(scaleBand)).toBe(false);
});
test('can get the right x value on linear scale', () => {
const domain: Domain = [0, 2];
const data = [0, 0.5, 0.8, 2];
const range: [number, number] = [0, 2];
const scaleLinear = new ScaleContinuous(ScaleType.Linear, domain, range);
expect(scaleLinear.bandwidth).toBe(0);
expect(scaleLinear.invertWithStep(0, data)).toBe(0);
expect(scaleLinear.invertWithStep(0.1, data)).toBe(0);

expect(scaleLinear.invertWithStep(0.4, data)).toBe(0.5);
expect(scaleLinear.invertWithStep(0.5, data)).toBe(0.5);
expect(scaleLinear.invertWithStep(0.6, data)).toBe(0.5);

expect(scaleLinear.invertWithStep(0.7, data)).toBe(0.8);
expect(scaleLinear.invertWithStep(0.8, data)).toBe(0.8);
expect(scaleLinear.invertWithStep(0.9, data)).toBe(0.8);

expect(scaleLinear.invertWithStep(2, data)).toBe(2);

expect(scaleLinear.invertWithStep(1.7, data)).toBe(2);

expect(scaleLinear.invertWithStep(0.8 + (2 - 0.8) / 2, data)).toBe(0.8);
expect(scaleLinear.invertWithStep(0.8 + (2 - 0.8) / 2 - 0.01, data)).toBe(0.8);

expect(scaleLinear.invertWithStep(0.8 + (2 - 0.8) / 2 + 0.01, data)).toBe(2);
});
test('invert with step x value on linear band scale', () => {
const data = [0, 1, 2];
const xDomain: XDomain = {
domain: [0, 2],
isBandScale: true,
minInterval: 1,
scaleType: ScaleType.Linear,
type: 'xDomain',
};

const scaleLinear = computeXScale(xDomain, 1, 0, 120, 0);
expect(scaleLinear.bandwidth).toBe(40);
expect(scaleLinear.invertWithStep(0, data)).toBe(0);
expect(scaleLinear.invertWithStep(40, data)).toBe(1);

expect(scaleLinear.invertWithStep(41, data)).toBe(1);
expect(scaleLinear.invertWithStep(79, data)).toBe(1);

expect(scaleLinear.invertWithStep(80, data)).toBe(2);
expect(scaleLinear.invertWithStep(81, data)).toBe(2);
expect(scaleLinear.invertWithStep(120, data)).toBe(2);
});
test('can get the right x value on linear scale with regular band 1', () => {
const domain = [0, 100];
const data = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90];

// we tweak the maxRange removing the bandwidth to correctly compute
// a band linear scale in computeXScale
const range: [number, number] = [0, 100 - 10];
const scaleLinear = new ScaleContinuous(ScaleType.Linear, domain, range, 10, 10);
expect(scaleLinear.bandwidth).toBe(10);
expect(scaleLinear.invertWithStep(0, data)).toBe(0);
expect(scaleLinear.invertWithStep(10, data)).toBe(10);
expect(scaleLinear.invertWithStep(20, data)).toBe(20);
expect(scaleLinear.invertWithStep(90, data)).toBe(90);
});
test('can get the right x value on linear scale with band', () => {
const data = [0, 10, 20, 50, 90];
// we tweak the maxRange removing the bandwidth to correctly compute
// a band linear scale in computeXScale

const xDomain: XDomain = {
domain: [0, 100],
isBandScale: true,
minInterval: 10,
scaleType: ScaleType.Linear,
type: 'xDomain',
};

const scaleLinear = computeXScale(xDomain, 1, 0, 110, 0);
// const scaleLinear = new ScaleContinuous(ScaleType.Linear, domain, range, 10, 10);
expect(scaleLinear.bandwidth).toBe(10);

expect(scaleLinear.invertWithStep(0, data)).toBe(0);
expect(scaleLinear.invertWithStep(5, data)).toBe(0);
expect(scaleLinear.invertWithStep(9, data)).toBe(0);

expect(scaleLinear.invertWithStep(10, data)).toBe(10);
expect(scaleLinear.invertWithStep(11, data)).toBe(10);
expect(scaleLinear.invertWithStep(19, data)).toBe(10);

expect(scaleLinear.invertWithStep(20, data)).toBe(20);
expect(scaleLinear.invertWithStep(21, data)).toBe(20);
expect(scaleLinear.invertWithStep(25, data)).toBe(20);
expect(scaleLinear.invertWithStep(29, data)).toBe(20);
expect(scaleLinear.invertWithStep(30, data)).toBe(20);
expect(scaleLinear.invertWithStep(39, data)).toBe(20);
expect(scaleLinear.invertWithStep(40, data)).toBe(50);
expect(scaleLinear.invertWithStep(50, data)).toBe(50);
expect(scaleLinear.invertWithStep(90, data)).toBe(90);
});
});
95 changes: 30 additions & 65 deletions src/lib/utils/scales/scale_continuous.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { bisectLeft } from 'd3-array';
import { scaleLinear, scaleLog, scaleSqrt, scaleUtc } from 'd3-scale';
import { DateTime } from 'luxon';
import { clamp } from '../commons';
Expand Down Expand Up @@ -60,11 +61,6 @@ export function limitLogScaleDomain(domain: any[]) {
}
return domain;
}
export enum StepType {
StepBefore = 'before',
StepAfter = 'after',
Step = 'half',
}

export class ScaleContinuous implements Scale {
readonly bandwidth: number;
Expand Down Expand Up @@ -149,7 +145,7 @@ export class ScaleContinuous implements Scale {
});
} else {
if (this.minInterval > 0) {
const intervalCount = (this.domain[1] - this.domain[0]) / this.minInterval;
const intervalCount = Math.floor((this.domain[1] - this.domain[0]) / this.minInterval);
this.tickValues = new Array(intervalCount + 1).fill(0).map((d, i) => {
return this.domain[0] + i * this.minInterval;
});
Expand All @@ -173,10 +169,34 @@ export class ScaleContinuous implements Scale {
}
return invertedValue;
}
invertWithStep(value: number, stepType?: StepType) {
const invertedValue = this.invert(value);
const forcedStep = this.bandwidth > 0 ? StepType.StepAfter : stepType;
return invertValue(this.domain[0], invertedValue, this.minInterval, forcedStep);
invertWithStep(value: number, data: number[]): any {
const invertedValue = this.invert(value - this.bandwidth / 2);
const leftIndex = bisectLeft(data, invertedValue);
if (leftIndex === 0) {
// is equal or less than the first value
const prevValue1 = data[leftIndex];
if (data.length === 0) {
return prevValue1;
}
const nextValue1 = data[leftIndex + 1];
const nextDiff1 = Math.abs(nextValue1 - invertedValue);
const prevDiff1 = Math.abs(invertedValue - prevValue1);
if (nextDiff1 < prevDiff1) {
return nextValue1;
}
return prevValue1;
}
if (leftIndex === data.length) {
return data[leftIndex - 1];
}
const nextValue = data[leftIndex];
const prevValue = data[leftIndex - 1];
const nextDiff = Math.abs(nextValue - invertedValue);
const prevDiff = Math.abs(invertedValue - prevValue);
if (nextDiff <= prevDiff) {
return nextValue;
}
return prevValue;
}
}

Expand All @@ -187,58 +207,3 @@ export function isContinuousScale(scale: Scale): scale is ScaleContinuous {
export function isLogarithmicScale(scale: Scale) {
return scale.type === ScaleType.Log;
}

function invertValue(
domainMin: number,
invertedValue: number,
minInterval: number,
stepType?: StepType,
) {
if (minInterval > 0) {
switch (stepType) {
case StepType.StepAfter:
return linearStepAfter(invertedValue, minInterval);
case StepType.StepBefore:
return linearStepBefore(invertedValue, minInterval);
case StepType.Step:
default:
return linearStep(domainMin, invertedValue, minInterval);
}
}
return invertedValue;
}

/**
* Return an inverted value that is valid from the exact point of the scale
* till the end of the interval. |--------|********|
* @param invertedValue the inverted value
* @param minInterval the data minimum interval grether than 0
*/
export function linearStepAfter(invertedValue: number, minInterval: number): number {
return Math.floor(invertedValue / minInterval) * minInterval;
}

/**
* Return an inverted value that is valid from the half point before and half point
* after the value. |----****|*****----|
* till the end of the interval.
* @param domainMin the domain's minimum value
* @param invertedValue the inverted value
* @param minInterval the data minimum interval grether than 0
*/
export function linearStep(domainMin: number, invertedValue: number, minInterval: number): number {
const diff = (invertedValue - domainMin) / minInterval;
const base = diff - Math.floor(diff) > 0.5 ? 1 : 0;
return domainMin + Math.floor(diff) * minInterval + minInterval * base;
}

/**
* Return an inverted value that is valid from the half point before and half point
* after the value. |********|--------|
* till the end of the interval.
* @param invertedValue the inverted value
* @param minInterval the data minimum interval grether than 0
*/
export function linearStepBefore(invertedValue: number, minInterval: number): number {
return Math.ceil(invertedValue / minInterval) * minInterval;
}
Loading

0 comments on commit 921468f

Please sign in to comment.