You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
85 lines
3.0 KiB
85 lines
3.0 KiB
import { ScatterData, PointType } from '../types'; |
|
import { ScatterSeries } from './scatter_series'; |
|
|
|
import { Delaunay } from 'd3-delaunay'; |
|
|
|
import * as _ from 'lodash'; |
|
import * as d3 from 'd3'; // only types |
|
|
|
// return type row: [ 0:x, 1:y, 2:(custom value | null), 3:pointIdx, 4:serie.target ] |
|
type Value = number; |
|
type PointIdx = number; |
|
type Target = string; |
|
export type DelaunayDataRow = [Value, Value, Value | null, PointIdx, Target]; |
|
|
|
export class DelaunayDiagram { |
|
private _delaunayData: DelaunayDataRow[]; |
|
private _delaunayDiagram: any; |
|
|
|
constructor( |
|
protected series: ScatterSeries, |
|
xScale: d3.ScaleLinear<number, number>, yScale: (string) => d3.ScaleLinear<number, number>, // TODO: bad, but idk how to do it better |
|
) { |
|
this._delaunayData = this.getDatapointsForDelaunay(); |
|
this.setDelaunayDiagram(xScale, yScale); |
|
} |
|
|
|
public get data(): DelaunayDataRow[] | undefined { |
|
if(!this._delaunayData || this._delaunayData.length === 0) { |
|
return undefined; |
|
} |
|
return this._delaunayData; |
|
} |
|
|
|
public setDelaunayDiagram(xScale: d3.ScaleLinear<number, number>, yScale: (string) => d3.ScaleLinear<number, number>) { |
|
if(!this._delaunayData) { |
|
console.warn('No data for delaunay initialization'); |
|
return; |
|
} |
|
console.time('delaunay-init'); |
|
this._delaunayDiagram = Delaunay.from( |
|
this._delaunayData, |
|
(d: DelaunayDataRow) => xScale(d[0]), |
|
(d: DelaunayDataRow) => yScale(this.series.getSerieByTarget(d[4])?.yOrientation)(d[1]), |
|
); |
|
console.timeEnd('delaunay-init'); |
|
} |
|
|
|
public findPointIndex(eventX: number, eventY: number): number | undefined { |
|
if(!this._delaunayDiagram) { |
|
return undefined; |
|
} |
|
let pointIndex = this._delaunayDiagram.find(eventX, eventY); |
|
if(pointIndex === -1) { |
|
return undefined; |
|
} |
|
// TODO: add search radius via https://github.com/d3/d3-delaunay/issues/45 |
|
return pointIndex; |
|
} |
|
|
|
public getDataRowByIndex(index: number): DelaunayDataRow | undefined { |
|
if(!this.data) { |
|
return undefined; |
|
} |
|
return this.data[index]; |
|
} |
|
|
|
private getDatapointsForDelaunay(): DelaunayDataRow[] | undefined { |
|
// here we union all datapoints with point render type(circle or rectangle) |
|
// it means that circles and rectangles will be highlighted(not lines) |
|
// TODO: set Defaults (if pointType === undefined, Circle type will be used futher) |
|
const seriesForPointType = this.series.visibleSeries.filter((serie: ScatterData) => serie.pointType !== PointType.NONE); |
|
if(seriesForPointType.length === 0) { |
|
return undefined; // to avoid ts error |
|
} |
|
return this.concatSeriesDatapoints(seriesForPointType); |
|
} |
|
|
|
private concatSeriesDatapoints(series: ScatterData[]): DelaunayDataRow[] { |
|
const datapointsList = _.map(series, serie => { |
|
const datapointsWithOptions = _.map(serie.datapoints, (row, rowIdx) => [row[0], row[1], row[2] || null, rowIdx, serie.target]); |
|
return datapointsWithOptions; |
|
}); |
|
return _.union(...datapointsList); |
|
} |
|
}
|
|
|