@@ -93,6 +93,8 @@ export default function ChartContainer({
9393 files,
9494 metrics = [ ] ,
9595 compareMode,
96+ multiFileMode = 'baseline' ,
97+ baselineFile,
9698 relativeBaseline = 0.002 ,
9799 absoluteBaseline = 0.005 ,
98100 xRange = { min : undefined , max : undefined } ,
@@ -531,40 +533,71 @@ export default function ChartContainer({
531533 elements : { point : { radius : 0 } }
532534 } ) , [ xRange , onXRangeChange ] ) ;
533535
534- const createComparisonChartData = ( item1 , item2 , title ) => {
535- const comparisonData = getComparisonData ( item1 . data , item2 . data , compareMode ) ;
536- const baseline =
536+ const buildComparisonChartData = ( dataArray ) => {
537+ const baselineVal =
537538 compareMode === 'relative' || compareMode === 'relative-normal'
538539 ? relativeBaseline
539540 : compareMode === 'absolute'
540541 ? absoluteBaseline
541542 : 0 ;
542- const datasets = [
543- {
544- label : t ( 'chart.diffLabel' , { title } ) ,
545- data : comparisonData ,
546- borderColor : '#dc2626' ,
547- backgroundColor : '#dc2626' ,
543+ const datasets = [ ] ;
544+ const stats = [ ] ;
545+ const addPair = ( base , target , colorIdx ) => {
546+ const diffData = getComparisonData ( base . data , target . data , compareMode ) ;
547+ const color = colors [ colorIdx % colors . length ] ;
548+ datasets . push ( {
549+ label : `${ target . name } vs ${ base . name } ` ,
550+ data : diffData ,
551+ borderColor : color ,
552+ backgroundColor : color ,
548553 borderWidth : 2 ,
549554 fill : false ,
550555 tension : 0 ,
551556 pointRadius : 0 ,
552557 pointHoverRadius : 4 ,
553- pointBackgroundColor : '#dc2626' ,
554- pointBorderColor : '#dc2626' ,
558+ pointBackgroundColor : color ,
559+ pointBorderColor : color ,
555560 pointBorderWidth : 1 ,
556- pointHoverBackgroundColor : '#dc2626' ,
557- pointHoverBorderColor : '#dc2626' ,
561+ pointHoverBackgroundColor : color ,
562+ pointHoverBorderColor : color ,
558563 pointHoverBorderWidth : 1 ,
559564 animation : false ,
560565 animations : { colors : false , x : false , y : false } ,
561- } ,
562- ] ;
563- if ( baseline > 0 && ( compareMode === 'relative' || compareMode === 'relative-normal' || compareMode === 'absolute' ) ) {
564- const baselineData = comparisonData . map ( p => ( { x : p . x , y : baseline } ) ) ;
566+ } ) ;
567+ const normalDiff = getComparisonData ( base . data , target . data , 'normal' ) ;
568+ const absDiff = getComparisonData ( base . data , target . data , 'absolute' ) ;
569+ const relNormalDiff = getComparisonData ( base . data , target . data , 'relative-normal' ) ;
570+ const relDiff = getComparisonData ( base . data , target . data , 'relative' ) ;
571+ const mean = arr => ( arr . reduce ( ( s , p ) => s + p . y , 0 ) / arr . length ) || 0 ;
572+ stats . push ( {
573+ label : `${ target . name } vs ${ base . name } ` ,
574+ meanNormal : mean ( normalDiff ) ,
575+ meanAbsolute : mean ( absDiff ) ,
576+ relativeError : mean ( relNormalDiff ) ,
577+ meanRelative : mean ( relDiff )
578+ } ) ;
579+ } ;
580+
581+ let colorIdx = 0 ;
582+ if ( multiFileMode === 'baseline' ) {
583+ const base = dataArray . find ( d => d . name === baselineFile ) || dataArray [ 0 ] ;
584+ dataArray . forEach ( item => {
585+ if ( item . name === base . name ) return ;
586+ addPair ( base , item , colorIdx ++ ) ;
587+ } ) ;
588+ } else {
589+ for ( let i = 0 ; i < dataArray . length ; i ++ ) {
590+ for ( let j = i + 1 ; j < dataArray . length ; j ++ ) {
591+ addPair ( dataArray [ i ] , dataArray [ j ] , colorIdx ++ ) ;
592+ }
593+ }
594+ }
595+
596+ if ( datasets . length > 0 && baselineVal > 0 && ( compareMode === 'relative' || compareMode === 'relative-normal' || compareMode === 'absolute' ) ) {
597+ const baseData = datasets [ 0 ] . data . map ( p => ( { x : p . x , y : baselineVal } ) ) ;
565598 datasets . push ( {
566599 label : 'Baseline' ,
567- data : baselineData ,
600+ data : baseData ,
568601 borderColor : '#10b981' ,
569602 backgroundColor : '#10b981' ,
570603 borderWidth : 2 ,
@@ -583,7 +616,8 @@ export default function ChartContainer({
583616 animations : { colors : false , x : false , y : false } ,
584617 } ) ;
585618 }
586- return { datasets } ;
619+
620+ return { datasets, stats } ;
587621 } ;
588622
589623 if ( parsedData . length === 0 ) {
@@ -626,7 +660,7 @@ export default function ChartContainer({
626660 const metricElements = metrics . map ( ( metric , idx ) => {
627661 const key = metric . name || metric . keyword || `metric${ idx + 1 } ` ;
628662 const dataArray = metricDataArrays [ key ] || [ ] ;
629- const showComparison = dataArray . length == = 2 ;
663+ const showComparison = dataArray . length > = 2 ;
630664
631665 const yRange = calculateYRange ( dataArray ) ;
632666 const options = {
@@ -638,28 +672,11 @@ export default function ChartContainer({
638672 } ;
639673
640674 let stats = null ;
641- if ( showComparison ) {
642- const normalDiff = getComparisonData ( dataArray [ 0 ] . data , dataArray [ 1 ] . data , 'normal' ) ;
643- const absDiff = getComparisonData ( dataArray [ 0 ] . data , dataArray [ 1 ] . data , 'absolute' ) ;
644- const relNormalDiff = getComparisonData (
645- dataArray [ 0 ] . data ,
646- dataArray [ 1 ] . data ,
647- 'relative-normal'
648- ) ;
649- const relDiff = getComparisonData ( dataArray [ 0 ] . data , dataArray [ 1 ] . data , 'relative' ) ;
650- const mean = arr => ( arr . reduce ( ( s , p ) => s + p . y , 0 ) / arr . length ) || 0 ;
651- stats = {
652- meanNormal : mean ( normalDiff ) ,
653- meanAbsolute : mean ( absDiff ) ,
654- relativeError : mean ( relNormalDiff ) ,
655- meanRelative : mean ( relDiff )
656- } ;
657- }
658-
659675 let comparisonChart = null ;
660676 if ( showComparison ) {
661- const compData = createComparisonChartData ( dataArray [ 0 ] , dataArray [ 1 ] , key ) ;
662- const compRange = calculateYRange ( compData . datasets ) ;
677+ const compResult = buildComparisonChartData ( dataArray ) ;
678+ stats = compResult . stats . length > 0 ? compResult . stats : null ;
679+ const compRange = calculateYRange ( compResult . datasets ) ;
663680 const compOptions = {
664681 ...chartOptions ,
665682 scales : {
@@ -705,7 +722,7 @@ export default function ChartContainer({
705722 onRegisterChart = { registerChart }
706723 onSyncHover = { syncHoverToAllCharts }
707724 syncRef = { syncLockRef }
708- data = { compData }
725+ data = { { datasets : compResult . datasets } }
709726 options = { compOptions }
710727 />
711728 </ ResizablePanel >
@@ -762,11 +779,16 @@ export default function ChartContainer({
762779 { stats && (
763780 < div className = "card" >
764781 < h4 className = "text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" > { key } { t ( 'chart.diffStats' ) } </ h4 >
765- < div className = "space-y-1 text-xs" >
766- < p > { t ( 'comparison.meanNormal' , { value : stats . meanNormal . toFixed ( 6 ) } ) } </ p >
767- < p > { t ( 'comparison.meanAbsolute' , { value : stats . meanAbsolute . toFixed ( 6 ) } ) } </ p >
768- < p > { t ( 'comparison.relativeError' , { value : stats . relativeError . toFixed ( 6 ) } ) } </ p >
769- < p > { t ( 'comparison.meanRelative' , { value : stats . meanRelative . toFixed ( 6 ) } ) } </ p >
782+ < div className = "space-y-2 text-xs" >
783+ { stats . map ( s => (
784+ < div key = { s . label } className = "space-y-1" >
785+ < p className = "font-medium" > { s . label } </ p >
786+ < p > { t ( 'comparison.meanNormal' , { value : s . meanNormal . toFixed ( 6 ) } ) } </ p >
787+ < p > { t ( 'comparison.meanAbsolute' , { value : s . meanAbsolute . toFixed ( 6 ) } ) } </ p >
788+ < p > { t ( 'comparison.relativeError' , { value : s . relativeError . toFixed ( 6 ) } ) } </ p >
789+ < p > { t ( 'comparison.meanRelative' , { value : s . meanRelative . toFixed ( 6 ) } ) } </ p >
790+ </ div >
791+ ) ) }
770792 </ div >
771793 </ div >
772794 ) }
0 commit comments