@@ -5,15 +5,11 @@ import { ResponseStatus } from 'tsp-typescript-client/lib/models/response/respon
55import { QueryHelper } from 'tsp-typescript-client/lib/models/query/query-helper' ;
66import { Entry } from 'tsp-typescript-client/lib/models/entry' ;
77import 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' ;
118import { 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' ;
1310import * as React from 'react' ;
1411import { flushSync } from 'react-dom' ;
1512import { TimeRange } from 'traceviewer-base/lib/utils/time-range' ;
16- import { BIMath } from 'timeline-chart/lib/bigint-utils' ;
1713import {
1814 XYChartFactoryParams ,
1915 xyChartFactory ,
@@ -24,7 +20,17 @@ import { ChartOptions } from 'chart.js';
2420import { Line , Scatter } from 'react-chartjs-2' ;
2521import { debounce } from 'lodash' ;
2622import { 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
2935export const ZOOM_IN_RATE = 0.8 ;
3036export 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