@@ -78,8 +78,8 @@ import Pagination from './Pagination';
7878import { SET_CHANNELS } from '../store/state/channels' ;
7979import { UPDATE_VIEWED_CHUNKS } from '../store/logic/fetchChunks' ;
8080import { ChannelInfosContext , ChannelMetasContext } from '../../eeglab/EEGLabSeriesProvider' ;
81- import { getMinMaxRange } from '../../utils' ;
82- import { number } from 'node_modules/@types/prop-types ' ;
81+ import { computePercentileRange , computeMean } from '../../utils' ;
82+ import MutableKeyDepCache from '../../MutableDepCache ' ;
8383
8484/**
8585 * The state of a channel type.
@@ -89,6 +89,47 @@ export type ChannelTypeState = {
8989 channelsCount : number ,
9090} ;
9191
92+ /**
93+ * The specific cache type used to cache channel ranges.
94+ */
95+ type ChannelRangeCache = MutableKeyDepCache <
96+ // The channel index.
97+ number ,
98+ // The dependencies upon which the range depends.
99+ ChannelRangeCacheDeps ,
100+ // The computed range.
101+ [ number , number ]
102+ >
103+
104+ /**
105+ * The dependencies upon which a channel range depends.
106+ */
107+ type ChannelRangeCacheDeps = {
108+ interval : [ number , number ] ,
109+ chunkIds : number [ ] ,
110+ }
111+
112+ /**
113+ * Check whethere two channel range cache dependencies are equal.
114+ */
115+ function compareChannelRangeDeps ( a : ChannelRangeCacheDeps , b : ChannelRangeCacheDeps ) : boolean {
116+ if ( a . interval [ 0 ] !== b . interval [ 0 ] || a . interval [ 1 ] !== b . interval [ 1 ] ) {
117+ return false ;
118+ }
119+
120+ if ( a . chunkIds . length !== b . chunkIds . length ) {
121+ return false ;
122+ }
123+
124+ for ( let i = 0 ; i < a . chunkIds . length ; i += 1 ) {
125+ if ( a . chunkIds [ i ] !== b . chunkIds [ i ] ) {
126+ return false ;
127+ }
128+ }
129+
130+ return true ;
131+ }
132+
92133type CProps = {
93134 ref : MutableRefObject < any > ,
94135 viewerWidth : number ,
@@ -580,6 +621,11 @@ const SeriesRenderer: FunctionComponent<CProps> = ({
580621 vec2 . add ( center , topLeft , bottomRight ) ;
581622 vec2 . scale ( center , center , 1 / 2 ) ;
582623
624+ // A mutable cache for the visible range of the values each channel.
625+ const channelRangeCache : MutableRefObject < ChannelRangeCache > = useRef (
626+ new MutableKeyDepCache ( compareChannelRangeDeps )
627+ ) ;
628+
583629 const scales : [
584630 ScaleLinear < number , number , never > ,
585631 ScaleLinear < number , number , never >
@@ -729,6 +775,31 @@ const SeriesRenderer: FunctionComponent<CProps> = ({
729775 )
730776 : filteredChannels ;
731777
778+ // Get the maximum range for each channel type based on the ranges of
779+ // the visible values of each visible channel of that type.
780+ const channelTypeRanges : Record < string , number > = { } ;
781+ channelList . forEach ( ( channel ) => {
782+ const bidsChannel = findBidsChannel ( channelMetadata [ channel . index ] , bidsChannels ) ;
783+ const type = bidsChannel ?. ChannelType ?? 'Unknown' ;
784+
785+ // Get the visible values range of that channel, from the cache if possible.
786+ const [ min , max ] = channelRangeCache . current . get (
787+ channel . index ,
788+ {
789+ interval,
790+ chunkIds : channel . traces . flatMap ( ( trace ) => trace . chunks . map ( ( chunk ) => chunk . index ) ) . sort ( ) ,
791+ } ,
792+ ( ) => { console . log ( "RECOMPUTE" ) ; return getChannelVisibleRange ( channel , interval ) }
793+ ) ;
794+
795+ const range = max - min ;
796+ if ( ! channelTypeRanges [ type ] ) {
797+ channelTypeRanges [ type ] = range ;
798+ } else {
799+ channelTypeRanges [ type ] = Math . max ( channelTypeRanges [ type ] , range ) ;
800+ }
801+ } ) ;
802+
732803 return (
733804 < >
734805 < clipPath
@@ -786,14 +857,18 @@ const SeriesRenderer: FunctionComponent<CProps> = ({
786857 ( chunk ) => chunk . values . length > 0
787858 ) . length ;
788859
789- const valuesInView = getChannelVisibleValues ( trace , interval ) ;
860+ const valuesInView = getTraceVisibleValues ( trace , interval ) ;
790861
791862 if ( valuesInView . length === 0 ) {
792863 return ;
793864 }
794865
795- // Get the range of all the displayed values for this channel.
796- const seriesRange = getMinMaxRange ( valuesInView ) ;
866+ // Get the range centered on the average for channels of the same type
867+ const average = computeMean ( valuesInView ) ;
868+ const bidsChannel = findBidsChannel ( channelMetadata [ channel . index ] , bidsChannels ) ;
869+ const type = bidsChannel ?. ChannelType ?? 'Unknown' ;
870+ const overallRange = channelTypeRanges [ type ] || 0 ;
871+ const seriesRange : [ number , number ] = [ average - overallRange / 2 , average + overallRange / 2 ] ;
797872
798873 const scales : [
799874 ScaleLinear < number , number , never > ,
@@ -1607,9 +1682,31 @@ SeriesRenderer.defaultProps = {
16071682} ;
16081683
16091684/**
1610- * Get the values of a channel that fall within the visible interval.
1685+ * Get the range of the visible values of a channel across all its traces.
1686+ */
1687+ function getChannelVisibleRange ( channel : Channel , interval : [ number , number ] ) : [ number , number ] {
1688+ let channelMin = Infinity ;
1689+ let channelMax = - Infinity ;
1690+
1691+ channel . traces . forEach ( ( trace ) => {
1692+ const values = getTraceVisibleValues ( trace , interval ) ;
1693+ if ( values . length === 0 ) {
1694+ return ;
1695+ }
1696+
1697+ const [ traceMin , traceMax ] = computePercentileRange ( values ) ;
1698+ console . log ( traceMin , traceMax ) ;
1699+ channelMin = Math . min ( channelMin , traceMin ) ;
1700+ channelMax = Math . max ( channelMax , traceMax ) ;
1701+ } ) ;
1702+
1703+ return [ channelMin , channelMax ] ;
1704+ }
1705+
1706+ /**
1707+ * Get the values of a trace that fall within the visible interval.
16111708 */
1612- function getChannelVisibleValues ( trace : Trace , interval : [ number , number ] ) : number [ ] {
1709+ function getTraceVisibleValues ( trace : Trace , interval : [ number , number ] ) : number [ ] {
16131710 const [ start , end ] = interval ;
16141711 const values = [ ] ;
16151712
0 commit comments