@@ -13,19 +13,21 @@ import { xKeyMap } from '@/utils/xKeyMap';
1313import { createAnnotation , ModalInfo } from '@/utils/annotationUtils' ;
1414import { RenderTooltip } from '@/constants/hoverUtils' ;
1515// import { digitize, weightedAvg } from '@/libs/math';
16- import { PlotTrace , PlotDatumWithBBox , PlotLayout , AveragePointCustomData } from '@/types/PlotTypes' ;
16+ import { PlotTrace , PlotDatumWithBBox , PlotLayout , AveragePointCustomData , RawPoint } from '@/types/PlotTypes' ;
1717import { ImageModal } from '@/components/ImageModal' ;
1818import RawDataPlotPanel from '@/components/RawDataPlotPanel' ;
1919import { Trash2 } from 'lucide-react' ;
2020import { flushSync } from 'react-dom' ;
2121import { isDarkRGB } from '@/libs/color' ;
22+ import { filterRawPointsByAverage , filterRawPointsByAverageCurve } from '@/utils/filterRawByAverage' ;
23+ import { filter } from 'd3' ;
2224const Plot = dynamic ( ( ) => import ( 'react-plotly.js' ) , { ssr : false } ) ;
2325
2426export default function LightCurvePlot ( ) {
2527 const {
2628 dataType, dataSelection, xAxis, errorBars, noOfBins, noOfDataPoint,
2729 plotType, pointSize, lineWidth, legendFontSize, labelFontSize, tooltipFontSize,
28- figure, rawFigure, avgPointRawMap, setSettings
30+ figure, rawFigure, avgPointRawMap, filterPercent , isFiltered , setSettings
2931 } = usePlotSettings ( ) ;
3032
3133 const [ loading , setLoading ] = useState ( false ) ;
@@ -35,16 +37,10 @@ export default function LightCurvePlot() {
3537 const [ modalInfo , setModalInfo ] = useState < ModalInfo | null > ( null ) ;
3638 const [ tooltipContent , setTooltipContent ] = useState < React . ReactNode | null > ( null ) ;
3739 const [ tooltipPosition , setTooltipPosition ] = useState < { left : number ; top : number } | null > ( null ) ;
38- const [ rawPlot , setRawPlot ] = useState < {
39- x : number ; y : number ; err ?: number ; customdata : unknown ;
40- phase ?: number ;
41- mjd ?: number ;
42- time ?: number ;
43- second ?: number ;
44- minute ?: number ;
45- hour ?: number ;
46- day ?: number ;
47- } [ ] | null > ( null ) ;
40+
41+ const [ rawPlot , setRawPlot ] = useState < RawPoint [ ] | null > ( null ) ;
42+ const [ filteredRawPlot , setFilteredRawPlot ] = useState < { tracesSW : PlotTrace [ ] ; tracesLW : PlotTrace [ ] } > ( { tracesSW : [ ] , tracesLW : [ ] } ) ;
43+ const [ filterBand , setFilterBand ] = useState < { minY : number ; maxY : number } | null > ( null ) ;
4844 // const [rawColor, setRawColor] = useState<string>('#1f77b4');
4945 const [ rawMarkerBorderColor , setRawMarkerBorderColor ] = useState < string > ( '#000000' ) ;
5046 const [ rawMarkerFillColor , setRawMarkerFillColor ] = useState < string > ( '#1f77b4' ) ;
@@ -130,36 +126,48 @@ export default function LightCurvePlot() {
130126 }
131127 const { traces : traceSW , patch : patchSW } = addTrace ( { wave : 'SW' , json : swJson , axis : xAxis , mode : dType , noOfBins, noOfDataPoint, timeKey, epoch, r_in : r1 , r_out : r2 , avgPointRawMap, markerFillColor, markerBorderColor, lineColor, plotType : plotType as 'lines' | 'markers' | 'lines+markers' , errorBars : errorBars as 'bar' | 'hide' | 'separate' | undefined , pointSize, lineWidth, dataMode } ) ;
132128 rawTracesSW . push ( ...traceSW ) ;
133- setSettings ( {
134- avgPointRawMap : Object . fromEntries (
135- Object . entries ( patchSW ) . map ( ( [ key , arr ] ) => [
136- key ,
137- arr . map ( p => ( {
138- ...p ,
139- x : typeof p . x === 'number' ? p . x : ( typeof p . x === 'string' ? Number ( p . x ) : ( p . x instanceof Date ? p . x . getTime ( ) : 0 ) )
140- } ) )
141- ] )
142- )
143- } ) ;
129+ setSettings ( prev => ( {
130+ avgPointRawMap : {
131+ ...prev . avgPointRawMap ,
132+ ...Object . fromEntries (
133+ Object . entries ( patchSW ) . map ( ( [ key , arr ] ) => [
134+ key ,
135+ arr . map ( p => ( {
136+ ...p ,
137+ x : typeof p . x === 'number'
138+ ? p . x
139+ : ( typeof p . x === 'string'
140+ ? Number ( p . x )
141+ : ( p . x instanceof Date
142+ ? p . x . getTime ( )
143+ : 0 ) )
144+ } ) )
145+ ] )
146+ )
147+ }
148+ } ) ) ;
144149 const { traces : traceLW , patch : patchLW } = addTrace ( { wave : 'LW' , json : lwJson , axis : xAxis , mode : dType , noOfBins, noOfDataPoint, timeKey, epoch, r_in : r1 , r_out : r2 , avgPointRawMap, markerFillColor, markerBorderColor, lineColor, plotType : plotType as 'lines' | 'markers' | 'lines+markers' , errorBars : errorBars as 'bar' | 'hide' | 'separate' | undefined , pointSize, lineWidth, dataMode } ) ;
145150 rawTracesLW . push ( ...traceLW ) ;
146- setSettings ( {
147- avgPointRawMap : Object . fromEntries (
148- Object . entries ( patchLW ) . map ( ( [ key , arr ] ) => [
149- key ,
150- arr . map ( p => ( {
151- ...p ,
152- x : typeof p . x === 'number'
153- ? p . x
154- : ( typeof p . x === 'string'
155- ? Number ( p . x )
156- : ( p . x instanceof Date
157- ? p . x . getTime ( )
158- : 0 ) )
159- } ) )
160- ] )
161- )
162- } ) ;
151+ setSettings ( prev => ( {
152+ avgPointRawMap : {
153+ ...prev . avgPointRawMap ,
154+ ...Object . fromEntries (
155+ Object . entries ( patchLW ) . map ( ( [ key , arr ] ) => [
156+ key ,
157+ arr . map ( p => ( {
158+ ...p ,
159+ x : typeof p . x === 'number'
160+ ? p . x
161+ : ( typeof p . x === 'string'
162+ ? Number ( p . x )
163+ : ( p . x instanceof Date
164+ ? p . x . getTime ( )
165+ : 0 ) )
166+ } ) )
167+ ] )
168+ )
169+ }
170+ } ) ) ;
163171
164172 }
165173 } catch ( err ) {
@@ -230,6 +238,7 @@ export default function LightCurvePlot() {
230238 ] ;
231239
232240 setSettings ( { rawFigure : { tracesSW : rawTracesSW , tracesLW : rawTracesLW } } ) ;
241+ setFilteredRawPlot ( { tracesSW : rawTracesSW , tracesLW : rawTracesLW } ) ;
233242 setSettings ( { figure : { data : fullData , layout : layout } } ) ;
234243 setFig ( { data : fullData } ) ;
235244 setSettings ( { avgPointRawMap } ) ;
@@ -240,7 +249,102 @@ export default function LightCurvePlot() {
240249 } , [ dataSelection , dataType , xAxis , errorBars , noOfBins , noOfDataPoint ] ) ;
241250
242251 useEffect ( ( ) => {
243- if ( ! rawFigure ) return ;
252+ if ( ! rawFigure || ! avgPointRawMap ) return ;
253+ if ( ! isFiltered ) { setFilteredRawPlot ( rawFigure ) }
254+ else {
255+ const tracesSW = rawFigure . tracesSW . map ( t => ( { ...t } ) ) ;
256+ const tracesLW = rawFigure . tracesLW . map ( t => ( { ...t } ) ) ;
257+
258+ tracesSW . forEach ( ( trace : PlotTrace ) => {
259+ const name = trace . name ?? "" ;
260+ if ( ! name . includes ( "_average" ) ) return ;
261+ const base_raw_name = name . split ( " " ) [ 0 ] . replace ( "_average" , "_raw" ) ;
262+ const rawTraceIndex = tracesSW . findIndex ( t =>
263+ ( t . name ?? "" ) . startsWith ( base_raw_name )
264+ ) ;
265+ const newRawTrace = {
266+ ...tracesSW [ rawTraceIndex ] ,
267+ x : [ ] ,
268+ y : [ ] ,
269+ err : [ ] ,
270+ customdata : [ ]
271+ } ;
272+
273+ trace . y ?. forEach ( ( avgY , index ) => {
274+ const cd = trace . customdata ?. [ index ] as AveragePointCustomData | undefined ;
275+ if ( ! cd ) return ;
276+
277+ const { type, epoch, r_in, r_out, phase } = cd ;
278+
279+ const key = `${ type } _${ epoch } _${ r_in } _${ r_out } _${ Number ( phase ) . toFixed ( 5 ) } ` ;
280+ const rawList = avgPointRawMap [ key ] ;
281+ if ( ! rawList ) return ;
282+
283+ const { kept } = filterRawPointsByAverage ( rawList , avgY , filterPercent ) ;
284+
285+ newRawTrace . x . push ( ...kept . map ( p => p . x ) ) ;
286+ newRawTrace . y . push ( ...kept . map ( p => p . y ) ) ;
287+ newRawTrace . err . push ( ...kept . map ( p => p . err ) ) ;
288+ newRawTrace . customdata . push ( ...kept . map ( p => p . customdata ) ) ;
289+ } ) ;
290+ newRawTrace . name = `${ base_raw_name } (${ newRawTrace . y . length } /${ rawFigure . tracesSW [ rawTraceIndex ] . y . length } )` ;
291+ if ( xAxis === 'phase' ) {
292+ newRawTrace . x = newRawTrace . x . concat ( ( newRawTrace . x as number [ ] ) . map ( v => v + 1 ) ) ;
293+ newRawTrace . y = newRawTrace . y . concat ( newRawTrace . y ) ;
294+ newRawTrace . err = newRawTrace . err . concat ( newRawTrace . err ) ;
295+ newRawTrace . customdata = newRawTrace . customdata . concat ( newRawTrace . customdata ) ;
296+ }
297+ tracesSW [ rawTraceIndex ] = newRawTrace ;
298+ } ) ;
299+ tracesLW . forEach ( ( trace : PlotTrace ) => {
300+ const name = trace . name ?? "" ;
301+ if ( ! name . includes ( "_average" ) ) return ;
302+ const base_raw_name = name . split ( " " ) [ 0 ] . replace ( "_average" , "_raw" ) ;
303+ const rawTraceIndex = tracesLW . findIndex ( t =>
304+ ( t . name ?? "" ) . startsWith ( base_raw_name )
305+ ) ;
306+ const newRawTrace = {
307+ ...tracesLW [ rawTraceIndex ] ,
308+ x : [ ] ,
309+ y : [ ] ,
310+ err : [ ] ,
311+ customdata : [ ]
312+ } ;
313+
314+ trace . y ?. forEach ( ( avgY , index ) => {
315+ const cd = trace . customdata ?. [ index ] as AveragePointCustomData | undefined ;
316+ if ( ! cd ) return ;
317+
318+ const { type, epoch, r_in, r_out, phase } = cd ;
319+
320+ const key = `${ type } _${ epoch } _${ r_in } _${ r_out } _${ Number ( phase ) . toFixed ( 5 ) } ` ;
321+ const rawList = avgPointRawMap [ key ] ;
322+ if ( ! rawList ) return ;
323+
324+ const { kept } = filterRawPointsByAverage ( rawList , avgY , filterPercent ) ;
325+
326+ newRawTrace . x . push ( ...kept . map ( p => p . x ) ) ;
327+ newRawTrace . y . push ( ...kept . map ( p => p . y ) ) ;
328+ newRawTrace . err . push ( ...kept . map ( p => p . err ) ) ;
329+ newRawTrace . customdata . push ( ...kept . map ( p => p . customdata ) ) ;
330+ } ) ;
331+ newRawTrace . name = `${ base_raw_name } (${ newRawTrace . y . length } /${ rawFigure . tracesLW [ rawTraceIndex ] . y . length } )` ;
332+ if ( xAxis === 'phase' ) {
333+ newRawTrace . x = newRawTrace . x . concat ( ( newRawTrace . x as number [ ] ) . map ( v => v + 1 ) ) ;
334+ newRawTrace . y = newRawTrace . y . concat ( newRawTrace . y ) ;
335+ newRawTrace . err = newRawTrace . err . concat ( newRawTrace . err ) ;
336+ newRawTrace . customdata = newRawTrace . customdata . concat ( newRawTrace . customdata ) ;
337+ }
338+ tracesLW [ rawTraceIndex ] = newRawTrace ;
339+ } ) ;
340+
341+ setFilteredRawPlot ( { tracesSW, tracesLW } ) ;
342+ }
343+ } , [ avgPointRawMap , dataSelection , rawFigure , filterPercent , isFiltered , xAxis ] ) ;
344+
345+
346+ useEffect ( ( ) => {
347+ if ( ! rawFigure || ! filteredRawPlot ) return ;
244348 const applyStyle = ( tr : PlotTrace , axisX : string , axisY : string ) => ( {
245349 ...tr ,
246350 mode : plotType as 'lines' | 'markers' | 'lines+markers' ,
@@ -249,19 +353,18 @@ export default function LightCurvePlot() {
249353 xaxis : axisX ,
250354 yaxis : axisY ,
251355 } ) ;
252- console . log ( "Updating figure with new styles" ) ;
253- console . log ( figure ?. layout ) ;
356+
254357 setSettings ( {
255358 figure : {
256359 data : [
257- ...rawFigure . tracesSW . map ( tr => applyStyle ( tr , 'x2' , 'y' ) ) ,
258- ...rawFigure . tracesLW . map ( tr => applyStyle ( tr , 'x2' , 'y2' ) ) ,
360+ ...filteredRawPlot . tracesSW . map ( tr => applyStyle ( tr , 'x2' , 'y' ) ) ,
361+ ...filteredRawPlot . tracesLW . map ( tr => applyStyle ( tr , 'x2' , 'y2' ) ) ,
259362 ] ,
260363 layout : figure ?. layout || { }
261364 }
262365 } ) ;
263366
264- } , [ plotType , pointSize , lineWidth , rawFigure ] ) ;
367+ } , [ plotType , pointSize , lineWidth , rawFigure , filteredRawPlot , isFiltered ] ) ;
265368
266369 useEffect ( ( ) => {
267370 clearAll ( ) ;
0 commit comments