Skip to content

Commit 6b35edf

Browse files
Siwei Zhangbhufmann
authored andcommitted
extract util methods from abstract-xy-output-component
Extract util methods from abstract-xy-output-component so that later they can be shared with generic xy view. Signed-off-by: Siwei Zhang <[email protected]>
1 parent 7a51d62 commit 6b35edf

File tree

2 files changed

+274
-154
lines changed

2 files changed

+274
-154
lines changed

react-components/src/components/abstract-xy-output-component.tsx

Lines changed: 47 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,11 @@ import { ResponseStatus } from 'tsp-typescript-client/lib/models/response/respon
55
import { QueryHelper } from 'tsp-typescript-client/lib/models/query/query-helper';
66
import { Entry } from 'tsp-typescript-client/lib/models/entry';
77
import ColumnHeader from './utils/filter-tree/column-header';
8-
import { scaleLinear } from 'd3-scale';
9-
import { axisLeft } from 'd3-axis';
10-
import { select } from 'd3-selection';
118
import { EntryTree } from './utils/filter-tree/entry-tree';
12-
import { XyEntry, XYSeries } from 'tsp-typescript-client/lib/models/xy';
9+
import { XYSeries } from 'tsp-typescript-client/lib/models/xy';
1310
import * as React from 'react';
1411
import { flushSync } from 'react-dom';
1512
import { TimeRange } from 'traceviewer-base/lib/utils/time-range';
16-
import { BIMath } from 'timeline-chart/lib/bigint-utils';
1713
import {
1814
XYChartFactoryParams,
1915
xyChartFactory,
@@ -24,7 +20,17 @@ import { ChartOptions } from 'chart.js';
2420
import { Line, Scatter } from 'react-chartjs-2';
2521
import { debounce } from 'lodash';
2622
import { isEqual } from 'lodash';
27-
import { getCollapsedNodesFromAutoExpandLevel, listToTree } from './utils/filter-tree/utils';
23+
import {
24+
applyYAxis,
25+
buildTreeStateFromModel,
26+
ColorAllocator,
27+
computeYRange,
28+
getTimeForX as timeForX,
29+
getXForTime as xForTime,
30+
zoomRange,
31+
panRange,
32+
setSpinnerVisible
33+
} from './utils/xy-shared';
2834

2935
export const ZOOM_IN_RATE = 0.8;
3036
export const ZOOM_OUT_RATE = 1.25;
@@ -149,6 +155,8 @@ export abstract class AbstractXYOutputComponent<
149155

150156
private _debouncedUpdateXY = debounce(() => this.updateXY(), 500);
151157

158+
private colors = new ColorAllocator();
159+
152160
constructor(props: P) {
153161
super(props);
154162

@@ -294,28 +302,15 @@ export abstract class AbstractXYOutputComponent<
294302
const treeResponse = tspClientResponse.getModel();
295303
if (tspClientResponse.isOk() && treeResponse) {
296304
if (treeResponse.model) {
297-
const headers = treeResponse.model.headers;
298-
const columns = [];
299-
if (headers && headers.length > 0) {
300-
headers.forEach(header => {
301-
columns.push({ title: header.name, sortable: true, resizable: true, tooltip: header.tooltip });
302-
});
303-
} else {
304-
columns.push({ title: 'Name', sortable: true });
305-
}
306-
const checkedSeries = this.getAllCheckedIds(treeResponse.model.entries);
307-
const autoCollapsedNodes = getCollapsedNodesFromAutoExpandLevel(
308-
listToTree(treeResponse.model.entries, columns),
309-
treeResponse.model.autoExpandLevel
310-
);
305+
const built = buildTreeStateFromModel(treeResponse.model as any);
311306
this.setState(
312307
{
313308
outputStatus: treeResponse.status,
314-
xyTree: treeResponse.model.entries,
315-
defaultOrderedIds: treeResponse.model.entries.map(entry => entry.id),
316-
collapsedNodes: autoCollapsedNodes,
317-
checkedSeries,
318-
columns
309+
xyTree: built.xyTree,
310+
defaultOrderedIds: built.defaultOrderedIds,
311+
collapsedNodes: built.collapsedNodes,
312+
checkedSeries: built.checkedSeries,
313+
columns: built.columns as any
319314
},
320315
() => {
321316
this.updateXY();
@@ -336,16 +331,6 @@ export abstract class AbstractXYOutputComponent<
336331
return ResponseStatus.FAILED;
337332
}
338333

339-
getAllCheckedIds(entries: Array<XyEntry>): Array<number> {
340-
const checkedSeries: number[] = [];
341-
for (const entry of entries) {
342-
if (entry.isDefault) {
343-
checkedSeries.push(entry.id);
344-
}
345-
}
346-
return checkedSeries;
347-
}
348-
349334
renderTree(): React.ReactNode | undefined {
350335
this.onToggleCheck = this.onToggleCheck.bind(this);
351336
this.onToggleCollapse = this.onToggleCollapse.bind(this);
@@ -371,35 +356,17 @@ export abstract class AbstractXYOutputComponent<
371356
renderYAxis(): React.ReactNode {
372357
// Y axis with D3
373358
const chartHeight = parseInt(this.props.style.height.toString());
374-
375-
const yScale = scaleLinear()
376-
.domain([this.state.allMin, Math.max(this.state.allMax, 1)])
377-
.range([chartHeight - this.margin.bottom, this.margin.top]);
359+
applyYAxis(
360+
this.yAxisRef,
361+
chartHeight,
362+
this.margin.top,
363+
this.margin.bottom,
364+
this.state.allMin,
365+
this.state.allMax
366+
);
378367

379368
const yTransform = `translate(${this.margin.left}, 0)`;
380369

381-
// Abbreviate large numbers
382-
const scaleYLabel = (d: number) =>
383-
d >= 1000000000000
384-
? Math.round(d / 100000000000) / 10 + 'G'
385-
: d >= 1000000000
386-
? Math.round(d / 100000000) / 10 + 'B'
387-
: d >= 1000000
388-
? Math.round(d / 100000) / 10 + 'M'
389-
: d >= 1000
390-
? Math.round(d / 100) / 10 + 'K'
391-
: Math.round(d * 10) / 10;
392-
393-
if (this.state.allMax > 0) {
394-
select(this.yAxisRef.current)
395-
.call(axisLeft(yScale).tickSizeOuter(0).ticks(4))
396-
.call(g => g.select('.domain').remove());
397-
select(this.yAxisRef.current)
398-
.selectAll('.tick text')
399-
.style('font-size', '11px')
400-
.text((d: any) => scaleYLabel(d));
401-
}
402-
403370
return (
404371
<React.Fragment>
405372
<svg height={chartHeight} width={this.margin.left}>
@@ -492,12 +459,7 @@ export abstract class AbstractXYOutputComponent<
492459
}
493460

494461
private viewSpinner(status: boolean): void {
495-
if (document.getElementById(this.getOutputComponentDomId() + 'handleSpinner')) {
496-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
497-
document.getElementById(this.getOutputComponentDomId() + 'handleSpinner')!.style.visibility = status
498-
? 'visible'
499-
: 'hidden';
500-
}
462+
setSpinnerVisible(this.getOutputComponentDomId(), status);
501463
}
502464

503465
private buildScatterData(seriesObj: XYSeries[]) {
@@ -506,7 +468,7 @@ export abstract class AbstractXYOutputComponent<
506468
const offset = this.props.viewRange.getOffset() ?? BigInt(0);
507469
seriesObj.forEach(series => {
508470
const color = this.getSeriesColor(series.seriesName);
509-
xValues = series.xValues;
471+
xValues = series.xValues as bigint[];
510472
const yValues: number[] = series.yValues;
511473
let pairs: xyPair[] = [];
512474

@@ -546,7 +508,7 @@ export abstract class AbstractXYOutputComponent<
546508
let xValues: bigint[] = [];
547509
seriesObj.forEach(series => {
548510
const color = this.getSeriesColor(series.seriesName);
549-
xValues = series.xValues;
511+
xValues = series.xValues as bigint[];
550512
dataSetArray.push({
551513
label: series.seriesName,
552514
fill: false,
@@ -570,117 +532,48 @@ export abstract class AbstractXYOutputComponent<
570532
}
571533

572534
private getSeriesColor(key: string): string {
573-
const colors = [
574-
'rgba(191, 33, 30, 1)',
575-
'rgba(30, 56, 136, 1)',
576-
'rgba(71, 168, 189, 1)',
577-
'rgba(245, 230, 99, 1)',
578-
'rgba(255, 173, 105, 1)',
579-
'rgba(216, 219, 226, 1)',
580-
'rgba(212, 81, 19, 1)',
581-
'rgba(187, 155, 176 , 1)',
582-
'rgba(6, 214, 160, 1)',
583-
'rgba(239, 71, 111, 1)'
584-
];
585-
let colorIndex = this.colorMap.get(key);
586-
if (colorIndex === undefined) {
587-
colorIndex = this.currentColorIndex % colors.length;
588-
this.colorMap.set(key, colorIndex);
589-
this.currentColorIndex++;
590-
}
591-
return colors[colorIndex];
535+
return this.colors.get(key);
592536
}
593537

594538
private calculateYRange() {
595-
let localMax = 0;
596-
let localMin = 0;
597-
598-
if (this.state && this.state.xyData) {
599-
this.state.xyData?.datasets?.forEach((dSet: any, i: number) => {
600-
let rowMax;
601-
let rowMin;
602-
if (this.isScatterPlot) {
603-
rowMax = Math.max(...dSet.data.map((d: any) => d.y));
604-
rowMin = Math.min(...dSet.data.map((d: any) => d.y));
605-
} else {
606-
rowMax = Math.max(...dSet.data);
607-
rowMin = Math.min(...dSet.data);
608-
}
609-
localMax = Math.max(localMax, rowMax);
610-
localMin = i === 0 ? rowMin : Math.min(localMin, rowMin);
611-
});
612-
}
613-
539+
const { min, max } = computeYRange(this.state.xyData?.datasets as any, this.isScatterPlot);
614540
this.setState({
615-
allMax: localMax * 1.01,
616-
allMin: localMin * 0.99
541+
allMax: max,
542+
allMin: min
617543
});
618544
}
619545

620546
protected getTimeForX(x: number): bigint {
621-
const range = this.getDisplayedRange();
622-
const offset = range.getOffset() ?? BigInt(0);
623-
const duration = range.getDuration();
624-
const chartWidth = this.getChartWidth() === 0 ? 1 : this.getChartWidth();
625-
const time = range.getStart() - offset + BIMath.round((x / chartWidth) * Number(duration));
626-
return time;
547+
return timeForX(this.getDisplayedRange(), this.getChartWidth() === 0 ? 1 : this.getChartWidth(), x);
627548
}
628549

629550
protected getXForTime(time: bigint): number {
630-
const range = this.getDisplayedRange();
631-
const start = range.getStart();
632-
const duration = range.getDuration();
633-
const chartWidth = this.getChartWidth() === 0 ? 1 : this.getChartWidth();
634-
const x = (Number(time - start) / Number(duration)) * chartWidth;
635-
return x;
551+
return xForTime(this.getDisplayedRange(), this.getChartWidth() === 0 ? 1 : this.getChartWidth(), time);
636552
}
637553

638554
protected zoom(isZoomIn: boolean): void {
639555
if (this.props.unitController.viewRangeLength >= 1) {
640556
const zoomCenterTime = this.getZoomTime();
641-
const startDistance = zoomCenterTime - this.props.unitController.viewRange.start;
642-
const zoomFactor = isZoomIn ? ZOOM_IN_RATE : ZOOM_OUT_RATE;
643-
const newDuration = BIMath.clamp(
644-
Number(this.props.unitController.viewRangeLength) * zoomFactor,
645-
BigInt(2),
646-
this.props.unitController.absoluteRange
647-
);
648-
const newStartRange = BIMath.max(0, zoomCenterTime - BIMath.round(Number(startDistance) * zoomFactor));
649-
const newEndRange = newStartRange + newDuration;
650-
this.updateRange(newStartRange, newEndRange);
557+
const view = this.props.unitController.viewRange;
558+
const abs = this.props.unitController.absoluteRange;
559+
const newRange = zoomRange(view, abs, zoomCenterTime, isZoomIn, ZOOM_IN_RATE, ZOOM_OUT_RATE);
560+
this.props.unitController.viewRange = newRange;
651561
}
652562
}
653563

654564
protected pan(panLeft: boolean): void {
655-
const panFactor = 0.1;
656-
const percentRange = BIMath.round(Number(this.props.unitController.viewRangeLength) * panFactor);
657-
const panNumber = panLeft ? BigInt(-1) : BigInt(1);
658-
const startRange = this.props.unitController.viewRange.start + panNumber * percentRange;
659-
const endRange = this.props.unitController.viewRange.end + panNumber * percentRange;
660-
if (startRange < 0) {
661-
this.props.unitController.viewRange = {
662-
start: BigInt(0),
663-
end: this.props.unitController.viewRangeLength
664-
};
665-
} else if (endRange > this.props.unitController.absoluteRange) {
666-
this.props.unitController.viewRange = {
667-
start: this.props.unitController.absoluteRange - this.props.unitController.viewRangeLength,
668-
end: this.props.unitController.absoluteRange
669-
};
670-
} else {
671-
this.props.unitController.viewRange = {
672-
start: startRange,
673-
end: endRange
674-
};
675-
}
565+
const view = this.props.unitController.viewRange;
566+
const abs = this.props.unitController.absoluteRange;
567+
const newRange = panRange(view, abs, panLeft);
568+
this.props.unitController.viewRange = newRange;
676569
}
677570

678571
protected tooltip(): void {
679572
const xPos = this.positionXMove;
680-
const timeForX = this.getTimeForX(xPos);
573+
const timeForXVal = this.getTimeForX(xPos);
681574
let timeLabel: string | undefined = timeForX.toString() + ' ns';
682575
if (this.props.unitController.numberTranslator) {
683-
timeLabel = this.props.unitController.numberTranslator(timeForX) + ' s';
576+
timeLabel = this.props.unitController.numberTranslator(timeForXVal) + ' s';
684577
}
685578
const chartWidth = this.isBarPlot ? this.getChartWidth() : this.chartRef.current.chartInstance.width;
686579
const chartHeight = this.isBarPlot

0 commit comments

Comments
 (0)