|
|
|
@ -1,10 +1,15 @@
|
|
|
|
|
import { TimeSerie, Options, yAxisOrientation } from './types'; |
|
|
|
|
|
|
|
|
|
// we import only d3 types here
|
|
|
|
|
import * as d3 from 'd3'; |
|
|
|
|
|
|
|
|
|
import cloneDeep from 'lodash/cloneDeep'; |
|
|
|
|
import min from 'lodash/min'; |
|
|
|
|
import minBy from 'lodash/minBy'; |
|
|
|
|
import max from 'lodash/max'; |
|
|
|
|
import maxBy from 'lodash/maxBy'; |
|
|
|
|
import sortBy from 'lodash/sortBy'; |
|
|
|
|
import reverse from 'lodash/reverse'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const DEFAULT_AXIS_RANGE = [0, 1]; |
|
|
|
@ -14,29 +19,80 @@ const DEFAULT_TRANSFORM = {
|
|
|
|
|
k: 1 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: replace all getters with fields. Because getters will be recalculated on each call
|
|
|
|
|
// TODO: replace all getters with fields. Because getters will be recalculated on each call. Use scales as example.
|
|
|
|
|
// TODO: remove duplicates in max/min values.
|
|
|
|
|
// TODO: add scales
|
|
|
|
|
// TODO: PodState can be divided in two classes, but it is hard now.
|
|
|
|
|
export class PodState<T extends TimeSerie, O extends Options> { |
|
|
|
|
private _xValueRange: [number, number]; |
|
|
|
|
private _yValueRange: [number, number]; |
|
|
|
|
private _y1ValueRange: [number, number] | undefined = undefined; // can be undefined, because y1 - is an optional param
|
|
|
|
|
private _y1ValueRange: [number, number]; |
|
|
|
|
private _transform: { x: number, y: number, k: number } = cloneDeep(DEFAULT_TRANSFORM); |
|
|
|
|
private _xScale: d3.ScaleLinear<number, number>; |
|
|
|
|
private _yScale: d3.ScaleLinear<number, number>; |
|
|
|
|
private _y1Scale: d3.ScaleLinear<number, number>; |
|
|
|
|
|
|
|
|
|
constructor( |
|
|
|
|
protected _d3: typeof d3, |
|
|
|
|
protected boxParams: { height: number, width: number }, |
|
|
|
|
protected series: T[], |
|
|
|
|
protected options: O, |
|
|
|
|
) { |
|
|
|
|
this.initRanges(); |
|
|
|
|
this.initScales(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected initRanges(): void { |
|
|
|
|
this._xValueRange = [this.minValueX, this.maxValueX]; |
|
|
|
|
this._yValueRange = [this.minValueY, this.maxValueY]; |
|
|
|
|
if(this.options.axis.y1.isActive) { |
|
|
|
|
this._y1ValueRange = [this.minValueY1, this.maxValueY1]; |
|
|
|
|
this._y1ValueRange = [this.minValueY1, this.maxValueY1]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected initScales(): void { |
|
|
|
|
this.setXScale(); |
|
|
|
|
this.setYScale(); |
|
|
|
|
this.setY1Scale(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected setYScale(): void { |
|
|
|
|
let domain = this._yValueRange; |
|
|
|
|
domain = sortBy(domain) as [number, number]; |
|
|
|
|
if(this.options.axis.y.invert === true) { |
|
|
|
|
domain = reverse(domain); |
|
|
|
|
} |
|
|
|
|
this._yScale = this._d3.scaleLinear() |
|
|
|
|
.domain(domain) |
|
|
|
|
.range([this.boxParams.height, 0]); // inversed, because d3 y-axis goes from top to bottom;
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected setXScale(): void { |
|
|
|
|
const domain = this._xValueRange; |
|
|
|
|
this._xScale = this._d3.scaleLinear() |
|
|
|
|
.domain(domain) |
|
|
|
|
.range([0, this.boxParams.width]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected setY1Scale(): void { |
|
|
|
|
let domain = this._y1ValueRange; |
|
|
|
|
domain = sortBy(domain) as [number, number]; |
|
|
|
|
if(this.options.axis.y1.invert === true) { |
|
|
|
|
domain = reverse(domain); |
|
|
|
|
} |
|
|
|
|
this._y1Scale = this._d3.scaleLinear() |
|
|
|
|
.domain(domain) |
|
|
|
|
.range([this.boxParams.height, 0]); // inversed, because d3 y-axis goes from top to bottom
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get yScale(): d3.ScaleLinear<number, number> { |
|
|
|
|
return this._yScale; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get xScale(): d3.ScaleLinear<number, number> { |
|
|
|
|
return this._xScale; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get y1Scale(): d3.ScaleLinear<number, number> { |
|
|
|
|
return this._y1Scale; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get xValueRange(): [number, number] | undefined { |
|
|
|
@ -57,14 +113,17 @@ export class PodState<T extends TimeSerie, O extends Options> {
|
|
|
|
|
|
|
|
|
|
set xValueRange(range: [number, number]) { |
|
|
|
|
this._xValueRange = range; |
|
|
|
|
this.setXScale(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
set yValueRange(range: [number, number]) { |
|
|
|
|
this._yValueRange = range; |
|
|
|
|
this.setYScale(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
set y1ValueRange(range: [number, number]) { |
|
|
|
|
this._y1ValueRange = range; |
|
|
|
|
this.setY1Scale(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
set transform(transform: { x?: number, y?: number, k?: number }) { |
|
|
|
|