import { Serie, Options } from '../types'; import { CoreSeries } from './series'; import { CoreOptions } from './options'; import * as d3 from 'd3'; import cloneDeep from 'lodash/cloneDeep'; import min from 'lodash/min'; import max from 'lodash/max'; import sortBy from 'lodash/sortBy'; import reverse from 'lodash/reverse'; const DEFAULT_AXIS_RANGE = [0, 1]; const DEFAULT_TRANSFORM = { x: 0, y: 0, k: 1 } // 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: PodState.transform has conflicts with d3.zoom.event.transform. It should be synchronized. export class PodState { private _xValueRange: [number, number]; private _yValueRange: [number, number]; private _y1ValueRange: [number, number]; private _transform: { x: number, y: number, k: number | string } = cloneDeep(DEFAULT_TRANSFORM); private _xScale: d3.ScaleLinear; private _yScale: d3.ScaleLinear; private _y1Scale: d3.ScaleLinear; constructor( protected boxParams: { height: number, width: number }, protected coreSeries: CoreSeries, protected coreOptions: CoreOptions, ) { this.setInitialRanges(); this.initScales(); } protected setInitialRanges(): void { this._xValueRange = [this.getMinValueX(), this.getMaxValueX()]; this._yValueRange = [this.getMinValueY(), this.getMaxValueY()]; this._y1ValueRange = [this.getMinValueY1(), this.getMaxValueY1()]; } protected initScales(): void { this.setXScale(); this.setYScale(); this.setY1Scale(); } protected setYScale(): void { let domain = this._yValueRange; domain = sortBy(domain) as [number, number]; if(this.coreOptions.axis.y.invert === true) { domain = reverse(domain); } this._yScale = d3.scaleLinear() .domain(domain) .range([this.boxParams.height, 0]); // inversed, because d3 y-axis goes from top to bottom; } protected setXScale(): void { let domain = this._xValueRange; if(this.coreOptions.axis.x.invert === true) { domain = reverse(domain); } this._xScale = d3.scaleLinear() .domain(domain) .range([0, this.boxParams.width]); } protected setY1Scale(): void { let domain = this._y1ValueRange; domain = sortBy(domain) as [number, number]; if(this.coreOptions.axis.y1.invert === true) { domain = reverse(domain); } this._y1Scale = d3.scaleLinear() .domain(domain) .range([this.boxParams.height, 0]); // inversed, because d3 y-axis goes from top to bottom } public clearState(): void { this.setInitialRanges(); this.initScales(); this._transform = { x: 0, y: 0, k: 1 }; } get yScale(): d3.ScaleLinear { return this._yScale; } get xScale(): d3.ScaleLinear { return this._xScale; } get y1Scale(): d3.ScaleLinear { return this._y1Scale; } get xValueRange(): [number, number] | undefined { return this._xValueRange; } get yValueRange(): [number, number] | undefined { return this._yValueRange; } get y1ValueRange(): [number, number] | undefined { return this._y1ValueRange; } get transform(): { x?: number, y?: number, k?: number | string } { return this._transform; } 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 | string }) { this._transform.x = transform.x !== undefined ? transform.x : this._transform.x; this._transform.y = transform.y !== undefined ? transform.y : this._transform.y; this._transform.k = transform.k !== undefined ? transform.k : this._transform.k; } public getMinValueY(): number { if(!this.coreSeries.isSeriesAvailable) { return DEFAULT_AXIS_RANGE[0]; } if(this.coreOptions.axis.y.range !== undefined) { return min(this.coreOptions.axis.y.range); } return this.coreSeries.minValueY; } public getMaxValueY(): number { if(!this.coreSeries.isSeriesAvailable) { return DEFAULT_AXIS_RANGE[1]; } if(this.coreOptions.axis.y.range !== undefined) { return max(this.coreOptions.axis.y.range); } return this.coreSeries.maxValueY; } public getMinValueX(): number { if(!this.coreSeries.isSeriesAvailable) { return DEFAULT_AXIS_RANGE[0]; } if(this.coreOptions.axis.x.range !== undefined) { return min(this.coreOptions.axis.x.range); } return this.coreSeries.minValueX; } public getMaxValueX(): number { if(!this.coreSeries.isSeriesAvailable) { return DEFAULT_AXIS_RANGE[1]; } if(this.coreOptions.axis.x.range !== undefined) { return max(this.coreOptions.axis.x.range); } return this.coreSeries.maxValueX; } public getMinValueY1(): number { if(!this.coreSeries.isSeriesAvailable) { return DEFAULT_AXIS_RANGE[0]; } if(this.coreOptions.axis.y1.range !== undefined) { return min(this.coreOptions.axis.y1.range); } return this.coreSeries.minValueY; } public getMaxValueY1(): number { if(!this.coreSeries.isSeriesAvailable) { return DEFAULT_AXIS_RANGE[1]; } if(this.coreOptions.axis.y1.range !== undefined) { return max(this.coreOptions.axis.y1.range); } return this.coreSeries.maxValueY; } // getters for correct transform get absXScale(): d3.ScaleLinear { const domain = [0, Math.abs(this.getMaxValueX() - this.getMinValueX())]; return d3.scaleLinear() .domain(domain) .range([0, this.boxParams.width]); } get absYScale(): d3.ScaleLinear { const domain = [0, Math.abs(this.getMaxValueY() - this.getMinValueY())]; return d3.scaleLinear() .domain(domain) .range([0, this.boxParams.height]); } }