Commit 2b42f5da authored by David Anderson's avatar David Anderson
Browse files

getDataAtEvent now groups the charts and datapoints into one callback

parent f0a534da
......@@ -73,9 +73,13 @@ export class AreaChartDemoComponent {
'click',
{
chart: activeChart,
onEvent: ( dpl: DataPoint[] ) => {
let first = dpl[ 0 ];
if ( !first ) {
onEvent: ( reports ) => {
let dpl: DataPoint[] = [];
reports.forEach( r => {
dpl = dpl.concat( r.dpl );
});
if (!dpl.length ) {
console.info( 'no data ' );
return;
}
......
......@@ -36,12 +36,15 @@ export class BarChartDemoComponent {
'click',
{
chart: barChart,
onEvent: ( dpl: DataPoint[] ) => {
let first = dpl[ 0 ];
if ( !first ) {
return;
}
console.info( dpl );
onEvent: ( reports ) => {
let dpl: DataPoint[] = [];
reports.forEach( r => dpl.push( ...r.dpl ) );
if (!dpl.length ) {
console.info( 'no data ' );
return;
}
console.info( dpl );
}
},
) );
......
......@@ -65,12 +65,10 @@ export class BoxplotDemoComponent {
'click',
{
chart: boxPlot,
onEvent: ( dpl: DataPoint[], chart: BoxPlot ) => {
let first = dpl[ 0 ];
if ( !first ) {
return;
}
console.info( `You are hovering over ${ dpl.length } points in Chart ${ chart.id }.` );
onEvent: ( reports ) => {
reports
.filter( r => r.dpl.length > 0 )
.forEach( r => console.info( `You are hovering over ${ r.dpl.length } points in Chart ${ r.chart.id }.` ) );
}
} as any
) );
......
......@@ -56,12 +56,15 @@ export class HeatmapChartDemoComponent{
'click',
{
chart: barChart,
onEvent: ( dpl: DataPoint[] ) => {
let first = dpl[ 0 ];
if ( !first ) {
return;
}
console.info( first.getExtent(barChart) );
onEvent: ( reports ) => {
let dpl: DataPoint[] = [];
reports.forEach( r => dpl.push( ...r.dpl ) );
if (!dpl.length ) {
console.info( 'no data ' );
return;
}
console.info( dpl );
}
},
) );
......
......@@ -89,9 +89,15 @@ export class LineChartDemoComponent {
'mousemove',
{
chart: activeChart,
onEvent: ( dpl: DataPoint[], chart: LineChart ) => {
let first = dpl[ 0 ];
console.log( first );
onEvent: ( reports ) => {
let dpl: DataPoint[] = [];
reports.forEach( r => dpl.push( ...r.dpl ) );
if (!dpl.length ) {
console.info( 'no data ' );
return;
}
console.info( dpl );
}
} as any
) );
......
......@@ -72,13 +72,15 @@ export class RadialChartDemoComponent {
'click',
{
chart: activeChart,
onEvent: ( dpl: DataPoint[] ) => {
let first = dpl[ 0 ];
if ( !first ) {
// console.info( 'no data ' );
return;
}
console.info( dpl.map( dp => dp) );
onEvent: ( reports ) => {
let dpl: DataPoint[] = [];
reports.forEach( r => dpl.push( ...r.dpl ) );
if (!dpl.length ) {
console.info( 'no data ' );
return;
}
console.info( dpl );
}
},
) );
......
import {
ScatterStyle
, PanBehavior
, GetDataAtMouseEvent
, DataPoint
, ScatterChart
, ContinuousAxis
, ChartCanvas
, ZoomBehavior,
, GetDataAtMouseEvent
, DataPoint
, ScatterChart
, ContinuousAxis
, ChartCanvas
, ZoomBehavior,
} from 'gist-charts';
import { Component, ViewChild, ElementRef } from '@angular/core';
/* cSpell: words divs scdp */
@Component({
selector: 'app-scatterchart-demo',
templateUrl: './scatterchart-demo.component.html',
styleUrls: ['./scatterchart-demo.component.css']
})
export class ScatterChartDemoComponent {
@ViewChild( 'drawspace' )
drawspace: ElementRef;
chart: ChartCanvas;
// rightChart: ScatterChart;
ngAfterViewInit() {
let me = this;
const pointCount = 20;
let charts: ScatterChart[] = [];
let draws: number = 1;
for ( let i = 0; i < draws; i++ ) {
let chart = new ScatterChart();
chart.getStyle = ( dp: DataPoint<ScatterData> ) => {
let style = dp.data as ScatterData;
return new ScatterStyle( style.stroke, style.fill, style.radius );
};
charts.push( chart );
}
let selection = new ScatterChart();
selection.getStyle = ( dp: DataPoint<ScatterData> ) => {
let style = dp.data as ScatterData;
return new ScatterStyle( style.stroke, style.fill, style.radius );
};
selection.background = 'rgba(255,255,255,0.5)';
selection.isActive = false;
let leftAxis: ContinuousAxis = new ContinuousAxis( 'left' );
let bottomAxis: ContinuousAxis = new ContinuousAxis( 'bottom' );
let rightAxis: ContinuousAxis = new ContinuousAxis( 'right' );
let topAxis: ContinuousAxis = new ContinuousAxis('top');
let axis = [
// topAxis,
bottomAxis,
// rightAxis,
leftAxis
];
me.chart = new ChartCanvas( me.drawspace.nativeElement );
// me.chart.background = 'rgb(255,255,255)';
axis.forEach(a => {
a.registerChart(charts);
a.registerChart( selection );
a.explicitBounds = [0, pointCount ]
// a.fill = 'white';
});
topAxis.getValue = bottomAxis.getValue = ( dp ) => { return dp.data.x; };
rightAxis.getValue = leftAxis.getValue = ( dp ) => { return dp.data.y; };
let selectedData: DataPoint[] = [];
function selectionUpdate() {
if ( selectedData.length ) {
selection.datalist( selectedData );
selection.isActive = true;
} else {
selection.isActive = false;
}
console.log( selectedData );
// me.chart.beginRender();
}
leftAxis.label = 'left';
bottomAxis.label = 'bottom';
rightAxis.label = 'right';
topAxis.label = 'top';
leftAxis.shouldExtendTicksAcrossChart = true;
bottomAxis.shouldExtendTicksAcrossChart = true;
me.chart.addChart(charts);
me.chart.addChart(selection);
me.chart.addAxis( axis);
let getData = new GetDataAtMouseEvent(
'click',
{
chart: charts,
onEvent: ( dpl: DataPoint[] ) => {
let first = dpl[ 0 ];
if ( !first ) {
selectedData = [];
selectionUpdate();
return;
}
// first.data.selected = true;
selectedData.push(first);
selectionUpdate();
}
},
);
let zoom = new ZoomBehavior();
// zoom.limitZoom( axis, 0.5, 1.5 );
let pan = new PanBehavior();
// pan.limitPan(bottomAxis, pointCount * -0.25, pointCount * 1.25 );
me.chart.addBehavior( [getData, zoom, pan] );
charts[0].alpha = 1;
let dataList = randomData( pointCount );
charts[ 0 ].datalist( dataList );
me.chart.beginRender();
// setTimeout(redraw, 20000);
}
intervals: number[] = [];
ngOnDestroy() {
this.intervals.forEach( int => {
window.clearInterval( int );
} );
}
@Component( {
selector: 'app-scatterchart-demo',
templateUrl: './scatterchart-demo.component.html',
styleUrls: [ './scatterchart-demo.component.css' ]
} )
export class ScatterChartDemoComponent {
@ViewChild( 'drawspace' )
drawspace: ElementRef;
chart: ChartCanvas;
// rightChart: ScatterChart;
ngAfterViewInit() {
let me = this;
const pointCount = 20;
let charts: ScatterChart[] = [];
let draws: number = 1;
for ( let i = 0; i < draws; i++ ) {
let chart = new ScatterChart();
chart.getStyle = ( dp: DataPoint<ScatterData> ) => {
let style = dp.data as ScatterData;
return new ScatterStyle( style.stroke, style.fill, style.radius );
};
charts.push( chart );
}
let selection = new ScatterChart();
selection.getStyle = ( dp: DataPoint<ScatterData> ) => {
let style = dp.data as ScatterData;
return new ScatterStyle( style.stroke, style.fill, style.radius );
};
selection.background = 'rgba(255,255,255,0.5)';
selection.isActive = false;
let leftAxis: ContinuousAxis = new ContinuousAxis( 'left' );
let bottomAxis: ContinuousAxis = new ContinuousAxis( 'bottom' );
let rightAxis: ContinuousAxis = new ContinuousAxis( 'right' );
let topAxis: ContinuousAxis = new ContinuousAxis( 'top' );
let axis = [
// topAxis,
bottomAxis,
// rightAxis,
leftAxis
];
me.chart = new ChartCanvas( me.drawspace.nativeElement );
// me.chart.background = 'rgb(255,255,255)';
axis.forEach( a => {
a.registerChart( charts );
a.registerChart( selection );
a.explicitBounds = [ 0, pointCount ]
// a.fill = 'white';
} );
topAxis.getValue = bottomAxis.getValue = ( dp ) => { return dp.data.x; };
rightAxis.getValue = leftAxis.getValue = ( dp ) => { return dp.data.y; };
let selectedData: DataPoint[] = [];
function selectionUpdate() {
if ( selectedData.length ) {
selection.datalist( selectedData );
selection.isActive = true;
} else {
selection.isActive = false;
}
console.log( selectedData );
}
leftAxis.label = 'left';
bottomAxis.label = 'bottom';
rightAxis.label = 'right';
topAxis.label = 'top';
leftAxis.shouldExtendTicksAcrossChart = true;
bottomAxis.shouldExtendTicksAcrossChart = true;
me.chart.addChart( charts );
me.chart.addChart( selection );
me.chart.addAxis( axis );
let getData = new GetDataAtMouseEvent(
'click',
{
chart: charts,
onEvent: ( reports ) => {
let pointsAtEvent: DataPoint[] = [];
reports.forEach( r => pointsAtEvent.push( ...r.dpl ) );
if ( !pointsAtEvent.length ) {
selectedData = [];
}
selectedData.push( ...pointsAtEvent );
selectionUpdate();
}
},
);
let zoom = new ZoomBehavior();
zoom.limitZoom( axis, 0.5, 1.5 );
let pan = new PanBehavior('wheel'); // use wheel because we want to click on the data
pan.limitPan( bottomAxis, pointCount * -0.25, pointCount * 1.25 );
me.chart.addBehavior( [ getData, zoom, pan ] );
charts[ 0 ].alpha = 1;
let dataList = randomData( pointCount );
charts[ 0 ].datalist( dataList );
me.chart.beginRender();
// setTimeout(redraw, 20000);
}
intervals: number[] = [];
ngOnDestroy() {
this.intervals.forEach( int => {
window.clearInterval( int );
} );
}
}
function randomData( pointCount: number = 10000 ): Array<DataPoint> {
let scdp: Array<DataPoint> = [];
// scdp.length = pointCount;
let tmpArr = [ 'blue', 'green', 'grey', 'black', 'brown', 'yellow', 'orange', 'purple', 'pink', 'red', 'blue', 'green', 'grey', 'black', 'brown', 'yellow', 'orange', 'purple', 'pink', 'red' ];
for ( let i = 0; i < pointCount; i++ ) {
let fill = tmpArr[ randomInt( 0, 9 )];
let dp = new DataPoint( {
x: randomInt(0, pointCount),
y: randomInt( 0, pointCount ),
radius: 3,
fill: fill,
stroke: undefined
});
// let stroke = ( randomColors ) ? fill : tmpArr[ Math.floor(( i + xStart + div / 2 ) / div ) ];
scdp.push(dp );
}
// scdp.sort();
return scdp;
let scdp: Array<DataPoint> = [];
// scdp.length = pointCount;
let tmpArr = [ 'blue', 'green', 'grey', 'black', 'brown', 'yellow', 'orange', 'purple', 'pink', 'red', 'blue', 'green', 'grey', 'black', 'brown', 'yellow', 'orange', 'purple', 'pink', 'red' ];
for ( let i = 0; i < pointCount; i++ ) {
let fill = tmpArr[ randomInt( 0, 9 ) ];
let dp = new DataPoint( {
x: randomInt( 0, pointCount ),
y: randomInt( 0, pointCount ),
radius: 3,
fill: fill,
stroke: undefined
} );
// let stroke = ( randomColors ) ? fill : tmpArr[ Math.floor(( i + xStart + div / 2 ) / div ) ];
scdp.push( dp );
}
// scdp.sort();
return scdp;
}
function randomInt( min: number, max: number ): number {
let diff = max - min;
return Math.round( Math.random() * diff + min );
let diff = max - min;
return Math.round( Math.random() * diff + min );
}
class ScatterData {
x: number = 0;
y: number = 0;
radius: number = 0;
fill: string ='';
stroke: string ='';
x: number = 0;
y: number = 0;
radius: number = 0;
fill: string = '';
stroke: string = '';
}
import { EventHandler } from '../classes/eventHandler.class';
import { BaseChart } from '../';
import { EventHandler } from '../types/eventHandler.interface';
import { makeList } from '../utility/makeList.method';
import { GistBehavior } from './gistBehavior.class';
import { LimiterType } from "../types/limiterType.type";
import { DataPoint } from '../classes/datapoint.class';
export type supportedEventType = 'dblclick' | 'click' | 'mousedown' | 'mousemove' | 'mouseout' | 'mouseover' | 'mouseup';
......@@ -12,24 +14,37 @@ export type supportedEventType = 'dblclick' | 'click' | 'mousedown' | 'mousemove
* @class GetDataAtMouseEvent
*/
export class GetDataAtMouseEvent extends GistBehavior {
/**
*Creates an instance of GetDataAtMouseEvent.
* @param {(supportedEventType | supportedEventType[])} 'dblclick' | 'click' | 'mousedown' | 'mousemove' | 'mouseout' | 'mouseover' | 'mouseup'
* @param {(EventHandler | EventHandler[])} eventHandlers Define what to do when the event happens
* @param {number} [throttleTime=250] How often this event can happen
* @param {LimiterType} [limiter='throttle'] 'throttle' will ensure the event will wait throttleTime, before firing again. 'debounce' will wait throttletime without a repeat event to fire.
* @memberof GetDataAtMouseEvent
*/
constructor(
eventNames: supportedEventType | supportedEventType[],
eventHandlers: EventHandler | EventHandler[],
waitTime: number = 250,
throttleTime: number = 250,
limiter: LimiterType = 'throttle' ) {
super( eventNames, eventHandlers, waitTime, limiter );
super( eventNames, eventHandlers, throttleTime, limiter );
}
_activate(): void {
const me = this;
me.eventHandlers.forEach( ( eh ) => {
makeList( eh.chart ).forEach( chart => {
if ( chart.isActive ) {
eh.onEvent( chart._implementation.getDataAtCoord( me.currentX, me.currentY ), chart, me.currentEvent );
}
} );
let reportList: Array<{ dpl: DataPoint[], chart: BaseChart }> ;
me.eventHandlers.forEach( ( eh ) => {
reportList = [];
makeList( eh.chart )
.filter( chart => chart !== undefined && chart.isActive )
.forEach( chart => {
reportList.push( {
dpl: chart._implementation.getDataAtCoord( me.currentX, me.currentY ),
chart: chart
} );
} );
eh.onEvent( reportList, me.currentEvent );
} );
}
}
import { EventHandler } from '../classes/eventHandler.class';
import { EventHandler } from '../types/eventHandler.interface';
import { ChartCanvas } from '../classes/chartCanvas.class';
import { makeList } from '../utility/makeList.method';
import { uID } from '../utility/uID.method';
......@@ -63,7 +63,7 @@ export abstract class GistBehavior {
* @type {number}
* @memberof GistBehavior
*/
public readonly waitTime: number;
public readonly throttleTime: number;
/**
......@@ -137,11 +137,11 @@ export abstract class GistBehavior {
constructor(
eventNames: string | string[],
eventHandlers: EventHandler | EventHandler[],
waitTime: number = 250,
throttleTime: number = 250,
limiter: LimiterType ) {
this.eventNames = makeList( eventNames );
this.eventHandlers = makeList( eventHandlers );
this.waitTime = waitTime;
this.throttleTime = throttleTime;
this.limiter = limiter;
}
......@@ -155,7 +155,7 @@ export abstract class GistBehavior {
protected _activate(): void {
const me = this;
me.eventHandlers.forEach(( eh ) => {
eh.onEvent( [], undefined, me.currentEvent );
eh.onEvent( [], me.currentEvent );
} );
}
......@@ -220,7 +220,7 @@ export abstract class GistBehavior {
me.reset();
}
}, me.waitTime );
}, me.throttleTime );
}
}
......@@ -232,7 +232,7 @@ export abstract class GistBehavior {
me.currentEvent = event;
me.timer = window.setTimeout(() => {
me.callActivate();
}, me.waitTime );
}, me.throttleTime );
}
private callActivate(): void {
......
import { BaseChart } from '../configs/charts/baseChart.class';
import { EventHandler } from '../types/eventHandler.interface';
import { GistBehavior } from './gistBehavior.class';
import { DataPoint } from '../classes/datapoint.class';